Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

SolidCraft

SolidCraft is an open-source parametric 3D CAD application written in pure Rust. Sketch on a plane, constrain and dimension the sketch, turn profiles into solids with features on a timeline, and change a parameter to watch the whole timeline rebuild.

It runs as:

  • a desktop app for Windows and Linux (egui + wgpu);
  • a web app in the browser (wasm, WebGPU or WebGL2), try it here;
  • a headless CLI, solidcraft-cli, that runs command scripts, measures and exports;
  • an MCP server that AI agents model with, headless or driving the running app.

Everything the user can do is a command with JSON parameters. The toolbar, the dialogs, scripts, the CLI, the control channel and MCP all run the same commands, so anything you can click you can also script, test or hand to an agent.

SolidCraft is early. Sketching, solid modelling, parameters, components with joints, sheet metal and a plastic-enclosure subset work; surfaces, drawings, CAM and simulation are deferred. The roadmap has the details, and the feature list lists every command by area, and the reference tests check modelling results against analytic and FreeCAD values.

New designs are saved as .sc. Existing .solidcraft designs still open, and Save keeps their name; solidcraft-cli commands keep their names and behavior.

This book has two halves: the user guide for people modelling with SolidCraft, and the developer guide for people working on it. The command reference is generated from the code on every build.

SolidCraft is licensed MIT OR Apache-2.0. It is independent: behaviour is designed from public documentation, general CAD conventions and user feedback, and reference tests use analytic values and open-source tools. SolidCraft is not affiliated with Autodesk, Dassault Systèmes, PTC or any other CAD vendor; product names such as Fusion, SolidWorks and Onshape are trademarks of their respective owners, named only to describe compatibility.

Getting started

The desktop executable is solidcraft, the CLI is solidcraft-cli, and designs are saved as .sc (.solidcraft designs from earlier releases still open, and Save keeps their name). Designs, settings and setups from builds released under the previous product name keep working (see the compatibility notes).

Desktop

Download the installer for your system from the Download section of the README or the releases page, or build it yourself with rustup, which installs the Rust version the repository pins (rust-toolchain.toml, 1.99.1) the first time you run cargo in it:

git clone https://github.com/tangency-labs/solidcraft && cd solidcraft
cargo run --release -p solidcraft -- --sample   # opens with a sample part

solidcraft part.step opens a STEP, IGES, 3MF, STL or OBJ file as a new design, and solidcraft design.sc opens a saved design. Windows builds cross-compile from Linux with cargo xwin build --release --target x86_64-pc-windows-msvc -p solidcraft.

The start page

SolidCraft opens on the start page; the house at the top left brings it back.

The start page, numbered 1 to 6 The start page, numbered 1 to 6

1 A new design in millimetres (or inches, beside it) · 2 Open a saved design · 3 Import a STEP, IGES or mesh file as a new design · 4 Designs you opened recently · 5 Samples to explore · 6 The File menu: save, export, preferences, shortcuts

A sample opens as an ordinary design you can change; a click on its tile is enough.

From there, A tour of the window shows what’s where, and Sketching step by step starts a part of your own.

macOS

Open the release .dmg and drag SolidCraft.app onto Applications.

The release .dmg is signed with a Developer ID and notarized by Apple, and the app and the disk image carry the notarization ticket, so macOS opens SolidCraft normally, even offline.

A copy you build yourself, or one from a fork that has no signing keys, is signed ad hoc and not notarized, so macOS blocks its first launch with “SolidCraft can’t be opened, because Apple can’t verify if the app is vulnerable”. Open System Settings › Privacy & Security, scroll down to the message about SolidCraft, click Open Anyway and confirm: the app opens normally from then on. From a terminal, remove the quarantine flag instead:

xattr -dr com.apple.quarantine /Applications/SolidCraft.app

When updating from a build released under the previous product name, follow the macOS upgrade notes so you replace it rather than install a second copy.

Linux AppImage

The first time you open the AppImage it adds a menu entry and icons under ~/.local/share (applications/io.tangencylabs.solidcraft.desktop, icons/hicolor), so the desktop shows the right name and icon. It adds nothing if appimaged already made an entry for the file, or if a launcher such as Gear Lever or AppImageLauncher started it (and removes the entry SolidCraft added before). To turn it off for good, create an empty file ~/.local/share/solidcraft/no-desktop-integration, or start the AppImage with SOLIDCRAFT_NO_DESKTOP_INTEGRATION=1.

If the AppImage doesn’t start and prints a FUSE message followed by execv error: No such file or directory, AppImageLauncher 2.x is intercepting it: it can’t start AppImages built on the static runtime, as SolidCraft’s is. Update AppImageLauncher to 3.0 (so far only published as betas; 3.0.0-beta-3 or newer works), or remove it.

Linux Flatpak

Install Flatpak using your distribution’s package manager. For an installation that receives updates, add Flathub for the required Freedesktop runtime and the SolidCraft application feed to the same user installation:

flatpak remote-add --user --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
flatpak remote-add --user --if-not-exists solidcraft https://docs.solidcraft.io/flatpak/solidcraft.flatpakrepo
flatpak install --user solidcraft io.tangencylabs.solidcraft
flatpak run io.tangencylabs.solidcraft

A SolidCraft remote added before the project moved to docs.solidcraft.io (from bherbruck.github.io) no longer finds updates. Point it at the new address, then update: flatpak remote-modify --user --url=https://docs.solidcraft.io/flatpak/repo/ solidcraft and flatpak update --user.

Earlier releases used the application IDs ai.storyteller.solidcraft and, up to 0.7, ai.storyteller.solvecraft. If you installed one of those from the SolidCraft remote, flatpak update --user reports it as end-of-life and offers to replace it with io.tangencylabs.solidcraft; accept, and your settings and autosaves move with it. If you installed a .flatpak bundle, install the new bundle, launch it once (it copies your settings), then run flatpak uninstall --user ai.storyteller.solidcraft (or ai.storyteller.solvecraft; see the compatibility notes).

Alternatively, download the release’s .flatpak file for your architecture and run flatpak install --user ./solidcraft-VERSION-linux-ARCH.flatpak. The bundle supplies the Flathub runtime repository address; installation still needs internet access to fetch any missing runtime. Launch with flatpak run io.tangencylabs.solidcraft, rather than looking for a host solidcraft binary. Files chosen through Open/Save use the desktop file-chooser portal; command-line paths outside Documents may need a specific additional filesystem permission.

Linux Snap

On Ubuntu and other distributions with snapd:

sudo snap install solidcraft
solidcraft                # or open SolidCraft from the app menu
solidcraft.cli --version  # the CLI, for scripts and `solidcraft.cli mcp`

The snap can open designs in your home folder (not hidden folders) and, through the file chooser, anywhere else. For USB drives and other mounted disks, run sudo snap connect solidcraft:removable-media once. A release’s .snap file installs without the store with sudo snap install --dangerous ./solidcraft-VERSION-linux-ARCH.snap; it then doesn’t update automatically.

Web

The same app runs in the browser: open SolidCraft on the web (?sample opens the sample part, ?webgl forces WebGL2 where WebGPU is missing or misbehaves; the browser remembers it until ?webgpu, and an app installed from Chrome or Edge starts with it too). Designs are saved in the browser’s private file storage, which needs HTTPS or localhost.

The web app at /app/ is always the latest release. Two more builds are there to try early: the release candidate (the newest -rc pre-release; a page saying so appears until the first one is out) and the edge build (the current main, rebuilt on every change). Both can break, and a design saved in them may not open in the release. Each keeps its designs, settings and recovery copies apart from the release’s, so trying one never changes what the release sees, and nothing is copied between them: to move a design, save it to a file in one and open the file in the other. Search engines are told to skip both.

After the first visit the web app works offline: the browser keeps a copy of it. Online, each launch gets the latest published build (the browser downloads it once when it changes); offline, when the network doesn’t answer within a few seconds, or when a new build can’t be downloaded whole, the kept copy starts (and the download carries on for the next launch).

Browsers that can install web apps add Install app… to the File menu: it installs SolidCraft as an app with its own window and launcher icon, working offline like the page. In Chrome, Edge and other Chromium browsers it shows the browser’s install prompt, and once SolidCraft is installed the item is gone. Safari has no such prompt, so the item shows its steps instead: Share › Add to Home Screen on iPhone and iPad, File › Add to Dock on a Mac (Safari 17 or later).

To build it yourself:

rustup target add wasm32-unknown-unknown
cd apps/solidcraft-web && trunk serve --release --no-autoreload

Offline use needs --no-autoreload: Trunk’s live reload changes the page as it serves it, so it no longer matches the build the browser would keep. Debug builds (trunk serve) never keep a copy.

Use ?open=<encoded HTTPS URL> to open a hosted .sc design as an unsaved copy, including its history, constraints, and joints. Encode the URL with encodeURIComponent when it contains query parameters. The host must allow CORS requests from the app’s origin. Downloads, including redirected URLs, must use HTTPS or stay on the app’s own origin, and fit within the File > Open design size limit. Failed downloads and invalid designs show an error and leave the empty design.

For self-hosting, ?open=/dutchman/jobs/abc/part.sc resolves against the page’s origin, using its scheme and port, including plain HTTP on a LAN. A full URL on that same origin is also accepted; other absolute URLs still require HTTPS. For example, expose a reverse-proxy service in Compose, routing / to solidcraft:8080 and /dutchman/ to a converter service. The converter can link to /?open=/dutchman/jobs/abc/part.sc without a separate origin or CORS setup. Paths must start with one /; protocol-relative paths (//), backslashes, control characters, and decoded .. segments are refused. Redirects must end at HTTPS or the page’s own origin. Embed mode and Open in SolidCraft preserve the path.

Add embed to show the viewport, timeline, browser tree, and joint controls without the ribbon, file menus, or start page:

<iframe src="https://docs.solidcraft.io/app/?embed&open=https://example.org/arm.sc"
        width="960" height="540" title="Arm design"></iframe>

Embedded designs do not save designs, preferences, or recovery copies to browser storage and do not ask before unloading. Open in SolidCraft opens the same link in the full app in a new tab. ?embed without open keeps the normal app layout.

Use embed=editor instead for a page that keeps the designs itself and has people model in the frame. It needs no open:

<iframe src="https://docs.solidcraft.io/app/?embed=editor"
        width="1280" height="800" title="Modelling"></iframe>

The ribbon, browser tree, timeline and properties stay; the application bar and start page do not. Ctrl+N, Ctrl+O, Ctrl+S, Ctrl+W and Ctrl+, do nothing there, because those belong to the page; Ctrl+Z and Ctrl+Y still undo and redo. As with embed, nothing is kept in browser storage: the page moves the design in and out with the document.export and document.import control methods.

Self-hosting with Docker

The web app is also a container image, ghcr.io/tangency-labs/solidcraft-web, for Linux on x86_64 and ARM64: latest is the latest release, 0.14.0 or 0.14 a release, edge the newest main. It serves the app with nginx on port 8080 and keeps nothing on the server: designs stay in each visitor’s browser.

docker run -d --name solidcraft -p 8080:8080 --restart unless-stopped ghcr.io/tangency-labs/solidcraft-web:latest

Or with Docker Compose, using packaging/docker/compose.yaml (SOLIDCRAFT_PORT picks another port):

docker compose -f packaging/docker/compose.yaml up -d      # start
docker compose -f packaging/docker/compose.yaml pull && docker compose -f packaging/docker/compose.yaml up -d   # update
docker compose -f packaging/docker/compose.yaml up -d --build   # build the image from your checkout instead

Then open http://localhost:8080/. On another machine the browser only saves designs over HTTPS, so put a reverse proxy with a certificate in front (Caddy, Traefik, nginx); the app works under any URL path, such as https://example.com/cad/. To build the image without Compose, from the repository root: docker build -f packaging/docker/Dockerfile -t solidcraft-web .

3D mouse in the browser

The web app reads a 3Dconnexion SpaceMouse or SpaceNavigator through the browser’s Gamepad API, with the same switch and speed in Preferences › Navigation as the desktop app. Edge and Chrome read the device as it is. A browser shows a gamepad to a page only once it has been moved there, so if nothing happens at first, push the cap firmly once. 3DxWare also turns the cap into mouse-wheel zoom in browsers; while the cap moves, the web app ignores the wheel and reads the cap itself. Before the browser shows the cap to the page, that zoom still gets through, so the first push after opening the page may zoom a little.

Firefox doesn’t list 3D mice (Mozilla bug 1226575). On Windows it does list 3DxWare’s virtual joystick, which can carry the cap instead. Save this as %APPDATA%\3Dconnexion\3DxWare\Cfg\Firefox.xml, then restart Firefox (and 3DxWare, if the cap still only zooms). It changes Firefox only: there 3DxWare sends the cap’s six axes to the joystick instead of the mouse wheel. Delete the file to undo it.

<?xml version="1.0" encoding="UTF-8"?>
<AppCfg Default="false" xmlns="" CfgFormatVersion="1.3" ThisFileVersion="1.1">
  <AppInfo>
    <Signature>
      <Name>STR_FIREFOX</Name>
      <ExecutableName>FIREFOX.EXE</ExecutableName>
      <Transport>KMJ</Transport>
    </Signature>
    <Options/>
  </AppInfo>
  <CfgProperties>
    <ID>ID_STR_FIREFOX</ID>
    <Name>STR_FIREFOX</Name>
    <InheritsFromID>ID_STR_FIREFOX</InheritsFromID>
  </CfgProperties>
  <Devices>
    <Device>
      <ID>ID_Standard_3D_Mouse</ID>
      <AxisBank>
        <Name>STR_DEFAULT_BANK</Name>
        <ID>Default</ID>
        <Axis><Enabled>true</Enabled><Input><ActionID>HIDMultiAxis_X</ActionID><Min>-512</Min><Max>511</Max><Deadband>0</Deadband></Input><Output><ActionID>HIDJoystick_X</ActionID></Output></Axis>
        <Axis><Enabled>true</Enabled><Input><ActionID>HIDMultiAxis_Y</ActionID><Min>-512</Min><Max>511</Max><Deadband>0</Deadband></Input><Output><ActionID>HIDJoystick_Y</ActionID></Output></Axis>
        <Axis><Enabled>true</Enabled><Input><ActionID>HIDMultiAxis_Z</ActionID><Min>-512</Min><Max>511</Max><Deadband>0</Deadband></Input><Output><ActionID>HIDJoystick_Z</ActionID></Output></Axis>
        <Axis><Enabled>true</Enabled><Input><ActionID>HIDMultiAxis_Rx</ActionID><Min>-512</Min><Max>511</Max><Deadband>0</Deadband></Input><Output><ActionID>HIDJoystick_Rx</ActionID></Output></Axis>
        <Axis><Enabled>true</Enabled><Input><ActionID>HIDMultiAxis_Ry</ActionID><Min>-512</Min><Max>511</Max><Deadband>0</Deadband></Input><Output><ActionID>HIDJoystick_Ry</ActionID></Output></Axis>
        <Axis><Enabled>true</Enabled><Input><ActionID>HIDMultiAxis_Rz</ActionID><Min>-512</Min><Max>511</Max><Deadband>0</Deadband></Input><Output><ActionID>HIDJoystick_Rz</ActionID></Output></Axis>
      </AxisBank>
    </Device>
  </Devices>
</AppCfg>

CLI

solidcraft-cli runs the same commands without a window:

cargo run --release -p solidcraft-cli -- commands                       # every command, as JSON
cargo run --release -p solidcraft-cli -- run examples/bracket.json --out bracket.step
cargo run --release -p solidcraft-cli -- eval examples/bracket.json     # volume, area, faces…
cargo run --release -p solidcraft-cli -- snapshot examples/bracket.json --out bracket.png
cargo run --release -p solidcraft-cli -- exec solid.box '{"length": 20, "width": 10, "height": 5}'
cargo run --release -p solidcraft-cli -- run mechanism.json --in arm.sc  # with a motion.export_animation step: a GIF

A script is a list of commands:

{"commands": [
  {"command": "sketch.create", "params": {"plane": "XY"}},
  {"command": "sketch.rectangle.two_point", "params": {"p0": [0, 0], "p1": [40, 30]}},
  {"command": "sketch.finish", "params": {}},
  {"command": "solid.extrude", "params": {"distance": 20}},
  {"command": "solid.fillet", "params": {"edges": [[0, 0, 10]], "radius": 3}}
]}

snapshot looks at the model from the iso view. --view NAME takes another standard view (front, back, top, bottom, left, right) or the name of a view saved in the design with view.save. --camera JSON takes any camera, as the MCP screenshot tool does (its conventions are in the MCP page). A script that applies a view itself with view.apply sets the camera too. The ground grid lies on Z 0, so it cuts through a part that straddles it: --no-grid leaves it out of the picture, and --no-axes the origin’s axes. The picture is drawn on the GPU with the viewport’s own pipelines, so it shows what the window would (a surface analysis such as inspect.zebra included); a machine without a GPU uses a software one (Mesa’s lavapipe on Linux, WARP on Windows), and with neither (a Mac without usable Metal, a Linux box without Mesa’s Vulkan) the CPU rasterizer draws it, without surface analysis colours, and the reply’s renderer says cpu with a warning. SOLIDCRAFT_GPU=auto|hardware|software|cpu chooses (hardware and software fail rather than fall to the CPU), and solidcraft-cli gpu lists the adapters and the tier a snapshot would use.

solidcraft-cli snapshot part.sc --out photo.png --view Photo
solidcraft-cli snapshot part.sc --out close-up.png --no-grid --no-axes --camera '{"target": [30, -20, 0], "yaw": 0.6, "pitch": 0.25, "distance": 60}'

Seeing what a cut or a join is about to do

A picture answers in a glance what a volume cannot: does the tool body reach through the material, and is it where you meant it to be? --body NAME:opacity=0.3 draws that body see-through, so whatever is inside it shows; --body NAME:color=#cc3333 gives it a colour. Either key on its own keeps what the design says about the other, and the option is a shortcut for appearance.assign, the command the Appearance dialog runs — the picture is the one the window would show with the same appearance assigned.

--tool NAME is the common case in one option: that body solid in the tool colour, every other body see-through around it. --section z=27 cuts the model at a plane so the inside shows, as inspect.section does: an axis and the world coordinate to cut at, ,flip to keep the other side, XY, XZ or YZ for a plane through the origin, or the command’s own JSON.

# The pin the cut would remove, inside the plate it stops 6 mm short of.
solidcraft-cli snapshot plate.sc --out preview.png --tool Pin
solidcraft-cli snapshot plate.sc --out cut.png --tool Pin --section z=12
solidcraft-cli snapshot plate.sc --out glass.png --body "Plate:opacity=0.25" --body "Pin:color=#1a7f4b"

A body drawn see-through is drawn see-through everywhere a picture is drawn, not only here: an appearance with an opacity below 1 (Glass - Clear in the library, or any opacity you assign) now reads as glass in snapshot, in MCP screenshots and in a design’s thumbnail, as it always has in the window. A face painted with a see-through look of its own still draws at its body’s opacity.

Windows PowerShell 5.1 drops the double quotes inside an argument it passes to a program: write them as \" there.

A command that fails stops the script. A feature can also fail later, when a parameter change rebuilds it; run then exits with status 1, lists the failing features and writes none of its --out and --save files, because the bodies lack what those features should have made. --allow-errors writes them anyway.

--quality standard|fine|finest sets how closely an STL, OBJ or 3MF written by --out follows curved faces, as file.export’s quality does: a chord tolerance of 2e-4, 5e-5 or 1e-5 of each body’s size, though never under 1 µm, so on a small body fine and finest can come out the same. The default is standard.

solidcraft-cli run --in part.sc --out part.stl --quality fine

--param-check warn|strict checks each step against its command’s parameter contract, for the commands that have one (describe_command in the MCP server shows them); the default, off, checks nothing. A misspelt optional key, such as operaton for solid.box’s operation, is otherwise ignored, and the step builds without it. warn runs the step and prints script.json: step 1: unknown key `operaton`; did you mean `operation`? on stderr; strict stops the run at that step, and nothing is written.

run prints each command’s result with --results (otherwise the measurement), and eval prints the measurement, as JSON on stdout. --json-out FILE writes that JSON to a file instead. The file is plain UTF-8, which is not what a redirect gives you everywhere: Windows PowerShell’s > re-encodes a program’s output, as UTF-16 or as UTF-8 with a byte-order mark depending on its settings, and strict JSON readers such as Python’s json.load then refuse it.

cargo run --release -p solidcraft-cli -- run examples/bracket.json --results --json-out results.json
cargo run --release -p solidcraft-cli -- eval examples/bracket.json --json-out measures.json

To find the slow steps of a script, --timings prints how long each command took instead of the measurement: {"timings": [{"index", "command", "ms"}], "total_ms"}, with index counted from 0. --profile adds each command’s profile: {"boolean_ms", "tessellate_ms", "other_ms"}, its wall time in the kernel’s booleans, in tessellation and in everything else, and prints the profiler’s table on stderr: where the time went, the kernel’s calls to the commands (docs/perf.md, “Where the time goes”); --profile-out trace.json also writes the spans as a Chrome trace. --profile works on a design or STEP file too. With --results as well, the results go in the same object as results; --results alone still prints a plain array.

cargo run --release -p solidcraft-cli -- run examples/bracket.json --profile

A design’s history turns into a script that rebuilds it, which makes a bug that only shows on one design a one-file reproduction:

cargo run --release -p solidcraft-cli -- script part.sc --out repro.json
cargo run --release -p solidcraft-cli -- eval repro.json               # the same part

The script starts with the user parameters (parameters.add) and the design’s units, sheet metal and plastic rules and materials, then adds each timeline feature with timeline.add, exactly as the design file holds it: same ids, names and parameter names, so later steps still find the sketches and bodies they use, and a feature that failed in the design fails the same way. Edit a step’s values to try a change, or delete the steps after a feature to see the design as it was there. Appearances, folders, named views and configurations are left out (the CLI notes which), and designs with components or joints are not supported yet. In the app and over MCP the same script comes from timeline.export_script.

Units are millimetres and degrees unless an expression says otherwise ("1 in", "30 deg"), and Z is up.

Name and compatibility

SolidCraft was renamed from an earlier working name. Since 0.5.0 everything uses the new name: the solidcraft and solidcraft-cli programs, .solidcraft designs (.sc since then), SOLIDCRAFT_* environment variables, settings folders and the solidcraft MCP server. Upgrading keeps working: designs, settings, recovery files, environment variables and CLI/MCP setups from earlier builds carry over. Every compatibility shim is listed in the compatibility notes.

Use the SolidCraft documentation and web-app URLs. GitHub redirects links to the renamed repository; old-name Pages and Flatpak URLs are not maintained as permanent aliases.

A tour of the window

The SolidCraft window with the sample plate open, its parts numbered 1 to 8 The SolidCraft window with the sample plate open, its parts numbered 1 to 8

1 Title bar · 2 Toolbar · 3 Browser · 4 Viewport · 5 Axis view · 6 Navigation bar · 7 Timeline · 8 Command search

To get around the view, drag with the right button to orbit, drag with the middle button to pan and turn the wheel to zoom at the pointer. The house above the axis view goes back to the Home view.

  • Title bar. On Windows and Linux the application bar is the window’s title bar. On the left: the File menu (New, Open, Save, Save As, Export…), undo and redo, the Home tab (the start page) and one tab per open design (each with its own undo history, selection and camera; a dot marks unsaved changes). Double-click a design’s tab to rename the design: type the name, then press Enter or click elsewhere to keep it, or Esc to leave the old one (Preferences › Interface turns this off, for a double-click that maximizes the window instead). On the right: Report a bug, Help (?), Preferences (which hold the theme) and the window’s buttons. On Linux, to have your desktop’s own title bar and window buttons instead, set SOLIDCRAFT_TITLEBAR=system or put "titlebar": "system" in graphics.json in the settings folder (see Troubleshooting) and restart; app (the default) puts the application bar back. The operating system’s window title automatically shows the active design, such as Bracket — SolidCraft. Unsaved changes add an asterisk: Bracket * — SolidCraft. Taskbars and window switchers use this title; the application bar itself is unchanged. With no design open, the title is SolidCraft.

  • Toolbar. The workspace switcher (DESIGN) sits on the left. The tabs (MODEL, SURFACE, ASSEMBLE, INSERT, INSPECT, MANAGE, VIEW and SHEET METAL) hold sections of commands. SKETCH appears while editing a sketch; SHEET METAL is always there and is highlighted for the active sheet body. Both select automatically when entered, and the other tabs remain available. Click a panel’s name for all its commands. With Switch panel menus on hover enabled in Preferences > Layout > Toolbar tabs, click once to open a menu, then hover another panel’s name to switch. Move outside the ribbon and open menu to close it. Drag a command from the dropdown onto a panel’s label or empty space to append it; the insertion line shows its destination. Drop onto an existing button to insert before it. Buttons can also move between panels, and the layout is saved. Click the + after the tabs, right-click a tab or empty toolbar space, or use Preferences > Layout > Toolbar tabs to hide or restore tabs. Every existing tab, including Sheet Metal and Weldments, stays visible by default. Tabs you hide stay hidden in every design until you show them again. Hidden tabs keep their customised buttons, commands in search and shortcuts; with all tabs hidden, + remains.

  • Browser (left). The design as a tree: document settings and units, named views, the origin, bodies, sketches, construction planes, canvases, joints and components. The eye at the row’s right end shows or hides an item (Preferences › Interface › Eye next to the fold arrow puts it back beside the fold arrow; press an eye and drag up or down over other eyes to show or hide them all at once, as one undo step), a component’s radio button makes it the active component, and right-clicking any row gives that item’s menu. Right-click the design or a component and choose Organize → Collapse All to close every component, folder and group in the whole tree, at every level (the design row stays open, so its first level stays listed). On the design, Organize → Expand All opens everything; on a component, Expand Below and Collapse Below open or close only the rows under it. Components start closed, so a large assembly opens as a short list; open one with its fold arrow, or use Find in Browser on a body to open the rows down to it (Preferences › Interface › Components start open opens every component from the start, as before). Preferences › Layout › Browser rows start closed (off by default) starts folders closed too, and components whatever Components start open says: they open with their arrow, Expand All, or Find in Browser on a body, face or edge in the view, which opens just the rows down to that body, and a pick in the view leaves the Browser as it is. Rows drag into groups and other components. Preferences › Interface › Bodies folders sets when a component’s bodies sit in a Bodies folder of their own: Several bodies only (the default) lists a component’s body straight under it when it has only one and no groups, Never lists every component’s bodies and groups straight under it (the folder’s menu items are then on the component’s menu), and Always keeps the folder. The design’s own Bodies folder, and a folder hidden with its eye, always stay.

  • Viewport. Click to select, Shift or Ctrl to add (Preferences › Selection), drag for a window or crossing box. While sketching, Ctrl/Cmd or Alt/Option suppress auto-snap by default; Shift prefers a midpoint; the pointer also snaps to where two curves cross. To reach something under something else (two lines drawn on top of each other, coincident points, an edge in front of a face, overlapping bodies), hold the left button still for half a second (long-press on a touch screen) or right-click › Select Other…: a list of everything under the cursor opens there, including hidden bodies and sketches near the pointer. The popup grows to fit the available window space, opening above the pointer when it needs the extra room, and scrolls only when the rows do not fit. Turn off Select hidden geometry with Select Other in Preferences › Sketch & selection to restore the original visible-only list under the pointer. Picking a hidden object leaves its visibility unchanged, so an Extrude profile can be chosen through a covering body. The sketch being edited comes first. Hovering a row lights it up; clicking it selects it (Shift or Ctrl adds), and in a command’s dialog it goes to the active selection box. Esc or a click elsewhere closes the list. Clicking again on the same spot, with nothing changed in between, cycles through visible candidates at that point. The wheel zooms at the cursor, the middle button pans and right-drag orbits freely (the mouse preset in Preferences switches to SolidWorks, Inventor, Alias, Maya, Blender, Tinkercad, OpenSCAD, HFSS or Touchpad style). On a trackpad, pinch zooms and two-finger scroll pans (Navigation › Two-finger scroll pans; off: scroll zooms like the wheel); mouse wheels zoom and the thumb wheel pans sideways. On Linux, pinch needs the native Wayland window system (see Troubleshooting). To zoom with vertical two-finger scroll while holding a key, choose the key in Navigation › Zoom with two-finger scroll while holding (Cmd on macOS or Ctrl on Windows/Linux, Alt, or Control). It is off until you choose one. With the Fusion preset and two-finger pan enabled, hold Shift while scrolling with two fingers to orbit freely instead of panning. No click is needed. Releasing Shift returns to panning, even during the same gesture. Shift does not snap this gesture to standard views; orbit gestures do nothing on flat 2D pages. Other presets keep their existing scroll behaviour. Shift+middle orbits freely in the default and Inventor presets. In every preset, Ctrl+left drag (Cmd on macOS) that starts on empty space orbits; a Ctrl+click still adds to the selection. Shift+right-drag snaps to the nearest standard or diagonal view; Shift does not request snapping when the preset already requires it to orbit. An orbit drag that reaches the edge of the view carries on: the cursor reappears at the opposite edge, so you can keep turning round the model without letting go (not on Wayland or in the browser). Preferences › Navigation › Wrap the cursor while orbiting turns this off.

    Enable Preferences → Navigation → Free orbit to rotate the mouse and 3D mouse continuously through both poles, preserving roll. During an orbit drag, Shift snaps the view direction and the picture rotation to the nearest quarter turn; releasing Shift resumes the unsnapped rotation. Under it, Turn around the vertical axis makes sideways mouse drags spin the model around the world’s vertical axis instead of the screen’s, while up and down drags still tilt over the top without a limit. Both default to off for every mouse preset. With SolidWorks navigation, right-click a planar face and choose Normal to Selection to use Look At (also Ctrl+8); repeat to flip the side, preserving scale and projection.

    Right drag orbits (Preferences › Navigation, on by default) is shown where the mouse preset orbits on a right drag. Off, a right drag leaves the camera alone and the right button only opens the context menu. Picking the SolidWorks preset turns it off and sets Middle double-click to Fit; both can be changed back afterwards.

    Mouse gestures (Preferences › Navigation, off by default) work with the SolidWorks mouse preset only (the wheel orbits, Ctrl + wheel pans) and take over the right button, which then no longer orbits: hold it and move the pointer 4 pt to open a ring of tools (280 pt inside, 350 pt outside), even while a tool is active, and touch a tool’s zone to take it. A right-click that does not move still opens the usual context menu. Each studio has its own tools, and Customize… sets how many tools a studio’s ring holds and in which place. Zones alternate between two tints, three when the number of tools is odd. The optional radial context menu is a separate setting, and gestures take the right button from it while they are on.

    With Touchpad selected in Preferences › Navigation, hold Shift and move the pointer to pan, or hold Alt (Option on macOS) and move it to orbit—no mouse button is needed. Vertical two-finger scrolling zooms in this preset, regardless of the saved two-finger pan setting; switching presets restores that setting. Release the modifier to stop navigating. Shift+click and Shift+left-drag still select. Combined Shift+Alt or Ctrl/Cmd modifiers do not start pointer-only navigation. The axis view shows the world’s X/Y/Z directions in SolidCraft’s colors. Click a signed axis tip to look along that axis; click the aligned tip again to look from the opposite side. Drag the axis view to orbit around the view centre, with Shift for snapping. Top and bottom keep the nearest quarter-turn heading. The house, the navigation bar’s Home button and a middle-button double-click in the view go home, framing what is shown now (Navigation › Middle double-click can make it Fit, which keeps the orientation, or turn it off). Interface › OK and Cancel in the view’s corner puts a large check and cross at the view’s top right while a dialog or a sketch is open, and moves the axis view or cube down below them. With a dialog open they do what its OK and Cancel do (the check is grey whenever OK is); in a sketch without a dialog they are Finish Sketch and Cancel Sketch. View angles in the navigation bar’s View menu (or Preferences › Graphics, or the view.angle_readout command) shows the view’s pitch, yaw and roll in degrees under the axis view, with Ortho or Persp for the projection; it is off by default and updates as the view turns. The angles turn about the up axis: yaw 0° is the Front view, 90° Left, −90° Right and 180° Back; pitch 90° is the Top view and −90° the Bottom; a positive roll turns the model clockwise on screen. Click the readout to type exact angles; Apply turns the view there (it keeps the view’s centre and zoom). Scripts set the same angles in radians with view.apply. Right-click for the marking menu: eight commands around the cursor, plus a menu for whatever is under it. Near the window’s bottom or right edge a menu opens above or to the left of the cursor, so it stays in view. Shaded views omit certified smooth joins between spline spans on extruded walls. Real creases, cap outlines and authored face splits remain visible. Wireframe retains its edge presentation, and hiding a join does not remove its CAD identity or change the exported solid. Unsupported or uncertain wall geometry keeps its existing edge presentation.

  • Dialogs dock on the right with a live preview. Most features also show drag handles and a value box in the viewport. Preferences › Interface › Dialogs › In the left column opens them in the browser’s place instead (and the History’s, when it is at the left), at least as wide as that column; the column comes back as it was when the dialog closes. While a dialog holds the column, the view’s top-left corner shows the design’s name: click it to open the Origin, Bodies and (with the History at the left) the features over the view, and click a row to pick it for the dialog’s active box. Interface › Dialog buttons › Top puts OK, Cancel and Apply in the dialog’s title row, right of the title, where they stay while sections open and close; Cancel then also takes the place of the close button. Enter and Esc work as before. The Sketch panel’s Finish Sketch, Look at and Slice move up under its title, above the options.

  • Timeline (bottom). Every feature in order. Double-click to edit, drag the marker to roll back, and drag features to reorder them. A sketch’s icon, here and in the browser, takes the colour of its state: the sketch colour while it is under constrained, the fully constrained colour, or red while its constraints conflict; its tooltip names the state (Preferences › Sketch › Sketch icons in the colour of their state, on by default, turns this off). Preferences can put the timeline vertically at the far left of the viewport, beside the browser. Its seam handle switches between icon chips and compact feature names; the browser can collapse to a rail beside either width. Docked under the browser, the two share the column: drag the seam between them to give either more height, and the split is remembered. Its controls jump to the beginning, step back one feature, play, step forward one feature or jump to the end. Play History, in the middle of those controls and under Inspect → History, advances through the remaining features, pausing half a second after each rebuilt step appears. It becomes Stop History while running; it is also in command search and can be assigned a shortcut in Preferences → Keyboard Shortcuts. Click Stop History or press Escape to stop playback; navigation and editing stop it too. To keep the controls to four, right-click them and choose Hide Play Button, or clear the setting under Preferences → Interface. Interface › Sketches › Under their feature lists a sketch in the vertical timeline one level in, under the first feature built from it, behind that feature’s fold arrow. Dragging the feature takes its sketch along. A later feature built from the same sketch gets a row that selects it, and while the marker sits between a sketch and its feature, or the two are in different folders, the sketch keeps a row of its own. The timeline along the bottom always lists features in creation order. Feature Folders can contain joints as well as features; closing a folder hides both behind one tile.

  • Status bar (off by default: Preferences › Interface › Status bar). A thin bar along the window’s bottom edge, below the timeline. From the right: the design’s units (click them to change the design’s units, one undo step), the open sketch’s state in its colour (under constrained with its degrees of freedom, fully constrained, constraints conflict) and what is being edited (Editing Sketch1, or Editing Extrude1 while that feature’s dialog is open). While it is on, the sketch’s state no longer shows in the view’s top-left corner.

  • Command search (S). Click the ribbon search field or press S to open the search popup below it. Results group commands by their ribbon path and show matching features and user parameters from this design. With an empty query, recent commands appear first, followed by pinned commands in the Commands group. Up/Down choose, Enter runs, Tab fills the selected name, and Esc closes. A feature opens its existing editor; a parameter opens Change Parameters filtered to its name. To run with explicit values, enter an id and JSON object, for example solid.extrude {"distance": 20}. The popup validates JSON as you type and shows parser errors. Shift+Enter runs it and keeps the popup open. Right-click a command for its existing pin and toolbar actions.

Preferences (File › Preferences) hold the theme, workspace layout, the units of new designs, the mouse preset, the visual style and autosave. Graphics › Colour vision gives the colours that carry a meaning (sketch states, axes, bends, analyses, icons) values for a weak red, green or blue sense: Protan, Deutan or Tritan. In a mode, the faces Draft and Accessibility Analysis mark as failing are striped too. Graphics › Viewport › Perspective view draws the view with perspective, and its Field of view (15° to 90°, about 34° by default) sets how strong the perspective is. The model keeps its size in the view when the angle changes: a wider angle brings the camera closer. A sketch you are editing is always drawn without perspective. Command messages and errors show for a few seconds in the status chip at the bottom left of the view. Click the chip, or choose Help › Messages…, for every message of the session: newest first, errors in red, each line selectable, with Copy all for a bug report. Help › Keyboard Shortcuts lists every key, and File › Keyboard Shortcuts changes them.

Workspace layout

General › Workspace layout switches between Floating cards and Compact panels. Compact joins the workspace selector and ribbon into one top bar, puts Browser flush underneath with a square join, and docks tool sheets below the compass. Tool sheets remain draggable: their four corners round away from the right edge, and their two right corners square when docked there. Browser keeps only the height its contents need. Assistant remains on the outside edge, and History keeps its chosen bottom, left or shared Browser column. The choice is saved between launches. Switching preserves the current tool, camera, selection and design; Cancel and Restore page defaults work like other preferences.

General › Show text under ribbon icons is a separate, saved option for either layout. Turn it off for shorter icon-only command buttons; tooltips, section names and command menus remain available.

Quick-access bar. Right-click any ribbon button, a row of a section’s drop-down or a command search result and choose Add to Quick Access: the command’s icon joins the application bar, after Save, Undo and Redo. A click starts the command as its ribbon button does; drag a button along the bar to reorder it; right-click it and choose Remove from Quick Access to take it off. The bar is empty, and so not shown, until a command is added, and holds up to 24 commands. Narrow windows hide it with Undo and Redo. It is saved with the toolbar’s customisations, so it is exported with the keyboard shortcuts and carried by settings profiles.

Layout › Show only the ribbon’s tabs (off by default) folds the ribbon to its tab row, so the view gets the room its panels took. Click a tab to show its panels over the view; they close again when a command starts, when the pointer leaves them, when Escape is pressed or when the open tab is clicked again. The command Ribbon Tabs Only (VIEW tab, the toolbar’s right-click menu and command search) switches the option, so it can be given a key in Keyboard Shortcuts: choose it again to keep the whole ribbon shown. Scripts run view.ribbon_tabs_only with {"on": true} or {"on": false}, or with no parameters to switch.

Interface › Layout › Show origin and bodies at the top (off by default) puts the active component’s Bodies folder and the Origin (planes, axes and point) at the top of the History when it sits on the left or under the browser and is wide enough for names. They are the browser’s own rows: the same eyes, folds and right-click menus, so one list is enough for single-part work. The History at the bottom is unchanged.

Interface › Menus › Large icons (the default is List) shows a section’s command menu as rows of large icons instead of a list of names. Point at an icon for its name, shortcut and help; the arrow keys move across and between rows. Rest the pointer on an icon for two seconds and a pin in its corner can be clicked to pin it to the ribbon or remove it (a quicker click anywhere on the icon starts the command). Right-clicking the icon, or dragging it onto the ribbon, also pins it; a pinned command always shows its pin.

Interface › Leave pinned commands out of the drop-down menus (off by default) lists in a section’s menu only the commands that are not already buttons on the ribbon. When the window is too narrow for a section’s buttons, its menu lists them again.

Colour themes

Interface › Theme lists Dark, Light and System (follows the operating system, and changes with it; on Linux it reads the desktop’s dark or light style through the desktop portal, as GNOME and KDE set it), the built-in themes and any themes you have imported. A choice applies at once. Under the themes, Viewport draws the 3D view in the Light or Dark theme’s colours whatever the window uses, for example the model on a light view in a dark window (Follow theme, the default, keeps the theme’s own). Logo draws the SolidCraft logo at the top left in shades of the theme’s accent instead of the theme’s logo colours. The built-in themes are Gruvbox (dark and light, each in hard, normal and soft contrast), SolidCraft 95, Platinum 9, Cupertino, Green Phosphor, Amber Phosphor, Blueprint, One Dark, Tokyo Night, Solarized Light, Nord, High Contrast, Graphite, Nosferatu, Catppuccin Mocha, Okibi and Akari (dark and light, from the Okibi design system), Clay and Clay Dark (warm ivory paper and warm charcoal, with a terracotta accent and rounded corners), and 1-bit and 1-bit Dark (pure black and white with square corners: black on white, or white on black, with selections drawn inverted).

A theme is a small JSON file that starts from the Dark or Light palette and changes any of its colours and corner radii by name:

{
  "format": "solidcraft-theme/1",
  "name": "My Theme",
  "base": "dark",
  "colors": {
    "panel": "#282828",
    "text": "#ebdbb2",
    "accent": "#83a598",
    "viewport_top": "#32302f",
    "viewport_bottom": "#32302f",
    "axis_x": "#fb4934",
    "axis_y": "#b8bb26",
    "axis_z": "#83a598"
  },
  "radii": {
    "radius_sm": 2,
    "radius_md": 4,
    "radius_lg": 6,
    "radius_xl": 8,
    "control_radius": 4,
    "sketch_mode_radius": 4
  }
}

Colours are #rrggbb, or #rrggbbaa for translucent ones. A few colours, such as the four Continuity Analysis seams (continuity), take a list. Any colour the file leaves out keeps the base palette’s value. axis_x, axis_y and axis_z colour every X, Y and Z axis: the origin, the view triad, the navigation cube and the Move gizmo. A file from an earlier version may set triad or gizmo instead; it still reads, and those colours now go to every axis.

radii is optional. Its six keys above accept integers from 0 to 32 in logical UI points; zero makes square corners. Omitted keys keep today’s base values: 4, 6, 8, 10, 6 and 7, respectively. The first four style small accents through large surfaces; control_radius styles form controls, and sketch_mode_radius styles sketch creation buttons. Themes own these settings, with matching shapes across light and dark variants. Dock joins remain square, circular controls remain circular, and control sizes and spacing stay the same. Platinum 9, 1-bit, 1-bit Dark, and the phosphor themes use square corners, SolidCraft 95 uses subtle 3-point corners, Blueprint uses the standard radii, Cupertino uses softer corners, and Clay and Clay Dark use rounded corners. Other built-in themes retain the defaults.

Tab accents also have separate fill, small-text ink and tint keys. Sketch uses sketch_accent, sketch_ink, sketch_soft; Surface, Mesh, Sheet Metal and Render use <tab>_fill, <tab>_ink, <tab>_soft (sheet for Sheet Metal). Form uses form_fill, form_tab_ink, form_soft because form_ink names the general form text. Model keeps accent, selected_ink, accent_soft. The older surface_accent, mesh_accent, form_accent and render_accent keys retain their existing roles and values. tab_on_fill_dark is the dark text fallback for tab pills when on_accent has less than 4.5:1 contrast with the fill. See the per-tab token reference for all pairs.

  • Import… adds a theme file and turns it on. Importing a file with the same name again updates that theme. A file with an unknown colour name or a malformed colour is refused, and the message names every problem. A theme shared as text imports the same way without a file: with the Interface page open, press Ctrl+V (Cmd+V on a Mac), or right-click the Import tile and choose Paste theme. Pasted text that isn’t a theme is left alone. A theme file dropped on the tile imports too.
  • Export… saves the theme on now as a complete file listing every colour and radius. It is a good starting point for your own theme.
  • Remove deletes the selected imported theme.

Retired window-band and procedural icon colours in older exports are discarded on import. Command icons use the shared SVG palette.

Imported themes are kept with your preferences, so they work in the browser app too. Colour vision modes still apply on top of any theme.

Theme-file options and overlays

Theme authors can add options and overlays to solidcraft-theme/1. Offered options appear below the theme cards in Interface, with the theme’s name. Switches, sliders, segmented choices and dropdowns apply immediately and are saved per theme, including a followed theme file. Reset appears when an offered value differs from its default and restores that theme’s defaults through the same preference update path. Cancel retains its existing behaviour of restoring the Preferences window’s opening snapshot. Themes with no offered options add no options block or badge. Existing theme files need no changes or appearance changes.

Options flow left to right into two columns. A parent and its child occupy one cell; turning the parent off disables its child without forgetting the child’s value. Choices with four or five values and long labels take a full row; choices with more than five values use a dropdown. The first ten non-advanced top-level entries appear before More · N; advanced entries and overflow always appear inside it. Section labels appear only above six top-level entries. Help is available on hover. A registered behaviour unavailable on the current device stays disabled in its place with a reason; unimplemented standard behaviours and their children are absent. Theme cards show an Options badge only when the theme offers controls.

Export… preserves the authored definitions and overlays for a theme that declares them. Older themes still export a complete token snapshot. The resolver API clamps numeric values; invalid saved preference values instead fall back to the theme’s defaults, retaining the raw saved values for compatibility.

For example, add these two keys alongside a theme’s colors:

{
  "options": [
    {
      "id": "warm_accent",
      "type": "bool",
      "default": true,
      "label": {"en": "Warm accent"},
      "help": {"en": "Use a warmer accent colour."}
    },
    {
      "id": "rounding",
      "type": "range",
      "default": 4,
      "min": 0,
      "max": 12,
      "step": 1,
      "unit": "pt",
      "parent": "warm_accent"
    }
  ],
  "overlays": [
    {
      "when": {"opt": "warm_accent", "eq": true},
      "colors": {"accent": "#cc7744"}
    },
    {
      "when": {"opt": "rounding", "gte": 4, "lte": 12},
      "radii": {"control_radius": 8}
    }
  ]
}

A file can declare up to 16 options. Each id is unique and matches [a-z][a-z0-9_]{0,31}. Every option has a type and a default (opacity may omit its default to use 100):

  • bool: the default is true or false.
  • choice: values contains 2 to 12 entries, each with a unique id in the same syntax and a localized label map, for example {"id":"warm","label":{"en":"Warm"}}. The default names one of those ids.
  • range: finite numeric min, max and positive step are required, with min no larger than max and the default inside those bounds. Optional unit has at most 4 characters. Numbers must fit a finite 32-bit float. Saved values are clamped to the bounds; step is an increment for future controls, not a rounding rule.

Optional label and help maps use language codes as keys, with en as the fallback. Labels have at most 40 characters and help strings at most 160, per language. Each map supports up to 64 language codes, each at most 32 ASCII letters, digits, hyphens or underscores. These texts belong to the theme and need no application catalog entries. advanced is an optional boolean; section is an optional string of at most 24 characters. parent names a declared boolean option, with at most one child per parent and no cycles. While the parent is off, overlays referring to the child are inactive; the child’s value is retained.

Four ids are reserved for definitions owned by the app:

IdTypeBoundsUnitParent
opacityrange40–100%—
blurrange0–60px—
tab_coloursbool———
tab_fade_msrange0–600mstab_colours

A theme declares these with their required type and a default; it may also narrow a range’s min and max. The default is clamped to those bounds. Other fields are ignored, including presentation text, step (fixed at 20 for tab_fade_ms, 1 for other standard ranges), unit and parent. A wrong type or bounds outside the standard range are import errors. Declaring tab_fade_ms also requires declaring its boolean parent, tab_colours. Custom options cannot redefine reserved ids. opacity, tab_colours and tab_fade_ms have registered standard behaviours. blur remains absent from the list until its behaviour is registered. The resolver exposes declared standard options in the order opacity, blur, tab_colours, each immediately followed by its child, then custom options in declared order.

Opacity scales only these floating surface fill tokens: panel, panel_header, toolbar, timeline, dialog_bg, dialog_body, dialog_footer, overlay and nav_tip. Scaling is applied after matching overlays. All other tokens, including text, icons, borders, controls, viewport and document colours, retain their resolved values. At 100%, with the implicit 100% default, or without offered opacity, every token is exactly the original resolved value. The supported range is 40–100%; the theme may narrow it. To keep normal text at 4.5:1 against a panel over black and white viewport extremes, each fill’s actual alpha rises as necessary. This also protects against background overrides and busy models. A theme whose authored foreground and opaque fill already fail contrast receives full alpha; opacity never rewrites the theme’s text colours.

Imported themes can also declare tabs, using exact ribbon registry names. Only panel, viewport_top and viewport_bottom are accepted; an unknown tab or any other token is an import error. Tabs without an entry keep the base surfaces. Existing tab accent fills, inks and tints keep their own tokens. For example, save this original theme as a JSON file and import it:

{
  "format": "solidcraft-theme/1",
  "name": "Garden tabs",
  "base": "dark",
  "options": [
    {"id": "tab_colours", "type": "bool", "default": true},
    {"id": "tab_fade_ms", "type": "range", "default": 240}
  ],
  "tabs": {
    "SKETCH": {"panel": "#123028", "viewport_top": "#103128", "viewport_bottom": "#204a3c"},
    "RENDER": {"panel": "#302039", "viewport_top": "#30203d", "viewport_bottom": "#493252"}
  }
}

The imported light/dark fixtures also provide Model surfaces. No built-in theme declares tab surfaces. With tab_colours off or absent, every tab keeps the base palette. With it on, tab_fade_ms blends panel and viewport surfaces together in Oklab with an ease-out cubic curve; zero switches instantly. A tab change during a fade starts from the colours currently displayed. Opacity readability floors also apply to tab panels and interpolated alpha. A viewport set to the opposite light/dark palette keeps that palette; a flat background preference takes precedence over tab gradients. Definitions and authored defaults roundtrip through theme export; saved option values are excluded.

Render’s untouched, unsaved preview also uses these animated surfaces, including viewport palette and flat-colour preferences. Explicit scene backplates, transparency, environments, gradient backdrops and saved setups keep their existing background instead. Saved setups do not record whether their background was explicitly chosen, so even a setup saved only for its camera conservatively retains its scene backplate. With tab surfaces absent/off, Render keeps its original flat scene background. Theme surfaces never change rendered images or exports.

Gruvbox has two cards, Gruvbox Dark and Gruvbox Light. Each offers a Contrast choice: Soft, Standard (the default), or Hard. These are the same six palettes as the former separate cards, including their icon colours. Saved selections and settings profiles using the former -soft and -hard ids select the corresponding family and contrast. Selecting a family again restores its last contrast; Reset returns it to Standard.

Onyx is an original dark stone-grey theme with quiet surface layers, fine etched edges and a restrained blue-grey accent. Its defaults pair 80% Opacity (adjustable from 60–100%) with a broad 60 px Blur: Opacity sets how much of the scene shows through; Blur sets how widely its detail softens. Its custom controls are Accent (Slate blue, Silver or Sage), Contrast (Soft or Defined), Brightness (60–120%, default 100%), then Foreground colours (Vibrant or Muted), with optional Tab colours and a Fade control in the standard controls above. Edges always use the fine Etched finish. Brightness smoothly adjusts stone surfaces, viewport backgrounds and grid tones while preserving text and semantic foreground colours. Vibrant is the default, giving the axes, icons and modelling guides stronger colour. Muted preserves the original quiet palette. Accent independently controls selection and highlights; foreground modes keep the stone-grey backgrounds and red/green/blue axis meanings. Tab colours gently tint panels and the viewport while preserving the selected accent. Accent changes selection and control accents; Foreground colours sets semantic glyphs, workspace colours and status colours while retaining their meaning. As with other themes, the existing blur availability and readability limits apply.

Up to 32 overlays apply in file order, after the base palette and the theme’s own value tables. Later matching entries win by key. Every overlay has one when: {"opt":"id","eq":value} for an exact, correctly typed comparison, or gte, lte, or both for inclusive range bounds. A choice comparison must name a declared choice. Unknown option ids, mixed comparisons and malformed values are refused with the overlay’s one-based position. An overlay may override colors, radii, borders and icon_palette; all their existing key and value checks apply. Overlays only select literal tables: there are no scripts, expressions or interpolation.

The optional integer requires defaults to 1; this app supports levels through 2. A larger level loads the authored base value tables, disables options and overlays, and exposes a needs_newer flag through the theme API for future UI. The theme-definition API can export options and overlays intact; the existing token snapshot export keeps its previous shape. The value resolver reports notices for unknown saved ids, wrong types, unknown choices, non-finite numbers and clamped values. Invalid saved values for declared options fall back to defaults; unknown saved ids are omitted because they have no definition or default.

A custom percent range may declare "effect":"surface_brightness" with requires: 2. Its bounds must stay within 60–120%, include 100%, and use default 100 and step 1. This continuously scales neutral surface, viewport, grid and border RGB after overlays and tab tables, before opacity readability protection. Alpha, text, semantic colours and selection colours stay authored. At 100% the transform is exactly unchanged. Only one such effect is allowed per theme; other effects and non-range bindings are rejected. The custom option keeps its authored label and order, after the standard controls.

Follow a theme file

In the desktop app, Follow a theme file (under the themes) takes a theme file that another tool writes and keeps it as the theme: whenever the file changes, SolidCraft reloads it within about a second, and again when its window comes back to the front, without a restart. This suits tools that generate a colour scheme from your wallpaper or keep one scheme across your desktop and applications (pywal and matugen are two examples); have them write a solidcraft-theme/1 file from a template. Choose… picks the file, Stop following (or choosing any theme) turns it off; it is off until you choose a file. Only a file that reads completely is applied: while it is being written, or if it is broken, missing or larger than 256 KiB, the last good theme stays on and the setting says why. solidcraft --theme-file my-theme.json follows a file for one run without changing your preferences. The browser app has no file to follow; import the theme there instead.

Graphics › Frame rate shows frames per second in the view’s bottom-right corner. Click it, or turn on Frame rate details, for the performance overlay:

  • a graph of the last few seconds of frames, one bar per frame, with lines at 60 and 30 frames per second. The triangles drawn are a line over it, and a tick at the top marks each frame that rebuilt the scene, so you can see how geometry changes the frame time;
  • a histogram of frame times, the 1% low (the mean of the slowest 1% of frames) and the worst frame, which show stutters that an average hides;
  • what the GPU drew last frame (triangles, line segments, draw calls), the viewport’s size in pixels with its MSAA and supersampling, the time the interface took, the last scene build and the graphics backend and adapter.

While the readout is on, the view redraws every frame. With vertical sync the frame rate tops out at the display’s refresh rate; the interface time and the GPU counts still show the work.

Settings profiles

General › Settings profiles keeps the input and interface settings together under a name, for example “Laptop” and “Large monitor”, and switches them at once. A profile holds:

  • the Interface, Navigation and Sketch pages: theme, interface scale and font, layout, mouse mode and the other navigation options, the radial menu and mouse gestures with every studio’s command lists, and the sketch modifier keys;
  • the interface language;
  • the keyboard shortcuts, the toolbar and the S box favourites;
  • the imported theme it uses, so it travels with an exported profile.

Settings that belong to the computer stay out: the 3D mouse (calibration, driver, speeds and buttons), graphics, units and decimals, the AI page, recent files and window positions.

  • Active profile lists the profiles; choosing one applies it. While its settings differ from what was saved, a note says so.
  • Save keeps the current settings under the typed name, replacing a profile of that name, and makes it the active one.
  • Rename and Delete act on the active profile.
  • Export… saves the active profile as one solidcraft-profile/1 file; Import… adds one without switching to it (a name already taken gets a number).

Switching, like any other change in Preferences, is taken back by Cancel. A setting a profile holds but this computer refuses (an interface font that isn’t installed) keeps its current value, and the message says how many did. Import ignores unknown keys, leaves out values this version can’t read, and refuses files over 4 MiB. Scripts and agents reach the same actions with the settings.profile.* commands (save, apply, rename, delete, list, export, import).

Navigation › Smooth wheel zoom is on by default. Turn it off to apply each wheel or trackpad event immediately, with no smoothing tail. It is independent of Animate view changes, which controls axis views, Look At, Home and Fit. Re-enabling smoothing during a wheel gesture takes effect after that gesture ends, avoiding leftover zoom being replayed.

Navigation › Alias uses Alias’s camera keys: Shift+Alt+left drag orbits, Shift+Alt+middle drag pans and Shift+Alt+right drag zooms (drag down to move away, up to move closer). Without Shift+Alt, a left drag still selects with a box and drags sketch geometry.

Some Linux desktops take Alt+drag for themselves (moving or resizing windows), so the viewport never sees it. To free it:

  • KDE Plasma: System Settings › Window Management › Window Behavior › Window Actions, set Modifier key to Meta (or None).
  • GNOME: run gsettings set org.gnome.desktop.wm.preferences mouse-button-modifier '<Super>' (or set Tweaks › Windows › Window Action Key to Super).

Shift-orbit snapping includes 45-degree edge views and isometric corner views, with a lightly damped spring transition and a small boundary margin to prevent flicker.

On desktop, Ctrl/Cmd+Plus (or Equals) enlarges the interface, Ctrl/Cmd+Minus reduces it, and Ctrl/Cmd+0 restores 100%. These shortcuts update the UI scale preference. The UI scale slider in Preferences applies the new layout when released, so it stays still while dragging.

Theme files can also define borders: primary outer outlines, secondary major section rules, tertiary small dividers, container solid preview/card/keycap outlines, and independent control outlines. Each accepts a width from 0 to 4 UI points and a color in hex. Optional primary_inset adds a companion outline inside panel chrome, with a gap from 0 to 12 points. Missing settings keep existing borders. Blueprint uses a single white outer outline, dotted section lines and dark minor dividers. Secondary rules optionally accept pattern: "dotted"; the default is solid. Focus, selection and error outlines retain their semantic colours. See the UI-kit border hierarchy for the schema.

SolidCraft 95 also defines optional borders.bevel: a per-band width and the highlight, light, shadow and dark_shadow colours. The shared UI uses these for raised panels/buttons, recessed fields and reversed pressed-button lighting. Themes that omit this setting keep their existing flat borders.

Theme files may also set icon_palette: opaque colours for SVG display roles, and three-colour pale/medium/strong ramps for Solid, Surface, Mesh, Form, Sketch, constraints and construction. These settings colour the existing command artwork without changing its shapes. Omitted roles keep the default light/dark palette.

How a design is built

SolidCraft is a parametric, history-based modeller. You don’t shape geometry directly: you describe the steps that make it, and SolidCraft computes the result from those steps. Change a step or a value and everything after it is computed again. This page explains that idea and the usual ways of working with it. The pages after it cover each tool in detail; the glossary defines most of the terms used here.

The idea in one paragraph

A design holds a timeline: an ordered list of features. Most parts start with a sketch, a 2D drawing on a plane, held in shape by constraints and dimensions. A feature such as Extrude or Revolve turns the sketch’s closed regions into a solid body; later features add to it, cut into it or finish it (holes, fillets, shells). Every dimension and feature input is a parameter, so a size can be changed at any time, and the timeline rebuilds from the first feature that uses it.

sketch (plane + constraints + dimensions)
   └─ feature (extrude, revolve, …)  →  body
         └─ more features (holes, fillets, patterns, shell, …)
               └─ change a parameter  →  the timeline rebuilds

Principles that make a design easy to change

  • Capture intent, not just shape. A hole “centred on the face” or “10 mm from that edge” stays right when the part grows; a hole placed by eye doesn’t. Use constraints and dimensions for what matters, and name the important values as parameters (type width = 40 in any value box: see Parameters and expressions).
  • Fully constrain sketches. Under-constrained sketch geometry is drawn blue and the Sketch Palette shows how many degrees of freedom are left; once none are, the geometry changes colour (white in the default dark theme) and the status reads Fully constrained (see Sketching and constraints). A fully constrained sketch changes only the way you tell it to.
  • Keep sketches simple, let features do the work. A rectangle and an extrude, then a fillet, is easier to change than one sketch that draws the rounded outline. Holes, patterns, mirrors, fillets and chamfers are features of their own for the same reason.
  • Build in a sensible order. The base shape first, then the features that add and cut, then the finishing features (fillets, chamfers, shell, draft). Features depend on the ones before them, so an early change ripples forward.
  • Sketch on stable references. An origin plane (XY, XZ, YZ, or Top, Front, Right) or a construction plane changes less than a face that a later edit may remove.

The timeline is the design

The timeline, the History strip along the bottom of the window, lists every feature in the order it is computed. Each feature can be edited (double-click), suppressed, renamed, reordered when nothing it depends on moves after it, and deleted; deleting asks first when other features depend on it.

Drag the rollback marker back to see the design as it was at an earlier step. Features after the marker are kept, not deleted, and a new feature is added at the marker. This is how you add something early in the design (a sketch the later features should use) without rebuilding the rest.

The timeline is not the undo history: Undo and Redo move back and forth through what you did; the timeline is what the design is made of.

Bodies and components

A body is one connected solid or surface shape. A design can hold many bodies, and features can make a new body, join or cut existing ones, or intersect them.

A component groups bodies, sketches and construction geometry, with its own origin, and can be placed more than once; each placement is an occurrence. Editing a component changes every occurrence of it; moving an occurrence moves only that copy. The active component (the radio button in the browser) receives new sketches and features.

Two ways of working follow from this:

  • One part, several bodies. For a single part, or a few parts that are designed against each other, model bodies in one design. Bodies are quick to make and to combine, split or cut with each other.
  • Components and joints. When parts are separate pieces (they move, repeat, or go on a bill of materials), make them components: New Component, or Create Components from Bodies to turn bodies you already modelled into components. Then connect them with joints (rigid, revolute, slider, cylindrical, pin-slot, planar, ball) and drive the motion. See Components and joints.

Starting with bodies and turning them into components later is a common path: it keeps the early design quick and makes the assembly when its structure is clear.

Common workflows

You want toStart withRead
Model a part from scratchNew Sketch (Create Sketch) on an origin plane, constrain and dimension it, Extrude or RevolveSketching, Solid features
Make a design you can resizeUser parameters, added with Parameters (Change Parameters), used in sketch dimensions and feature inputsParameters and expressions
Build an assemblyBodies turned into components, or New Component, then jointsComponents and joints
Design a bent sheet partA base flange from a sketch, then flanges, hems and folds under a sheet metal ruleSheet metal
Design a plastic enclosureA shelled body, then bosses, lip and groove, snap fits, ribsPlastic parts
Work on a part from another programOpen a STEP, IGES, 3MF, STL or OBJ file as a new designImport and export
Script a design or hand it to an agentCommands in a script for solidcraft-cli, or the MCP serverGetting started, MCP

Everything you click is a command with named parameters, and the toolbar, scripts, the CLI and AI agents all run the same commands. A design built in the window can be turned into a script with solidcraft-cli script, which rebuilds it without the window (designs with components or joints are not supported there yet).

Terms if you come from another CAD program

Other programs use other words for the same ideas. SolidCraft uses one term for each:

SolidCraftYou may know it as
timeline (shown as the History strip)history, feature tree
rollback markerrollback bar, end-of-part marker
browsermodel tree, feature tree, design tree
bodysolid body, surface body
componentpart, sub-assembly
occurrenceinstance, component instance
designdocument, model, part file, assembly file

The full list, with what each term must not be confused with, is in the glossary.

Finding help

  • Help (?) › Documentation in the title bar opens this manual.
  • Help (?) › Keyboard Shortcuts lists the shortcuts; see also Keyboard shortcuts.
  • The command reference describes every command and its parameters.
  • Report a bug, in the title bar, drafts an issue report with the details the maintainers need, for you to review and file on GitHub; the app doesn’t send anything itself.
  • Questions and ideas: the Discord and the issue tracker.

Sketching and constraints

A sketch lies on a plane: an origin plane, a construction plane or a planar face. Start one with Create Sketch (sketch.create) and click a plane, or pick a face first. Right-clicking a plane, in the view or on its browser row, offers Create Sketch on that plane too. While Create Sketch waits for its plane, the origin planes are shown, and the plane under the pointer (in the view, or its row in the browser) lights up and takes the grid, so you see which plane the sketch will lie on before you click. Finish Sketch returns to the model. Cancel Sketch (sketch.cancel) returns without keeping what changed since the sketch was created or opened for editing: a new sketch is removed, an edited one goes back to how it was, as one step that Undo brings back.

Step by step

A first sketch: a 60 × 40 mm outline with a 12 mm hole in it, on the Top plane.

1. Start the sketch. Click New Sketch. Each origin plane lights up as the pointer passes over it; click the flat one (Top, XY). The sketch opens with the view facing it.

2. Draw. Pick Rectangle, click the origin for the first corner and type the sides: 60, Tab, 40. Click (or press Enter) to place it. Then pick Circle, click a centre inside the rectangle, type 12 for the diameter and click. Each value you type becomes a dimension.

3. Dimension. The rectangle is pinned by the origin and its two sides, but the circle can still move. Pick Dimension, click the circle’s centre, then the left side, click where the dimension goes and type 20. Do the same from the bottom side. With nothing left free, the sketch is fully constrained and its curves turn white.

4. Finish. Click Finish Sketch to go back to the model; the shaded region is a profile that Extrude can use (Solid features).

The finished sketch, numbered 1 to 5 The finished sketch, numbered 1 to 5

1 Sketch tools · 2 A constraint (this side is vertical) · 3 A dimension · 4 The sketch’s state · 5 Finish Sketch

Two more tools. Offset (O) draws a copy of curves at a distance: click one side of the rectangle and the whole outline comes along (Chain), type 4 and press Enter for a wall 4 mm inside it. The distance is a dimension like any other.

Trim (T) removes the piece of a curve between the curves that cross it. With a circle drawn on the top side, click the piece of the side inside the circle, then the half of the circle outside the rectangle: what’s left is a notch.

Working in a sketch

Opening a sketch turns the view to face it; to keep the view as it is and face the sketch only with Look At, turn off Preferences › Sketch › Turn to face a sketch when it opens. When the sketch ends, the view stays where it is by default. Preferences › Sketch › When a sketch ends changes that: Return to the previous view goes back to the view from before the sketch, and Return unless rotated goes back only when you panned or zoomed the sketch view but didn’t turn it.

In an assembly a new sketch belongs to the active component and is listed in its Sketches folder. Turn on Preferences › Sketch › Sketching on a part’s face makes that part active to keep sketches with the part they are drawn on: a sketch started on a face of another component’s body first activates that component, at the placement you picked, and is made there, as if you had activated the part yourself. The part stays active afterwards, so an extrude or cut made from the sketch lands in the same part; activate the design or another part again as usual. Off (the default), the sketch belongs to the active component. In scripts the same is sketch.create {"plane": {"face": …}, "activate_part": true}; the reply’s activated names the component and placement it activated.

In scripts, XY, XZ, YZ and their Top, Front, Right aliases name origin planes. Preferences › Interface › Origin plane names shows them as Top, Front and Right in the browser, dialogs and lists; scripts and saved designs keep XY, XZ and YZ. These names are reserved: a construction plane cannot be created with or renamed to one (case-insensitive). If an older design already has a construction plane with such a name, a string argument naming it is ambiguous and is rejected; use the other alias (XY for Top) for the origin plane, the construction feature’s numeric ID to select it, or an explicit {origin: [x,y,z], normal: [x,y,z]} plane. Existing saved references retain their meaning; opening a design does not reinterpret old sketches.

Plane Through Axis And Point (Model > Construct) creates a plane containing an origin or construction axis, straight model edge or sketch line, and a point off that line. Pick the axis first, then a vertex, sketch point or construction point. A point on the axis leaves infinitely many possible planes, so choose an off-axis point. The plane follows its references when they change and can support a new sketch. Scripts use construct.plane.axis_point, for example:

{"axis":{"axis":"Z"},"point":{"point":[10,20,30]},"name":"Fence"}

Curves adapt to the current zoom and display scale. Picking and highlighting use the same visible boundary, including small circles at Fit and sketches in rotated components. Profile shading follows those curves while preserving holes. Zooming changes the display without changing sketch geometry, dimensions, or undo history. On short windows or enlarged displays, the Sketch Palette’s options scroll while its constraint status and Finish Sketch button remain visible.

Hover over a circle or arc centre, or the midpoint of a straight sketch edge, then move horizontally or vertically to see a dotted alignment guide. The guide connects that reference to the cursor’s projection on the sketch axis, within the viewport; it disappears when you move away from the axis. SolidCraft keeps the two most recent of these references and can draw both guides. One guide on its own does not move the cursor. While a drawing tool (Line, Rectangle, Circle, Arc, Polygon, Slot, Point…) waits for a point, any sketch point is a reference too: the end of a line or arc, a rectangle’s corner, or a free point. Preferences › Sketch › Alignment guides from end points and corners while drawing turns that off and keeps only centres and midpoints. Preferences › Sketch › Auto snap to recent centers and midpoints is off until you turn it on. With it on, a drawing tool snaps the cursor where the horizontal through one reference meets the vertical through the other. On a rectangle, the top midpoint and a side midpoint then meet at the rectangle’s centre, so that point can be placed without a construction line. Holding Shift for midpoint-only snapping skips the crossing. This works while editing and before a drawing tool’s first click. Placing the point adds no geometry or constraint of its own. Esc, leaving the viewport, or ending the sketch clears the references. Center and midpoint alignment guides turns the guides off, which also leaves the cursor unmoved; the configured snap-suppression modifier does the same for that gesture.

Geometry

Point is available directly on the default Sketch CREATE toolbar. Click it, then click in the sketch to place a point. Existing toolbar customizations are kept; use Reset Toolbar from the toolbar’s context menu to restore the default buttons.

Lines and polylines (L), rectangles (two-point R, three-point, centre), circles (centre C, two-point, three-point, tangent), arcs (three-point, centre, tangent), polygons, slots, ellipses, fit-point and control-point splines, conics, points and text. Construction (X) turns curves into reference geometry that doesn’t form profiles. Modify tools include trim (T), extend, break, fillet, chamfer, offset (O), mirror, patterns, move/copy and scale. With trim on, click a piece to remove it, or press and drag a path across several pieces: the ones it crosses light up and all go when you let go, in one undo step (sketch.trim.path). Fillet and chamfer take several corners at once: pick the corners, set the size once, and every corner is rounded or cut alike in one undo step.

Line draws a chain: each click adds a segment from the last point, and a right-click or Esc ends the chain. To draw one separate line with a press, a drag and the release instead, turn on Preferences › Sketch › Press and drag draws one line: the line runs from where you press to where you let go, snapping and lining up as clicks do, and ends there; clicks still draw a chain, and a drag shorter than about 24 points is still a click.

To round corners without changing tool, turn on Preferences › Sketch › Rest on a line’s last point to switch to a tangent arc. With a segment drawn, move the pointer away from the chain’s last point, bring it back and hold it still for about a third of a second: a label next to the pointer says Tangent Arc, and the next click ends an arc tangent to the last segment. The same gesture goes back to lines. Arcs and lines join into one chain, each click is one undo step, and a right-click or Esc ends the chain as usual.

Tools with several variants (line and midpoint line; two-point, three-point, centre and rounded rectangle; the circle, arc, slot, polygon and spline variants) share one toolbar button with a drop-down. The button and the family’s shortcut start the variant you picked last, so a shortcut such as L can start a Midpoint Line; pick the variant you want from the drop-down to change it (Keyboard shortcuts).

Once a shape’s first point is down, value boxes (a line’s length and angle, a rectangle’s width and height, a circle’s diameter, an ellipse’s major and minor radius…) follow the cursor. Typing goes into the first box, Tab moves to the next and Enter places the shape; each typed value becomes a dimension. A value is an expression (25, 1 in, d1 * 2), or width = 20 (also #width = 20), which also adds the user parameter width and dimensions with it, as when placing a dimension. A box that does not become a dimension (a polygon’s sides, a rounded rectangle’s corner radius) takes a value only.

The Sketch Palette’s Construction Geometries option hides construction curves while you work. A hidden construction curve cannot be picked by a click or by a box. Nothing invisible is selectable. The option belongs to one sketch and is saved with the design. Centerlines are a line type of their own and keep showing. Projected geometry that carries the construction line type, such as the trace of a plane, answers to Projected Geometries instead. The option changes display only. Construction curves were already outside every profile.

The Construction Line drop-down in Sketch CREATE draws infinite reference lines directly. It contains two-point, horizontal, vertical, angle, parallel, perpendicular, tangent through an external point, tangent at a curve point, common tangent, and angle-bisector tools. These lines remain construction geometry regardless of the Sketch Palette’s drawing line type. Turn off Extended construction line tools in Preferences > Sketch to restore the earlier menu.

For horizontal or vertical lines, click the point to pass through. For a line at an angle, click its point, then type the angle from sketch X in the inline box and press Enter, or click the direction. Angle expressions can refer to parameters and follow their later changes. Parallel and perpendicular lines ask for a straight sketch line, then a point; the reference stays picked so you can place several lines. Right-click clears the current points and references; Escape ends the tool.

The horizontal, vertical, parallel and perpendicular tools show a Length box beside the pointer, which stays empty (infinite) until you type a digit: letters still start other tools. With a length, the first click sets the start and a second click the side the segment runs to; the angle tool has the Length box beside its Angle box. The segment gets a length dimension that follows the typed expression. An empty box keeps the infinite line. The Construction | Normal switch under the Length box makes such a segment a normal sketch line, ready to close a profile; it starts at Construction, is greyed out while the line is infinite (an infinite line is always construction), and leaves the Sketch Palette’s line type alone.

A command call can cut a horizontal, vertical, angle, parallel or perpendicular construction line to a length instead: length makes a segment from the point at, running towards near, and adds a length dimension that follows its expression (#10258). With construction: false that segment is a normal sketch line, ready to close a profile.

Tangents through an external point take a circle or circular arc, the external point, and a click choosing the desired preview. A common tangent takes two circles/arcs and a click choosing the solution. Contacts outside an arc’s drawn sweep are excluded. A tangent at a curve point takes a circle/arc and a contact point, or an endpoint of an open spline or conic. Interior free-form contacts and ellipse tangents are not supported by these tools yet. A bisector takes two intersecting straight lines; click near the desired one of the two preview lines. Parallel or coincident references have no unique angle bisector and are refused.

Each placement is one undo step. Direction, parallel, perpendicular, tangency and bisector relations are kept as ordinary sketch constraints; the reference geometry follows later edits within those constraints. Programmatic callers can use sketch.construction_line with mode, or one of its named variants. near chooses the closest solution; solution chooses its zero-based index. For example:

{"command":"sketch.construction_line.parallel","params":{"curve":"l1","at":[20,10]}}

Polygon tools show their edge outline while positioning the second point, so the orientation can be checked before placement. Inscribed and circumscribed polygons retain their support circle and update the outline when the inline Sides value changes. A polygon drawn By side has a Length box for the side from the first click and the same Sides box; a typed length becomes a dimension of that side, and the other sides stay equal to it.

Text is set in the built-in font (Liberation Sans) unless it is given another: sketch.text and sketch.edit_text take a font, the family or full name of an installed font (“DejaVu Sans”) or the path of a .ttf / .otf file, and the Edit Text dialog has a Font field for it. An unknown font is an error, and the font is kept with the text in the design, so the text stays editable in it. SOLIDCRAFT_FONT_DIRS adds folders to the ones searched for installed fonts. A text’s height, its angle and each coordinate of its at can be expressions of the design’s parameters, given as strings ("height": "plate_height / 10", "at": ["plate_width / 2", 5]): the text keeps the expression and follows the parameters when they change, and saves it with the design. A number takes the place of an expression, and so does moving the text or dragging its handles (what moves becomes a number; turning a text leaves its height as it was). A string that names no parameter is the number it comes to. The same two commands refuse a text that is not a string, an at that is not two numbers or expressions, a height or angle that is neither (or a height that is not positive) and an expression that cannot be worked out with a message, instead of drawing the text with a default; left out, they keep their defaults.

Right-click a letter’s filled interior or outline and choose Edit Text. You can also start Edit Text first and click the letter. All outlines belonging to that text are highlighted while the dialog is open. A profile bounded by multiple text sources or other geometry cannot identify one text: use Select Other to choose its outline instead. Change the text, font, baseline X/Y, height or angle, then confirm with OK; Cancel keeps the original text. Position and height use the document’s units. The four fields take expressions: one that names a parameter (plate_width / 2) stays an expression, is shown as typed when the dialog is opened again, and keeps the text following the parameter.

To work on the letters’ shapes themselves, right-click the text and choose Explode Text (sketch.break_link with the text’s link). Its outlines stay where they are as ordinary lines, arcs and curves that can be picked, trimmed, dimensioned and extruded one by one; the text itself is gone, so it can no longer be edited as text or follow parameters. Undo brings it back.

A text can follow a curve of its sketch (its Path). That curve becomes construction: the letters stand on it and their descenders cross it, so as a normal curve it would cut them into pieces, and picking the text would pick those pieces. Taking the text off the curve leaves it construction; Normal/Construction turns it back.

Drag a text outline to move all its letters together. Let go to finish in one undo step, or press Esc during the drag to restore the original text. The text stays editable. For an exact displacement, select an outline and choose Move/Copy from its context menu. Enter Distance X, Distance Y and an optional Angle about the text’s baseline; lengths accept document units or explicit units such as 1 in. Enable Create Copy to leave the original in place and create a separately editable text. Cancel leaves the design unchanged. Projected geometry remains protected from ordinary dragging.

Project (P) and Intersect bring model edges into the sketch as linked geometry that follows later changes. Sketching on a face projects its edges automatically. A projected edge stays the edge that was picked, wherever a later change moves it; when that edge no longer exists, the projection keeps its last shape and the sketch says it was lost. For Project, select visible source curves or points from another sketch, model edges, faces, vertices or bodies, or axes and planes, then press P. The entire supported selection is projected in one undo step. A rejected projection keeps the selection for correction; an empty or mixed selection starts the ordinary picking tool. External sketch selections retain their source sketch, even when the active sketch uses the same point or curve names.

A sketch on a construction plane built from the model (through a sketch line, through three points, midway between two faces…) projects, intersects, includes and holds text like a sketch on any other plane, and its links follow when a parameter moves that plane.

Run from a script or the control channel, every command that adds sketch geometry returns the ids of what it added (created: {points, curves, constraints, dimensions}). Ids are stable and never reused within a sketch, even after a delete, so a script can dimension or constrain the new geometry right away.

Extend finds intersections with exact ellipses, rational conics and spline spans. It keeps the original line or circular support and extends only beyond the selected endpoint; conflicting constraints or an ambiguous overlap leave the sketch unchanged. To extend two lines or arcs until they meet, hold Shift and click the first curve, then Shift-click the second: both grow to where they cross and are joined there, in one undo step.

Closed convex chains of tangent lines and circular arcs support inward offsets that consume a rounded corner. When the offset passes the corner radius, the neighbouring straight supports meet at a sharp corner. The surviving lines and endpoint IDs stay stable; the retired arcs return under their original IDs if a later edit restores their radius. Whole-boundary profile selections follow this transition through save and reopen, so an extruded lip can remain associative. Existing eligible closed analytic offsets upgrade without changing their saved geometry or IDs.

This transition requires a closed convex boundary with tangent junctions and a certified nonempty region. An extinguished arc referenced individually by a constraint or another offset cannot retire. Creating another offset from an already sharp retired boundary also requires breaking its association first. Open, nonconvex and mixed free-form chains keep their existing behavior. Break Offset Association freezes the current boundary, including private arc centres and construction flags, for independent editing.

Offsets of smooth ellipses, conics and splines follow their source after dimension changes, dragging and reopening a design. Their stored cubic spline has a whole-span normal-distance error bound of 0.002 mm. Singular tangents, curvature cusps, possible self-intersections and excessive subdivisions are refused. Offset these curves individually: mixed chain junctions currently require separate geometry. sketch.offset.break releases an offset for independent editing, and undo restores its association. Older independently sampled offsets remain independent. The fit points of such an offset stay hidden, except the two ends of an open one: lines and arcs snap to them and stay coincident with them as the offset follows its source, so the gap between a spline and its offset can be closed into a profile. Smooth open and newly created smooth closed offsets can be offset again, including bounded refinement after a source edit. New closed outputs use a periodic cubic fit with continuous tangent and curvature at the seam. Break Offset Association preserves that interpolation, and its closing point remains joined when dragged. Deleting an output also removes its unused private fit points; deleting a source with a retained dependent is refused.

For explicit closed fit splines, sketch.spline.fit_point accepts periodic: true and at least three distinct cyclic samples. It closes the loop automatically. Existing calls and older saved closed splines retain natural interpolation; older associative closed offsets also retain their saved natural fit. A legacy seam without a unique normal still refuses another offset. Periodic intent is stored on each curve and survives save, reopen and Break. While drawing a fit spline, click its starting point after three distinct samples to close it smoothly. Right-click finishes an ordinary natural fit. Offset accepts both ellipse handles and dense fitted curves; the second click sets the side and distance from the curve.

Equation Curve draws a curve from formulas instead of clicks: x(t) and y(t) over a range of t, or y = f(x) over a range of x, using the design’s parameters. Equation curves walks through it step by step, with pictures and examples (a line, a parabola, a wave, a circle, a spiral, a gear tooth’s involute). Right-click the curve and choose Edit Equation Curve to change the formulas (sketch.equation_curve and sketch.equation_curve.edit from scripts and agents).

While drawing in a planar sketch, Construction (X) changes the line type without ending the tool or losing picked points and inline values. The preview and hover feedback keep construction dashes and centerline dash-dot patterns visible. With curves selected, Construction changes those curves as before.

The pointer snaps to existing sketch points, to a line’s midpoint, and to where two curves cross (two lines, a line and a circle or arc, or two circles or arcs). The snap hint shows X. The new point stays on both curves when either one moves. Hold Shift to prefer a line midpoint instead; hold Ctrl/Cmd or Alt/Option (the default suppress chord) to stop snapping for that gesture. Scripts pass "x:<curve>+<curve>:x,y" as a point argument, the same way "mid:<line>" and "on:<curve>:x,y" work.

With Preferences › Sketch › Line up with other sketch points while drawing turned on (it’s off by default), the pointer also lines up while drawing with the sketch’s points on screen: line ends, centres, standalone points and the origin, and the shape’s own earlier points. It also lines up with the midpoint of a line you just hovered, whose dotted guide is showing. It lines up on both axes at once, level with one point and plumb with another, so a rectangle drawn beside another can take its height from one and its width from a point below. A line kept horizontal or vertical from its start can still end under or beside another point. A dotted guide runs from each point it lines up with, and the snap hint shows H, V or H+V. The click keeps the alignment: the new point gets a Horizontal or Vertical constraint with each sketch point it lined up with, in the shape’s undo step, unless the sketch already sets it. With the preference off, drawing lines up only with the previous point, one axis at a time.

Enable 3D Sketch in the Sketch Palette to draw lines, fit splines and points off the plane.

Enable Select 3D sketch curve points in Preferences > Sketch to show and select drawn 3D curve points as construction references after finishing the sketch. This option is off by default. For example, choose Offset Plane, select its base plane, then select a spline point in the Through point field. The plane follows that point when the curve is edited. Interior points work as well as endpoints, and references use their full 3D positions. Hole placement continues to use planar sketch points; drawn 3D curve points are not Hole targets. While placing these points, you can snap to points on other visible sketches. For a bridge line, start Line and click an endpoint on each source sketch; each endpoint uses its source plane, even when that plane differs from the active sketch. These snaps capture positions; they do not create associative links to the source points. New 3D geometry follows its sketch’s component placement, including the selected occurrence and pending position changes. Click a drawn fit point to show its move triad; its arrows move along world X, Y and Z. Scripts also supply world coordinates to sketch.line3d, sketch.spline3d, sketch.point3d and sketch.move3d. Activate the desired occurrence before editing an explicit sketch belonging to a component with multiple placements.

Older drawn or detached 3D wires keep their saved world positions. Included and surface-linked wires with an explicit component marker keep that meaning after the link is broken. Old files whose links became lost after an incorrectly scoped cross-component refresh may require a fresh link once the source is available; their stored point values are preserved on opening.

Constraints and dimensions

Horizontal/Vertical accepts a click on a line or clicks on two distinct sketch points, including the origin, endpoints and circle centres. For two points, it chooses horizontal or vertical alignment from their current positions. Hold Shift near a line’s midpoint to highlight and select it as either point. The midpoint follows both endpoints as the line moves, resizes or rotates. Two line midpoints can also be aligned. Hovering and the first pick are only previews; Esc cancels them without changing the sketch. Accepting the second pick reuses or creates a sketch point with an ordinary Midpoint constraint, then adds the H/V constraint in one undo step. Both constraints can be selected and deleted independently; deleting either keeps the point. After a fillet or chamfer, its Midpoint constraint refers to the original full side, retained as construction geometry, just like a manually constrained midpoint.

Scripts use the same command, with a midpoint reference in the points array:

{"command":"sketch.constraint.horizontal_vertical","params":{"points":[{"midpoint":"l1"},"c1.center"],"mode":"vertical"}}

mode can be horizontal or vertical; omitting it chooses from the current positions.

The solver is SolidCraft’s own (damped Gauss–Newton with degree-of-freedom analysis). Constraints: coincident, horizontal/vertical, parallel, perpendicular, tangent, equal, concentric, collinear, midpoint, symmetry, fix, curvature (G2) and polygon. Sketch Dimension (D) adds linear, aligned, horizontal, vertical, point-to-line, line-to-circle, point-to-circle, circle-to-circle, radius, diameter, arc length and angle dimensions. For an arc’s length, click the arc and then one of its end points. An ellipse is drawn with its major and minor axes as construction lines through its centre (Preferences › Sketch › Draw ellipses with their axes; turned off, the ellipse is drawn alone, and D on an ellipse gives its minor radius wherever the value is pulled, as before). As with a Center Rectangle’s diagonals, trim, extend and break stop at them, and they stay when the ellipse is deleted. Click the ellipse and pull the value across its major axis for the major half-axis, or out past an end of the major axis for the minor half-axis, also on an ellipse whose axis lines were deleted; once one half-axis has a dimension, the next click on the ellipse gives the other; click an axis line for the whole axis. Each is a parameter (d1, d2, …), so it can hold an expression.

Concentric accepts two circles or arcs, or a circle or arc and a sketch point (including the origin). Preselect the pair and choose Concentric, or start the tool and pick both, in either order.

Press D, click what you want to measure and type the value: as soon as the click settles the dimension (one line, circle or arc, or the second point or line of a distance or angle), its value box takes the keyboard with the measured value selected, so typing replaces it. Numbers, units (1 in), expressions (2*width) and parameter names all work; letters go into the box, not to tool shortcuts. width = 40 (or #width = 40) defines the user parameter width and dimensions with it, in one step. While you type, the dimension text follows the cursor, and Enter places it there (or in its default place if you haven’t moved the cursor) as one undo step. Esc cancels the dimension.

The first length dimension of a sketch with nothing sized yet scales the drawing it measures to its value, so the shape stays as drawn; separate shapes that nothing joins keep their size. To have the first dimension size only what it measures, turn off The first dimension scales the drawing in Preferences › Sketch.

You can still place the text first. Before you type, a click on empty sketch space puts the text there and keeps the value box focused; a click on another point or line makes the dimension a distance or angle instead; Tab leaves the picks waiting for a click to place the text.

To change a dimension you already have, double-click it, or, with Sketch Dimension on and nothing picked yet, click it once: its value box opens with the value selected, Enter applies, and the tool stays on for the next dimension. The dimension’s text always takes the click; its lines take it only away from sketch points and curves, which still start a new dimension.

To type values in a small window instead, set Preferences › Sketch › Dimension values to In a window. The Dimension tool then places the text first: click what you want to measure, then where the text goes, and the window opens there (double-clicking a dimension opens it too). Its field takes the same numbers, units, expressions and parameter names as the box, and the sketch shows the value as you type. The arrows beside the field, Up and Down, and the mouse wheel over the field step a number by Window steps (1 mm and 1° unless you change them; Shift steps ten times as far, Alt a tenth), keeping the unit it is written in. The check (or Enter) applies the value as one undo step and the tool stays on for the next dimension; the cross (or Esc) drops this dimension only, and Esc with no window open puts the tool down.

To have shapes dimensioned as you draw them, turn on Dimension new shapes in Preferences › Sketch (it is off by default). Each line, rectangle, circle, arc, slot, polygon or ellipse you draw then gets all its dimensions at the sizes you drew: a line its length and angle, a rectangle its width and height, a circle its diameter, an ellipse its major and minor radius, and so on. Values you type in the drawing boxes win, and a size the sketch already sets (the angle of a line that snapped horizontal) is left out. The shape and its dimensions are one undo step. In scripts, sketch.draw.dimensioned takes "as_drawn": true.

Tick Also place them from the origin (fully constrained) as well, and each new shape is also dimensioned from the sketch origin until nothing of it is left free: a horizontal and a vertical distance to its centre or first point, and one more where the shape could still turn (a polygon, a rectangle drawn at an angle). A shape already attached to the origin or to geometry that is placed gets none, and a point on an axis is put on that axis rather than given a 0 distance. In scripts: "locate": true.

Between two points, or on a line, that don’t line up with an axis, where you place the dimension picks what it measures: across from them, the distance between them; above or below them, the horizontal distance; beside them, the vertical distance.

A line and a circle or arc (click the line, then the circle’s edge, or the other way round) measure from the line to the circle’s edge, square to the line: the gap between them, 0 when the circle touches the line. When the line cuts through the circle, the dimension measures how far the edge reaches past the line instead. An arc is measured to the circle it lies on. For the distance to the centre, click the centre point instead of the edge. In scripts: {"command": "sketch.dimension", "params": {"entities": ["l1", "c1"], "value": 5}}.

A point and a circle or arc measure from the point to the circle’s edge, along the radius through the point: 0 puts the point on the circle, and a point inside the circle measures how far within the edge it lies. Two circles or arcs measure between their edges, along the line through their centres: the gap between them (0 when they touch), how far they overlap when they cross, or, when one lies inside the other, the narrowest gap between them (the thinnest part of the wall). For the distance between the centres, click the two centre points instead.

A dimension the sketch can’t take, because the geometry is already fixed by other constraints and dimensions, is added as a driven (reference) dimension instead: it is shown in parentheses, measures the sketch, and has no value to edit.

For Coincident, pick two points, a point and a curve (in either order), or two distinct straight lines. Two lines join their closest pair of endpoints; the first picked line stays in place when the existing constraints allow it. Pick endpoints explicitly when you want to connect a different pair.

Horizontal/Vertical also applies immediately to one selected line or two distinct selected points. Shift-click to add the second point before choosing the constraint.

For Parallel, Perpendicular, Equal, Tangent, Concentric and Collinear, select exactly two sketch curves (Shift-click to add the second) and choose the constraint to apply it immediately. You can also start the constraint first and then pick its curves. An incomplete or mixed selection leaves the tool ready for ordinary picking.

Coincident accepts an exact selection of two points or a point and a curve. Midpoint accepts one point and one line, in either selection order, or two lines: the second line moves (keeping its length) until its midpoint sits on the first line’s midpoint, and stays centred there as either line changes. Symmetry accepts two distinct points and one symmetry line, regardless of where the line occurs in the selection. Successful constraints clear the selection; rejected constraints preserve it.

Under-constrained geometry is blue and fully constrained geometry turns dark, and the status shows the remaining degrees of freedom. A constraint or dimension that would over-constrain the sketch is refused rather than applied. Drag a point to move it; the solver keeps every constraint while you drag.

A sketch’s constraints can still come to conflict through a change elsewhere: a parameter, or an earlier sketch that projected geometry follows. While they do, the curves and points a failing constraint holds, and that constraint’s glyph or dimension, are drawn in the conflict colour (red; the theme key sketch_conflict, which follows a theme’s error colour unless the theme sets it), so the constraint to change or remove is easy to find. Constraints that are only redundant (implied by others, still holding) keep their usual look. Preferences › Sketch › Conflicting geometry in the conflict colour (on by default) turns this off: then only the glyphs and dimensions of the failing constraints are red, as in earlier versions.

Profiles

Closed loops become profiles automatically, including regions split by crossing lines and islands inside outlines. Extrude, revolve, sweep and loft pick profiles.

A feature remembers a picked profile by the curves around it, not by where it was clicked: "profiles": [{"curves": ["l1", "l2", "a1", "l3"]}] (or [{"curves": ["c1"]}] for a circle) picks the region whose outer loop is made of exactly those curves, or the pieces of them that other curves cut off. It is found again on every recompute, so the feature keeps the same region when a parameter moves the geometry. Islands inside the region are its holes. When no region, or more than one (both halves of a circle cut by a line), is bounded by exactly those curves, the feature reports an error rather than taking another region. Profiles picked by index or by point ([{"point": [x, y]}]) are stored the same way when the feature is made.

Measurement units

Change the current design through Document Settings > Units in the browser. The units preference applies to new designs. Existing sketch annotations, live drawing lengths and Measure follow the current design units without resizing geometry. Inches display at least three decimal places and retain a meaningful fourth digit; a higher display precision can be selected in Preferences. Bare length values typed in the dimension tool use the current design units. Explicit units such as 25.4 mm remain valid in an inch design. Angular dimensions keep their angular units. Deliberately typed four-place inch literals retain trailing zeros. Component/canvas distance controls and inspection, parts-library and flat-pattern measurements follow design units too; parameter expressions and metric standard names retain their explicit authored units.

Measure shows the currently selected distance (centre, minimum or maximum) and separate X, Y and Z distances on the canvas. The component dimensions follow the world axes and remain visible until the Measure dialog closes. To hide these extra lines and labels, turn off Preferences > Graphics > Show X/Y/Z distances. The choice is saved between runs. Drag the distance or an X, Y or Z label to move it away from the part; a moved label keeps a line to what it measures. Double-click a label to put it back. Measuring other items starts the labels in their own places again.

Reviewing mirror, pattern, and offset results

Mirror, rectangular/circular Pattern, and Offset show the active selection step in their shared tool window. Click an input to change it; click a selected object again or Ctrl/Cmd-click it to remove it. Each input has its own Clear button. Source inputs show a count, and single references identify the chosen mirror line, direction, or centre. Box selection also works while the window is open. Sketch modify tools take geometry from the sketch being edited.

If your preselection includes exactly one straight construction line of the active sketch, opening Mirror uses it as Mirror line, keeping your selected entities. Other construction lines in the sketch do not count unless selected. With none or multiple selected construction lines, choose the reference manually. You can clear or replace the line. To always choose it manually, turn off Preferences > Sketch > Use the preselected construction line for Mirror.

Proposed sketch geometry appears green before confirmation. Change the inputs or values to update the preview, then press OK. Cancel discards the preview. A circular pattern centre can be an existing point or a position clicked in empty sketch space.

Sketch patterns keep each instance related to the seed geometry: moving the seed updates the copies, and a copy cannot deform independently. Circle and ellipse radii follow the seed too. Choose a circle’s centre point to keep a circular pattern centred on that circle. A centre placed in empty space becomes a sketch point that can be dimensioned or made coincident. Double-click the pattern badge (at the circular centre or beside the rectangular seed), or right-click a seed or copy and choose Edit Sketch Pattern, to change quantity, spacing, direction, total angle or the entities it copies. Confirmed edits preserve surviving instance ids, except that changing the entities rebuilds every copy, so dimensions and relations on the old copies are lost; Cancel discards the preview. Pattern settings survive save/reopen and undo/redo. Deleting a seed, copy or pattern relation breaks that association and leaves the remaining geometry independent. Patterns saved by older versions remain independent copies; recreate them to add associations.

Offset distances are editable driving dimensions: double-click a dimension or change its model parameter to resize the offset without moving its source curves. The selected side, distance, and association survive save/reopen and undo/redo. Symmetric offsets share one driving distance; Two Sides has an independent distance for each side. Disconnected selected curves or chains are all offset. Breaking an association retains its current geometry and removes that association’s driver. Older offsets retain their saved fixed distances.

Equation curves

An equation curve is a curve you describe with a rule instead of drawing it with clicks. You type the rule, and SolidCraft draws the curve that follows it, exactly, every time.

It is the way to get shapes that are hard to draw by hand: a smooth wave, a parabola, a spiral, or the curved side of a gear tooth.

Your first curve: a straight ramp

  1. Start a sketch: SOLID › CREATE › Create Sketch, then click a plane.
  2. Click SKETCH › CREATE › Equation Curve. A window opens with a wave already filled in.
  3. Set Form to Explicit. Now there is one rule to fill in, y(x): how high is the curve at each x?
  4. Type x / 2 in y(x), 0 in Start and 40 in End.
  5. Click OK.

The rule says: the height is half of how far along you are. At x = 10 the curve is 5 high, at x = 40 it is 20 high. Start and End say where along x the curve begins and stops.

Explicit: one rule for the height

With Explicit, you write the height y using x. Try these (same steps, other rules):

Rule in y(x)StartEndWhat you get
x / 2040a straight ramp (above)
x * x / 20-3030a parabola, the shape of a thrown ball’s path
8 * sin(x / 5)020 * pia smooth wave

In the wave, 8 is how tall each bump is and 5 stretches it sideways: try x / 10 and the waves get twice as long.

Parametric: a rule for x and a rule for y

Some curves go back on themselves, like a circle: for one x there are two heights. For those, set Form to Parametric and give two rules, x(t) and y(t).

Think of t as a clock. It starts at Start and ticks up to End. At every moment the two rules say where the pen is: across (x) and up (y). The curve is the path the pen draws while the clock runs.

Rule in x(t)Rule in y(t)StartEndWhat you get
20 * cos(t)20 * sin(t)02 * pia circle of radius 20
2 * t * cos(t)2 * t * sin(t)04 * pia spiral, two turns

For the circle, cos and sin walk the pen around, and 20 is how far from the middle it stays. 2 * pi is one full turn, so the circle closes. In the spiral, the distance from the middle (2 * t) grows while the clock runs, so the pen moves outwards as it goes round.

Angles are in radians

sin, cos and tan count angles in radians, not degrees: a full turn is 2 * pi (about 6.28), half a turn is pi. You can also write an angle with its unit for Start and End, such as 360 deg.

A number without a unit is in your design’s unit (millimetres unless you changed it).

Other functions

Besides sin, cos and tan, a rule can use sqrt, exp, ln, pow, abs, min, max and the hyperbolic functions sinh, cosh and tanh (and their inverses asinh, acosh, atanh), which take a plain number. A hanging chain is 20 * cosh(x / 20) in y(x), and the flare of a horn is often written with cosh and sinh.

Using parameters: a gear tooth

Rules can use the design’s parameters. Here a parameter r (20 mm) is the size of a base circle, and the curve is the involute of that circle: the path the end of a string makes as you unwind it from the circle. The sides of most gear teeth have this shape.

Rule in x(t)Rule in y(t)StartEnd
r * (cos(t) + t * sin(t))r * (sin(t) - t * cos(t))01.5

Change r in the Parameters dialog and the curve redraws itself to match: the rule is kept, not just the shape.

A variable with its range’s unit

Normally the variable is a plain number, even when Start and End are lengths. That stops a rule from mixing it with a length parameter: with a a length, a ^ 2 / (4 * t) comes out as a length squared.

Tick Variable has the range’s unit and the variable is a length, so the rule reads as the maths does:

Rule in x(t)Rule in y(t)StartEnd
ta ^ 2 / (4 * t)a3 * a

Start and End must then both be lengths, or one a length and the other a plain number, which is in your design’s unit (0 to a starts at 0). A length and an angle are refused: they have no unit in common. A rule that needs a plain number divides the variable by a length, such as sin(t / a).

From curve to part

An equation curve is an ordinary sketch curve, so you can build with it:

  • A curve that ends where it starts closes by itself, smoothly. Extrude it and you get a solid. This flower uses a radius that goes in and out six times as the clock goes round once:

    Rule in x(t)Rule in y(t)StartEnd
    (20 + 4 * cos(6 * t)) * cos(t)(20 + 4 * cos(6 * t)) * sin(t)02 * pi

  • An open curve can be closed with lines: draw lines from its two ends, then extrude the shape. For example, close a wave with three lines down to the x axis to get a wavy strip.

  • It can be the path of a sweep.

Changing a curve

Right-click the curve and choose Edit Equation Curve. The same window opens with the rules in it; change them and click OK.

When something goes wrong

  • The curve is tiny or huge. Check the numbers in the rule and the Start/End range: sin never goes past 1, so sin(x) alone is at most 1 mm high. Multiply it (8 * sin(x)).
  • The wave has too few bumps, or far too many. Remember radians: sin(x) repeats every 2 * pi (6.28) along x, not every 360.
  • The circle has a gap. End must be a whole turn: 2 * pi, or 360 deg.
  • A rule is rejected. Use * for multiply (2 * t, not 2t), and close every bracket.

For scripts and agents

The commands are sketch.equation_curve and sketch.equation_curve.edit (see the command reference). The curve is a fit point spline through points worked out from the rules, as many as keep it within the Tolerance (0.01 mm unless you set another), up to 500. Its points are fixed, so other curves can start at its ends. When a parameter a rule uses changes, the points follow. The pictures on this page come from the scripts in docs/book/scripts/equation-*.json.

Solid features

For prerequisites, a readable options table and a first example, open the practical Extrude guide.

Features live on the timeline and rebuild incrementally from the first one that changed. Each one can be edited (double-click), suppressed, renamed, reordered (if nothing it depends on moves after it), rolled back to, and deleted. Deleting asks first when other features depend on it.

Step by step

The sketch from Sketching step by step becomes an open box: extruded, hollowed, its rim rounded, holes added round the tube, then made taller from the timeline. In each dialog, Enter is the same as OK.

1. Extrude. Click Extrude, then the sketch’s profile: a preview appears with an arrow. Drag the arrow, or type the distance (12) and press Enter.

2. Shell. Click Shell, then the face to leave open (the top). Keep the 2 mm wall and press Enter.

3. Fillet. Click Fillet, then the four outer top edges, type the radius (1.5) and press Enter. Rounding after the shell keeps it simple: Shell doesn’t have to follow the rounds.

4. Hole. Click Hole, then the box’s floor beside the tube: the hole goes where you click. Set Diameter to 4 and click OK. Its distance from two edges can be set too (below).

5. Pattern. Open the Arrange panel and choose Circular Pattern. Set Object Type to Features and click Hole1 on the timeline; for Axis, click the tube’s rim. Type 4 for the quantity and press Enter: four holes, evenly round the tube.

6. Change it. Double-click Extrude1 on the timeline: the model rolls back to it and its dialog opens. Type 20 and press Enter. Everything after it (shell, fillet, hole and pattern) rebuilds on the taller block.

The features

PanelFeatures
Createextrude (one side, two sides, symmetric, taper, start offset, to a distance or through all; new body, join, cut, intersect; thin walls along curves), revolve, sweep, loft, box, cylinder, sphere, torus, coil, pipe, rib, web, emboss, hole (simple, counterbore, countersink, tapped; on faces, at distances from edges, or at sketch points), thread, rectangular/circular/path patterns, mirror
Modifypress pull, fillet, chamfer, shell, draft, scale, combine, split body, offset face, replace face, move/copy, align, remove
Constructoffset plane (at a distance or through a point), plane at angle, plane along path
Inspectmeasure, section analysis, interference, compare bodies, curvature comb and map, zebra, draft, minimum radius, isocurves, centre of mass

Quick measurement (off by default; turn it on under Preferences › Interface): with edges, faces, sketch curves or points selected, the view’s bottom right corner, above History, shows what they measure: an edge’s length (Ø and R for a round one), a face’s area and perimeter, a point’s position, two items’ distance and angle, several edges’ or faces’ total. A click copies it. The Measure tool (I) does more.

Extrude, Revolve, Sweep, Loft, Coil and the Box, Cylinder, Sphere and Torus choose their operation from what their material does, until you pick one: Join when it makes one body with a body it touches (a block against a wall, a ring round a hole, a boss meeting only part of a face), New Body when it would be a separate piece, and Cut when it goes into a body (Extrude) or lies inside one (Revolve and the Box, Cylinder, Sphere and Torus). The window finds out by running the Join on a copy of the design in the background, so its choice is what OK makes; it changes only when that check is in, and OK waits for it. An operation you pick stays, and a feature you edit keeps its own. Pipe, Thicken and Boundary Fill start as a New Body. Only shown bodies take part: with some bodies hidden, the feature names the shown ones as its targets, so a hidden body is never joined or cut, and showing it again later changes nothing. Choose the operation automatically (Preferences › Design, on by default) turns this choice off: each window then keeps the operation it opens with, Extrude its own Join or Cut, as before.

A Join you pick whose material touches none of its target bodies still joins it, to the first one as a separate piece, and the feature’s warning says so: “Body1 is now in 2 separate pieces”. Combine joins the bodies it is given without that warning. Changing the default does not alter operations already saved in a design.

Extrude to a picked target (Preferences › Tools, on by default) lets a pick choose the extent. With it on and a profile selected in the solid Extrude dialog, picking a face away from its plane, a curved face, an off-plane vertex or a parallel plane automatically switches Distance to To Object. Coplanar faces still join the profile selection; non-parallel planes are not targets. Clicking into the Profiles box first makes the next picks profiles again, even off the plane. The extent can still be chosen manually. For a new extrusion without start or end offsets or a From Object input, the target also sets the stored distance and one-sided direction, so returning to Distance keeps that reach. Thin Extrude keeps its existing picks. With the setting off, picks only add profiles.

Dragging Extrude’s distance arrow stops at flat faces parallel to the profile that lie over it, such as the far side of the block a cut goes into: once the arrow’s tip comes within a few screen points of such a face, the distance becomes exactly that face’s and the face is outlined. Elsewhere the distance moves in round steps. Hold Alt or Ctrl for a free drag, which stops at neither. Extrude arrow stops at faces (Preferences › Tools, on by default) turns the face stops off; the arrow then moves in round steps only, as before. The distance stays a number; to follow the face when the model changes, use To Object or Through All.

A positive extrude taper slopes the side walls outward, so the profile grows along the extrusion direction and the far end is larger; a negative taper slopes them inward, making the far end smaller. For example, a 10 × 10 mm square extruded 10 mm with taper: "5 deg" has an approximately 11.75 × 11.75 mm far face; with taper: "-5 deg", it is approximately 8.25 × 8.25 mm.

A taper slopes the sides of any profile, sketch text included: lines, arcs and spline or conic curves move in or out together, and a flat tip shorter than the taper closes to a point. On spline and conic sides the slope is approximate: the moved outline is a curve of the same kind within 2 % of the taper’s offset (or 10 µm) of the true one, a curve that turns too much for that being cut into pieces first; where a curve bends tighter than the offset, the offset folds and is smoothed over. A taper steeper than the profile’s details (a stroke, a curve or a gap narrower than it) is refused with that reason; a smaller taper or distance builds. A taper composes with a start offset (a drafted pocket that starts part way up a part): the offset moves the profile along the normal and the sides are drafted from there, so the profile is the section at the start whatever the offset. A taper with a two-sided or symmetric extent is not built yet and says so.

Thin Extrude (thin: true on solid.extrude, the Thin Extrude box in its dialog) builds a wall of constant thickness along sketch curves instead of filling profiles: open chains (a single wall) and closed loops (a ring), lines, arcs, circles, ellipses and splines. In the dialog, a picked profile stands for the curves around it (its outline and its holes). The wall lies on the curves’ centre, inside, outside, or on both sides with two thicknesses (side: center, inside, outside, two_sides with thickness2). A closed loop’s inside is the area it encloses; an open chain’s is the side it bends toward (the inside of a U), and a straight line’s is the left of its first curve as drawn. Curves that meet end to end are one chain whose corners are mitred; walls that cross or meet (a ring and a cross, a honeycomb) become one piece. Every extent, operation and target of an extrude applies, and a taper for walls of straight lines or of whole circles. A wall thicker than the radius of a curve it follows on the inside is refused. Spline walls follow the exact offset within 0.0001 mm.

Editing an extrusion retains its signed distances, From/To Object inputs, taper expressions and explicit body targets. Revolve edits retain world, sketch, explicit-line and model-edge or face axes, both angle expressions and To Object targets. An unchanged All profiles selection continues to include profiles added later. Cancel restores the complete timeline; a successful edit is one undo step, including downstream parameter changes. Editing a placed component previews the edited feature before its downstream history. Cylindrical Emboss profile guides follow the sketch’s chosen occurrence frame.

Newly selected adaptive profiles store complete outer and hole source identities when the entire boundary can be qualified. Changing an ellipse or periodic offset after extrusion can then change its spline span count while keeping the same selected material region. A partial, intersected or ambiguous boundary cannot satisfy that whole-source reference; changing the hole identities also makes it lost. Older curve-span selections retain their exact stored multisets. If an older feature has already lost its profile after subdivision changed, edit the feature and reselect the intended current region to capture the new identity.

New symmetric solid extrusions treat the entered distance as the total thickness and split it evenly around the start plane: 10 mm spans -5 to +5 mm on an XY sketch. Switching from One Side to Symmetric keeps the entered total thickness. Through All still reaches targets in both directions, and Two Sides retains its independent distances. Symmetric extrusions saved before this change keep their original per-side distances when reopened or edited. Selecting another direction in the feature editor uses the current total-distance convention.

New symmetric revolves split the entered angle evenly around the sketch profile, regardless of which way the selected axis points. Two Sides measures the second angle backward and the first angle forward along the sketch-normal sweep direction. Profiles on opposite sides of an axis each use their own sweep direction. Designs saved before format 11 keep their original angular placement when opened, edited or copied; creating another revolve uses the new behavior. To Object follows that same direction for each selected profile, including curved target faces. Pick a target away from the axis so its angular position is defined.

For cylindrical Emboss, select a supported exterior cylinder face and a tangent sketch profile. Deboss cuts radially into the wall. Orthogonal line profiles, including holes and concave outlines, wrap with exact radial walls; the profile must stay clear of seams and ends. Complete cylinders and coaxial sleeves are supported. Capture pending component positions before applying. The feature stores the selected target and author occurrence paths, so edits and reopening retain the chosen instances. Other curved surfaces and free-form profiles remain unsupported. A new Emboss without an explicit face infers a unique eligible supported cylinder; select the face when the target is ambiguous. Older saved features without a face retain their planar placement.

A Hole with Single placement can also sit at distances from up to two straight edges of its face. After picking the face, click Reference 1 and pick an edge, then pick a second one into Reference 2. Each edge gets a Distance row, measured from where you picked; the canvas shows each one as a dimension from the edge to the hole’s centre, with a box beside it. Type a distance in either place and the hole moves so its centre is that far from the edge, measured in the face’s plane. Moving the hole measures the distances again. The hole keeps its distances when the part changes: it follows its edges, and the distances are parameters like its diameter.

A Cylinder placed on a planar face takes reference edges the same way. Select the face, then start Cylinder: it stands where you clicked, and the window has Reference 1 and Reference 2. Pick one or two straight edges of that face; each gets a Distance, measured from where you clicked, which you can also type in the window or in its box on the canvas. The cylinder’s base centre then follows the edges when the part changes, and the distances are parameters like its radius. Edit Feature picks the edges again.

A Hole with Single placement drills where you pick a planar face. A pick on or near a point of a shown sketch that lies on that face, such as a circle’s centre, places the hole exactly at that point, as Multiple placement picks it; Select Other (press and hold) also offers the face where you clicked. The corners of text outlines don’t count, nor do projected points while the sketch hides projected geometry. The hole keeps that place on the face; to have holes follow later edits of the sketch, place them with Multiple.

Two settings under Create Hole in Preferences › Design, both off by default, change how a Single hole is placed:

  • Snap to face and edge centres: the face itself offers snap points too: its middle, and the centre of each round edge on it (a boss standing on it, a rounded corner, a hole already in it). While the cursor is over the face they show as small marks; a pick near one, or on or beside one of those round edges, places the hole at that centre, and the mark it takes is named. Hold Alt or Ctrl to place the hole exactly where you click.
  • Move holes by clicking or dragging: each placed hole has a handle at its centre. Drag it to move the hole (it snaps when snapping is on), or click it to take the hole out. A pick elsewhere on a face that already has a hole moves the hole there instead of taking it out. Edit Feature shows the handle as well, so a hole can be moved later.

The Hole dialog draws a section through the hole as its options make it (type, extents, drill point, tapped) and puts each size in a field beside the dimension it sets; hovering a field, or typing in it, lights its dimension.

Hole Tap Type is Simple (type the diameter), Clearance (a hole a bolt passes through) or Tapped (a thread). With Clearance, pick the bolt Size (M1.6 to M64) and the Fit (Close, Normal or Loose): the hole takes the ISO 273 clearance diameter, shown in Clearance diameter as a default you can change, the way Tap drill diameter works for a tapped hole; the section shows the bolt’s shank through the hole. Scripts use clearance: "M4" with fit: "normal" on solid.hole. The hole stores its diameter, not the bolt: Edit Feature opens it as a Simple hole of that diameter.

With Hole Type Counterbore and Clearance, Screw head sizes the counterbore for a cap screw: a socket head (ISO 4762) or a button head (ISO 7380-1) of the bolt’s size. It fills Counterbore Ø and Counterbore depth with the head’s diameter and height plus 0.5 mm each side and below; change either to use your own. Scripts use head: "ISO 4762" with clearance on solid.hole.

The Thread feature’s Hand is Right (the default), Left, or Both (a right- and a left-handed groove crossing, as on a reversing screw). Scripts use hand: "left" (or "right", "both") on solid.thread; omitted, a thread is right-handed. Tapped holes are right-handed.

Fillets and chamfers handle straight edges between planar faces, whole smooth loops, runs of neighbouring edges with mitred or spherical corners, and closed chains of curved edges (a rolling ball). See the roadmap’s known limitations for the rest.

Curvature continuous (G2) in the Fillet dialog’s Type blends straight edges between two planar faces with an exact quintic section. The tangent plane and curvature match each side face: curvature goes to zero at both contact rails, unlike a circular G1 fillet. Each end must meet one planar face; perpendicular and sloped planar ends are supported. Curved edges/faces, multi-face ends, and intersecting corner blends are not supported; they return an error, not a G1 fallback. Equivalent Radius sets the same contact setback as a circular fillet (for a right angle, setback equals the entered value); it is not a constant curvature radius. Reduce/Taper to Fit are unavailable for this type.

Scripts and MCP use the same command, for example on a 20 mm box:

{"command": "solid.fillet", "params": {"edges": [[20, 0, 10]], "radius": 2, "type": "curvature"}}

type: "g2" is an alias. Existing fillets still default to circular, constant-radius G1 blends.

Where three or more rounded edges meet at a corner, the Fillet dialog shows Corners: Ball (the default, a piece of the rolling ball’s sphere, tangent (G1) to each round) or Smooth patch (corner: "g2": the corner is set back along each edge and filled with a curvature-continuous (G2) patch, so reflections run through it without a kink). A smooth patch takes Constant radius and Exact fit. Its Setback is how far along each edge from the corner the patch starts, one distance for every corner (setbacks); left empty, it is 1.5 × the radius.

Feature references to faces and edges are stored by name, so an upstream edit that moves geometry keeps them on the right face (see Persistent naming). When a reference can’t be found, the feature warns instead of silently picking another face.

Plane Along Path accepts one sketch curve (including a drawn 3D wire) or model edge. Pick the path in the viewport and enter Position (0 to 1): 0 starts the path, 1 ends it, and 0.5 places the plane halfway along its length. The plane is perpendicular to the path’s local segment. Preview does not change the design; OK creates one editable timeline feature. The plane follows later path edits. Curved paths use their sampled polyline; absolute distance, reference-object placement and multi-curve chains are not yet available in this dialog.

Comparing two bodies

Compare Bodies (inspect.compare) lays two versions of one part over each other, such as a part and the same part exported by another program, and finds the places where only one of them has material. It works from the two surfaces, not from a boolean of the bodies, so it also compares bodies that share almost every face, the case a boolean (Combine, Interference) handles worst.

{"command": "inspect.compare", "params": {"a": "Bracket", "b": "Bracket (2)", "tolerance": 0.01, "color": true}}

Without a and b it compares the two selected bodies, or the design’s only two. Both bodies are measured (volume, area, centre of mass and box, and difference: b − a of each). Each surface is sampled at the corners of its triangles and at points at most spacing (0.5 mm) apart on larger faces, and every sample measures its distance to the other surface: deviation gives the largest and the mean distance each way. Samples further than tolerance (0.01 mm) form the regions: only_in_a where a has material and b has none, only_in_b the other way round. Each region gives its volume, the deviating surface area, the largest deviation and where it is, its centre and box, and the faces of each body it lies on (faces_a, faces_b, numbered as model.faces lists them), largest volume first. The volume, centre and box are exact (measured_by: "boolean"): the pieces of the cut a − b (or b − a) that lie in the region, measured as inspect.measure measures a body. When the cut cannot be made, the region’s volume and centre are not measured (measured_by: "not measured", null values, not_measured gives the cut’s error), and never estimated; its deviation, area, faces and box are still given, and the side’s total is null. identical is true when no region is found, and verdict says it in one line. warnings lists what makes the result less certain: a body whose surface does not enclose the volume it measures (open, or with faces that failed to import), and a cut that could not be made. With color, the faces that hold a region turn red where only a has material and green where only b has, on both bodies: the faces mostly beyond the tolerance (a small change on a large face leaves the face alone), or, for a region with none, the face with the largest share. That is one undo step; without color the command changes nothing.

The bodies are meshed within a quarter of the tolerance. The kernel meshes no finer than a millionth of a body’s size, so keep the tolerance at least twice that (0.02 mm for a part 10 m across). Large bodies are sampled more coarsely, at about max_points (eight million) points each; sampling.spacing_mm says what was used, and a larger max_points (up to 50 million) samples them finer, at the cost of time.

When editing an Extrude in the UI, the Browser’s bodies, sketches, construction objects and feature rows follow the history marker before that feature. Later objects return when editing ends. Rolling back manually keeps feature rows listed, so they remain available to edit. A hidden body supplying the profile’s face is shown temporarily during the edit, including when a folder or component hides it. OK, Cancel and Escape end that display override; the body’s saved visibility and unrelated visibility changes are preserved.

Editing a feature from a script

For solid.pattern.circular, axis accepts X, Y, Z, {origin, dir}, or an edge or round face of the model, {edge: [x, y, z]} or {face: [x, y, z]} (a point on it). An explicit origin must contain three finite numeric coordinates in millimetres ([x, y, z] or {x, y, z}); omitting it uses the world origin. Expressions and construction-axis references are not supported. Invalid origins are rejected rather than silently replaced with zero.

Axes and points on round edges and faces

Wherever a 3D command takes an axis (Revolve, Surface Revolve, Circular Pattern, Plane at Angle, and Move’s Rotate) you can pick a circular edge, which gives the axis through its centre along its normal, or a cylinder, cone or torus face, which gives that surface’s axis. Hovering one in an axis input draws the axis as a dashed construction line, and the line stays drawn once the axis is picked. Flat faces and edges that are neither straight nor circular can’t be picked there.

Revolve, Surface Revolve, Circular Pattern and Plane at Angle keep the picked edge or face as a reference ({edge: [x, y, z]} or {face: [x, y, z]} from a script), found again by its persistent name every time the design is evaluated. If a parameter moves the cylinder, the feature turns about it where it is now. The axis always points the same way, with its largest component positive, so a rebuild that reverses the edge doesn’t flip the turn. Straight edges picked as axes keep working as before, as a fixed line.

Where a 3D command takes a point (the construction points, axes and planes built from points, Revolve and Extrude’s To Object, a surface patch’s pass-through points, and Move’s pivot and points), a circular edge stands for its centre. A hovered circle shows a small star at its centre. A construction point keeps the edge, so it follows the circle’s centre as the model changes.

timeline.inspect {feature} returns a feature’s definition: the fields timeline.edit changes, with their current values. timeline.edit {feature, set} merges set into that definition (objects are merged field by field, anything else replaces the value), rebuilds the feature and reports recomputed and restored. The feature’s type cannot change, and sketch geometry is edited with the sketch commands. Expressions are text ("30", "30 mm", "width / 2", "45 deg"); a bare number is a length in the design’s units, or degrees for an angle.

{"command": "timeline.inspect", "params": {"feature": "Slab"}}
→ {"type": "extrude", "type_name": "ExtrudeFeature",
   "definition": {"sketch": 1, "profiles": {"type": "all"}, "operation": "new_body",
                  "extent": {"distance": "10", "direction": "positive"}},
   "inputs": [{"label": "Distance", "param": "d1", "expression": "10", "unit": "mm"}], …}
{"command": "timeline.edit", "params": {"feature": "Slab", "set": {"extent": {"distance": "12"}}}}

inputs lists the feature’s named inputs. parameters.change {name: "d1", expression} sets one of those directly. Optional fields that are not set do not appear in the definition; they can still be added through set. The fields of the common features:

FeatureFields
Extrudeextent: {distance, direction: positive|negative|symmetric, distance2, start_offset, through_all, taper, to, to_offset, from}; operation: new_body|join|cut|intersect; targets; profiles; sketch; thin: {curves, thickness, side: center|inside|outside|two_sides, thickness2}
Revolveangle, angle2, symmetric, axis, to, operation, targets, profiles, sketch
Filletradius, edges (a point on each edge), body, style
Chamferdistance, distance2 or angle, flip, edges, body
Boxlength, width, height, corner, operation
Cylinderradius, height, base, axis, operation, references (an edge point and a distance each, as a hole’s)
Holediameter, depth, position, direction, hole (simple, drilled, counterbore, countersink), thread, to, references (an edge point and a distance each)
Shellthickness, faces, direction, body, tangent_chain
Draftangle, faces, neutral, pull, body
Offset facedistance, faces, body
Patternpattern (its kind and counts/spacing), features, bodies
Mirrorplane, features, bodies, combine

For any other feature, timeline.inspect shows the fields.

Where a command takes a face as a point (face.delete, solid.shell, solid.draft, solid.offset_face, solid.split_face, the faces of solid.replace_face, and the face of sheet.convert), a script can give the face’s name instead, as model.faces lists it: {"command": "face.delete", "params": {"faces": ["F2:side#1", "F2:side#2"], "body": "Plate"}}. model.faces also lists a point on each face that picks it (the centroid of a face with a hole in it is in the hole, that of a half cylinder inside the cylinder) and, for a cylindrical face, its cylinder: origin and axis of its axis, and radius. A name that is more than one face (the pieces of a split face, or one face of several bodies) is refused; body says which body to look on.

Press Delete on anything selected (bodies, faces, sketches, features, planes, components, canvases). It runs as one undoable step and lists the features that would go with it or fail.

Face Extrude, face Revolve, and Press Pull retain linked generated profiles. When a modified planar face has no construction certificate, these internal profiles use the legacy named-face attachment and projection behavior so upstream edits still update the plane and outline. This compatibility path is private to those modeling tools: newly attached user sketches require a certified face or an explicitly fixed plane. Legacy internal attachments can follow the nearest matching face after topology changes; inspect their result after major upstream edits. Activate the intended occurrence before generating a face profile from a repeated component; capture a separately moved source position before creating a legacy profile across components.

Negative Press Pull across supported aligned prismatic faces retains round through-holes, including after a joined extrusion and in a rotated component occurrence. The cut partitions the current material into axial sections, preserving the hole through undo, redo, and reopen. Walls that cannot be qualified as a parallel extrusion still use the general Boolean path and may report an unsupported coincident-surface operation atomically.

Parameters and expressions

Every dimension and feature input is a model parameter (d1, d2, …). User parameters are ones you add yourself, with a unit, an expression and an optional comment. Change them in Change Parameters (parameters.change). The timeline rebuilds from the first feature that uses them.

When creating or editing a sketch dimension, its value editor shows its model parameter name (d1, d2, …). Double-click another dimension to edit it: the current value is applied, as Enter does (if it is not valid, the box stays open with its error), and the clicked dimension opens with its value selected. Single-click another driving dimension to insert its name: when the value is just a number (such as 10 or 10 mm) the name replaces it; otherwise it goes in at the cursor, replacing selected text and keeping what you typed, so typing 10 + and clicking d1 gives 10 + d1. The name goes in a moment after the click, once it is clear the click was not the first of a double-click. Add arithmetic such as / 2 to build a dependent dimension. Press Enter to apply the expression or Escape to cancel the edit. Add and edit user parameters through the existing Change Parameters command.

The Parameters dialog, numbered 1 to 5 The Parameters dialog, numbered 1 to 5

1 Apply each change as you leave its field · 2 User parameters, and Add parameter · 3 Model parameters, grouped by the sketch or feature they belong to · 4 An expression · 5 The value it works out to

Naming a value as you type it

Any value input can name its value: type width = 40 (or #width = 40) for a dimension, a value box while drawing a shape (a line’s length, a rectangle’s width and height, a circle’s diameter: the shape is drawn at that size and its dimension uses the parameter), an extrude distance, a fillet radius, a hole diameter, a sheet metal rule, a joint’s offset and so on. SolidCraft adds the user parameter width with the expression 40, and the input uses width from then on, so editing it again shows width. A bare number reads as it would in that input (the design’s units for a length, degrees for an angle), and the parameter keeps that unit. The right-hand side can be any expression, such as width = depth * 2. The command and its parameter are one undo step.

Editing a value that already has its own parameter (a dimension’s d7, a feature input’s) with height = 12 renames that parameter to height, references following, and sets it to 12; no other parameter is made. When the value is just a parameter’s name (width), width = 50 sets that parameter instead. A name that is already taken, a unit, function or constant name, or a value that refers to itself is refused with the reason, and nothing changes. A parameter’s own expression and a configuration cell can’t define another parameter.

Scripts, the control channel and MCP tools take the same form in any expression parameter ("distance": "depth = 15"); the result names what it made in defined (the first name) and definitions ([{name, expression, unit}]).

In the MANAGE tab’s PARAMETERS section, click Parameters to open the user and model parameter editor. The Configurations button in CONFIGURATIONS opens the configuration table; Configure remains in the dropdown for configuring columns.

The Filter box lists the parameters whose name, expression, comment or group contains what you type, in any case. It also finds a name by the starts of its words, in order: toodia, toofla and outdia all find tooth_outward_flange_diameter. Underscores, hyphens and camelCase (boltHoleDepth) separate the words. Autocomplete offers parameters the same way.

Press Enter in the new parameter’s name, expression or comment field to add it. Valid entries return focus to the name field for the next parameter; invalid entries remain editable. In expression fields, type a parameter or dimension name for autocomplete, or press Ctrl+Space to open suggestions, including in an empty field. Up/Down selects an autocomplete suggestion, Enter/Tab accepts it, and Escape dismisses the suggestions. Accepting a suggestion does not also submit the parameter. When what you typed is already a complete name (width), longer names such as width_extra stay listed without a selection: Enter applies what you typed, and Up/Down picks one of them first. A unit right after a number (10 mm) is left alone; anywhere else a unit is a prefix like any other, so rad followed by Enter gives radius.

With Preferences › Design › Step values with the arrow keys and the wheel on (it is off by default) and no suggestions open, Up/Down step a plain number in any value field (a dialog’s rows, the value box beside the geometry, a sketch dimension, a parameter’s expression) by 1 in the unit it is written in: 20 mm becomes 21 mm, 2,5 mm becomes 3,5 mm. Hold Shift for 10 or Alt/Option for 0.1. The mouse wheel over the focused field does the same, a step a notch, and the preview follows each step. An expression such as width / 2 is left as typed.

Expressions are unit-aware: 2 * width + 5 mm, angle / 2, sqrt(area), 1 in + 3 mm, using + - * / ^ %, parentheses, sin cos tan asin acos atan atan2 sinh cosh tanh asinh acosh atanh sqrt abs sign min max floor ceil round trunc mod exp ln log log10 pow hypot (log is base 10) and unit conversion. A number and the unit after it are one value: 10 mm / 2 mm is 5 and 90 deg / 1 rad is π/2, and a fraction of numbers before a unit is one value too (1/2 in is half an inch; after a / only written without spaces: 1 in / 1/2 in is 2, while width / 10 / 2 mm divides by 10 and then by 2 mm). A decimal comma works too: 2,5 mm is 2.5 mm, and -(width-0,5)/2 is -(width-0.5)/2 (directly in a function’s parentheses the comma separates its arguments, so write max(2.5, 3) there). A mixed number is one value, as imperial sizes are written: 2 1/8" and 3 7/16 in are 2.125 in and 3.4375 in (a whole number, a space, then a proper fraction of whole numbers). A length where an angle is expected (or the other way round) is an error, not a silent conversion. Cycles are reported with their path (a → b → a), renaming a parameter updates every expression, dimension and feature input that uses it, and a parameter in use can’t be deleted (the error lists its users).

The design’s units (Document Settings in the browser: mm, cm, m, in or ft) set how a bare number or unitless arithmetic expression typed into a length field is read. In an inch design 0.25 and 1 / 4 mean 0.25 in. A new user parameter given no unit (parameters.add or parameters.import without unit) is a length in the design’s units, as the Parameters dialog makes it: 40/2 is 20 in. Operation dialogs start with lengths in the design’s units, including secondary distances, wall thicknesses, gaps, offsets and radii. One is the exception: a new Solid Fillet’s radius opens at the 2 mm the tool ships with, shown in the design’s units (“0.07874 in” in an inch design), because a radius is bound by the faces beside its edges. Canvas handles and live sketch values use the same units as the operation. Angles, counts and scale factors keep their own units. Preferences selects the default units for new designs; existing designs use their own Document Settings.

Explicit units override that choice: 2 mm is still two millimetres in an inch design. Parameter expressions such as width / 2 remain driving expressions. Editing a saved feature preserves its physical size; an existing millimetre expression can still show mm so accepting an unchanged field cannot reinterpret it. Computed sizes and tolerances keep their full precision when written back into editable fields, independently of display precision.

Parameters export to and import from CSV or JSON (parameters.export, parameters.import). In a CSV, a value that a spreadsheet would run as a formula is written with a ' before it, so the spreadsheet shows it as text. That is a value that starts with =, +, - or @, their full-width forms, a tab or a line break, after any spaces and '; a plain number such as -5 is left as it is. The import takes the ' off again. A spreadsheet that saves the file again as CSV can drop the ' (Excel does), so export again from SolidCraft instead of passing that file on. Earlier versions of SolidCraft wrote every value unchanged, so in a CSV from one of them, a value whose first character really was a ' typed before a formula character ('=x) now imports without it (=x). Configurations (config.*) keep a table of rows, each with its own parameter values and suppressed features, physical materials and body appearances, switched with config.activate. The table’s Library picker selects material or appearance values. Choose Follow material to clear an appearance override. Custom looks retain their colour and opacity across activation, undo and save/reopen. See configuration commands and examples.

Checking a design across a parameter’s range

Parameter Sweep (inspect.parameter_sweep) rebuilds a copy of the design at each of a parameter’s values and reports what no longer holds, so a design meant to be driven by its parameters (one template, many sizes) can be checked before it is trusted. The design itself is not changed.

{"command": "inspect.parameter_sweep", "params": {"parameter": "Width", "from": 300, "to": 510, "steps": 21}}

values lists the values instead (numbers or expressions). For each value it gives the rebuild’s time and what changed from the design as it is now: features that fail (failed) or warn (warned), joints whose origin is no longer found (joints_lost) or turned over on its own part (joints_flipped, more than 90°), sketch planes that turned over (sketches_flipped) and sketch solves that fail or only hold by flipping a triangle’s turn or an offset’s side (sketches_failed). summary counts the values that show each.

Use Compare Parameter Variants when several parameters must change together. Each named variant starts from the same open design, so values from one variant never carry over to the next. For example, compare a pair of bracket sizes:

{"command": "inspect.parameter_variants", "params": {"variants": [
  {"name": "Compact", "set": {"Width": "40 mm", "Thickness": "4 mm"}},
  {"name": "Wide", "set": {"Width": "60 mm", "Thickness": "6 mm"}}
]}}

The command accepts up to 12 variants with up to 16 existing parameters each. It returns the baseline’s volume, mass and body counts, then one result per variant in input order. The result reports new feature, joint or sketch regressions using the same checks as Parameter Sweep. A refused expression is an error for that variant; later variants still run. A variant with a rebuild regression has no metrics. Open bodies do not count toward volume or mass. The design and its undo history are unchanged.

Components and joints

A design is a tree of components. New Component (component.create) adds one, and Create Components from Bodies (component.from_bodies) turns bodies into components. Each component has its own origin, bodies, sketches and timeline entries. The active component (the radio button in the browser) receives new sketches and features; the others are drawn faded.

Sketches drawn on a part’s face while the design was active belong to the design, so they collect in its Sketches folder. Move Sketches to Their Parts (component.move_sketches.to_parts), on a Sketches folder’s menu and on a sketch’s menu, moves the folder’s sketches (or the selected ones) to the part whose face each is drawn on, as dragging it onto the part would. Each stays where it is on its face, and the whole move is one undo step. A sketch with no single part to go to stays where it is: one on an origin, construction or fixed plane, on a body of the design itself, on a face without an exact attachment, or already in its part. The status bar says how many moved and why the others stayed; the command’s reply lists each sketch. With no sketches it looks at every sketch in the design.

Step by step

A two-leaf hinge: each leaf is a plate with a barrel along one edge, modelled as a body.

1. Make components. Right-click the Bodies folder in the browser and choose Create Components from Bodies. Each body becomes a component of its own. The first is grounded (the pin beside it): it stays where it is while the others move.

2. Join them. On the ASSEMBLE tab, click Joint. Click the end of Leaf B’s barrel, then the same end of Leaf A’s: Leaf B moves so the two circles meet. Choose Revolute for a hinge. If the part lands the wrong way round, tick Flip. Click OK.

3. Move it. Drag Leaf B: it turns about the hinge and nothing else. Leaf A is grounded, so it doesn’t move.

4. Drive it. Drive Joints turns a joint through its values: drag the slider, or type the angle. A ghost shows where the part started. OK keeps the new angle.

Components, placement and joints

A component is placed by its occurrences. Paste creates another instance of the same component, and Paste New creates an independent copy. Move an occurrence with the move triad, then Capture Position to keep it or Revert to put it back. Capture Position also saves every joint’s current values, and Revert Position puts the joints back to them, clamped to their limits, together with the parts, as one undo step. SolidCraft asks before the next command if a move wasn’t captured. Ground fixes an occurrence in place, and Unground (the same row of Placement in the browser’s context menu) frees it again.

The design’s first part is grounded when it is made: the first occurrence placed directly in the design by New Component, Create Components from Bodies or an import. Parts made after it, standard parts and copies made with Paste or Paste New start free, so do parts made inside other components. Any number of parts may be grounded, or none; opening a design never grounds anything. A grounded part refuses Move until it is ungrounded, and component.create takes grounded: true or false to choose for itself.

Drag a part to put it somewhere else. With the design active, press the left button on a part and drag: the part follows the pointer, the point you grabbed moving in the plane through it that faces the view, and stays where you let go, with its position captured. The whole drag is one undo step, and Escape during the drag puts the part back. The drag moves the placement of the active component that holds the part, so with the design active a part inside a subassembly moves the whole subassembly; activate the subassembly to move the part inside it. Moving a definition shared by several placements moves it in all of them, as Move does. A grounded part does not move, and a part that joints hold to something grounded moves only as far as its joints allow (below). Dragging any member of a chain joined to nothing grounded moves the whole chain, preserving its joint values. Shared nested definitions move in every instance; the selected instance follows the pointer even when its parent is rotated differently from the first one. A drag that starts on empty space, on a part that can’t move, or with Shift, Ctrl (Cmd) or Alt held draws a selection box instead. Other uncaptured moves stay pending. Scripts and agents do the same with occurrence.drag {occurrence or path, at, point}, which moves the placement by point - at in world coordinates and answers the placements it moved.

Move along the joint’s free axes. Select a face or a single assembly part, then choose Assemble → Move, or right-click the selected face and choose Move. Thick straight arrows move along the allowed axes; circular arrows turn about them. Grounded parts, rigid joints, fixed coordinates and coordinates controlled by parameter expressions have no handle. The handles appear only after Move is started and disappear when Measure or another command starts. A face selected for measurement does not open Move. If the selected part has no free joint value, Move shows a warning instead of handles. Check rigid joints, grounding, joint limits and parameter-driven values; OK or Escape dismisses the warning without changing those constraints.

While dragging, a readout shows the actual distance or angle from the drag’s starting pose, including when a joint limit or contact stops the part. Distances use the design’s display units. Rotation uses 10° steps near the ring; pull the pointer outside the dashed guide to use 0.1° steps. Crossing the guide changes precision without jumping to another angle. Change both step sizes under Preferences → Design → Assembly Move. Each drag is one undo step; Escape while dragging restores its starting pose. Done (or Escape when idle) closes the handles.

To use the previous Move panel, turn off Use joint motion handles for assembly Move in the same settings section.

Place a part from parameters. occurrence.move takes its translate as numbers or as length expressions over the design’s parameters ("translate": ["0 mm", "tube_front_y", 0]), as solid.move does for bodies. An occurrence moved by expressions that name a parameter keeps them (expressed_moves in component.list) and, whenever such a parameter changes, moves on by the difference, along the axes the move was given in (space: local or world), so an imported STEP part placed at a station follows the station. Other moves of it (a drag, another move) stay, and undo, saving and opening keep the expressions. Renaming a parameter carries into them, and deleting one they use is refused. A move by expressions is captured at once (capture: false is refused with them). If an expression stops working out (say a parameter becomes an angle), the part stays where it is until it works out again. The rotation (axis, angle, origin) and a matrix are still worked out once, when the move is made.

Paste New copies nested component definitions, sketches, feature histories and generated dimension parameters. Repeated child instances remain shared within the new copy. Materials, appearances and wholly internal joints/motion records receive copied references. Design-wide user parameters remain shared; retained external links and omitted cross-boundary motion records are reported. Unsupported destructive links refuse the entire copy. See individual-instance and copy details.

Extract to File (component.extract, under Component in a component’s right-click menu) moves a component into a design of its own: its sketches, features, bodies, subcomponents and the joints among them go to the new .sc file at its origin, with the parameters they use, and the component becomes linked to that file, as Insert Design makes it. Its placements and the joints to the rest of the assembly stay. Components whose own history builds the same bodies are offered to become placements of the new link too (component.copies, component.replace_with_link). The menu offers Extract only for a component that can leave: nothing outside uses its sketches or changes its bodies, no bodies were moved into or out of it, and none of its subcomponents is joined to the rest of the design.

Joints (joint.create, J) connect two components by joint origins that snap to faces, edges, circles and vertices: rigid, revolute, slider, cylindrical, pin-slot, planar and ball, with offsets, flip and limits. As-built Joint keeps the current position, Rigid Group locks components together, Drive Joints moves a joint through its values, and Motion Link couples two joints. A rigid joint holds its parts still, so Drive Joints has nothing to drive on it: it says so and offers Edit Joint, where the joint’s type changes to Revolute (rotate), Slider (slide) or Cylindrical (both). Links can be chained (one joint drives a second, which drives a third), and a value is carried through a chain of up to 64 links in whatever order they were made. Contact sets stop motion where bodies would pass through each other, Motion Study keys joint values along a timeline, and Interference shows overlapping volumes in red, lists the pairs closer than its Clearance and each body’s nearest neighbour.

In the Joint and Joint Origin dialogs, picking a straight edge puts the origin at its midpoint with the motion axis along that edge. A revolute joint turns about the picked edge; a slider moves along it. Round edges use their circle’s centre and normal. Existing saved joint origins keep their axes.

A joint origin picked on a part inside a sub-assembly that is placed more than once stays on the copy it was picked on. A joint at it holds or moves that copy as a whole, since every copy shares the part inside. Scripts give the copy with "path": the occurrence ids from the root down to the part, as occurrence.properties reports them.

A joint origin on a face or a hole keeps the turn it had when it was picked: when the face tilts (a bend angle or a rotation changing with a parameter) the part on it tilts with the face but doesn’t spin about it.

A joint origin can also sit on an edge of a flat face ("edge": [x, y, z]): at the edge’s middle, pointing out of the face with its y into it, or with "corner": [x, y, z] at the corner near that point, its x and y running along the two edges into the face. The edge, the face and the corner are found again by name, so a bracket set 40 mm in from a panel’s corner (a planar joint with values [40, 40, 0]) stays 40 mm in from that corner when the panel changes size.

A joint’s offset, angle and values can be typed as expressions over the design’s parameters ("offset": "gap + 2", "values": ["travel"]). The joint keeps them and works them out again whenever the design changes, so a part seated gap above a panel moves with gap. Renaming a parameter carries into the joints that use it, and deleting one they use is refused. If an expression stops working out (say a parameter becomes an angle), the joint is left out of the solve and joint.solve gives the reason, instead of placing the part from old numbers. Typing a plain number, or driving the value, drops that expression.

Relations are joints that tie only part of a placement, so a part is placed the way a designer describes it, one relation at a time: parallel (z along z, free to move and turn about it), perpendicular and angle (z square to z, or at the joint’s angle from mated), point_on_plane (a point on a face’s plane, offset off it), point_on_line (a point on an axis) and line_on_plane (an axis on a face’s plane: a cylinder’s radius as offset lays it tangent). A relation is made where the parts are: B moves no further than the relation needs. Several relations between the same two parts add up; the solver meets them together, each one’s degrees of freedom come off what is left, and one that can’t be met is reported, not forced:

{"command": "joint.create", "params": {"type": "parallel", "a": "Panel top", "b": "Bracket bottom"}}
{"command": "joint.create", "params": {"type": "point_on_plane", "a": "Panel top", "b": "Bracket corner"}}
{"command": "joint.create", "params": {"type": "parallel", "a": "Panel front", "b": "Bracket face", "flip": true}}
{"command": "joint.create", "params": {"type": "point_on_plane", "a": "Panel front", "b": "Bracket corner", "offset": -40}}
{"command": "joint.create", "params": {"type": "point_on_plane", "a": "Panel end", "b": "Bracket corner", "offset": -40}}

leaves the bracket flat on the panel, square to its front, its corner 40 mm in from the front and the end, wherever the panel’s size takes them. Coincident faces are a planar joint, a distance between faces is planar with an offset, concentric is cylindrical and two points that meet are ball.

The check behind it, inspect.clearance, does all of that in one call for any number of bodies, which suits scripts and agents checking a whole assembly:

{"command": "inspect.clearance", "params": {"clearance": 1, "nearest": 2, "skip_within": "component"}}

It returns overlaps (pairs sharing at least min_volume, 0.01 mm³ by default, with the shared volume, area, box and centre), near (pairs closer than clearance, with the distance and the two nearest points; a contact sharing less than min_volume is listed here at 0 with its overlap_mm3), nearest (each body’s nearest closest bodies) and unchecked (pairs whose overlap could not be worked out, with the reason). against limits the check to pairs with one body in bodies and the other in against, and skip_within leaves out pairs inside one occurrence ("occurrence") or one placed component, its subcomponents included ("component"): the parts of a bought component that touch by design. Distances are measured on the exact faces and edges; the meshes only rule out pairs that are too far apart, and the pairs left are checked in parallel. inspect.interference remains for the overlaps alone: it clears pairs whose meshes stay apart without intersecting them, intersects the rest in parallel, and returns after budget_s seconds (default 240) at most, listing the pairs it had no time for as not checked.

Mesh bodies (STL, 3MF and OBJ imports) are not checked by either command: each is listed in unchecked as {a: name, b: null, reason: "mesh body: not checked"}, so count: 0 or an empty overlaps is not mistaken for “no overlap” when a mesh body was in the design.

Gear, Rack and Pinion and Screw (on the ASSEMBLE panel) are motion links whose ratio comes from the parts rather than a typed number:

  • Gear (joint.gear {a, b, ra, rb}) couples two joints that turn. Joint 2 turns ra/rb as far as joint 1, the other way round; Internal gear (one of them is a ring gear) turns both the same way. Pick the round face of each gear, such as its pitch cylinder, or type the radii. Tooth counts work as well as radii, since only their proportion counts. Picking a gear’s face also chooses the joint that gear turns on.
  • Rack and Pinion (joint.rack_pinion {a, b, radius}) couples a joint that turns with one that slides: the rack travels the pinion’s pitch radius times its turn in radians (r·θ). Either joint may drive the other. In joint.list the ratio is given per degree, as for any link between an angle and a distance.
  • Screw (joint.screw {joint, pitch}, or {a, b, pitch} for a revolute joint and a slider) advances pitch per turn. A cylindrical joint is a screw on its own: its turn drives its travel. The thread is right-handed; Reverse makes it left-handed.

A radius or pitch may be an expression of the design’s parameters, and a picked face is found again whenever the design changes. Change the parameter or the gear’s diameter, and the ratio follows. The new link leaves the driven joint where it is, so teeth modelled in mesh stay in mesh. Reverse turns the driven joint the other way, for joints whose axes point opposite ways. Redefining a link (link: i, such as turning it the other way with Reverse) also leaves the driven joint where it is, so the gears stay in mesh at the pose they are in. These couplings are motion links, so they work in closed loops, with contact sets and in motion studies: a rack can push the piston of a slider-crank, or a pinion can steer through a rack and its tie rods. Drive the joint that drives the coupling (joint 1, the pinion, or the screw’s turn), not the driven one. A joint follows one link at most, and a coupling that would drive its own driver is refused. joint.list lists each coupling’s definition under links with an error when it can’t be worked out (the picked face is gone); the link then keeps its last ratio.

Cam (joint.cam {a, b, profile}, on the ASSEMBLE panel) makes a follower ride a cam. The cam turns on a revolute joint (a); the follower is a slider (b, a translating follower such as a valve tappet) or a revolute joint (b, an oscillating follower such as a rocker arm). The profile is the cam’s outline in a plane square to its axis: a closed profile of a sketch in the cam’s component, or the cam’s edges picked on its body (one click on an edge takes the closed loop it is on in that plane). The follower is:

  • a Knife edge: a point on the cam;
  • a Roller of a radius, typed or taken from the roller’s round face (picking that face also says where the roller is);
  • a Flat face square to a slider’s line, such as a bucket tappet.

A translating follower’s line runs along its slider’s axis, through its Tip when one is picked (its knife’s point or its roller’s centre), so a follower offset from the cam’s axis rides the right flank. An oscillating follower needs its tip, or its roller’s face, to know its arm. The follower rides the cam from the side it is on (Other side takes the other one). The follower’s travel over a turn of the cam is worked out from the profile and kept with the link, one value a degree with a smooth curve between them that never overshoots its samples. It is worked out again whenever the design changes: change the cam’s sketch, a parameter or the roller, and the follower follows. Like the gears, the new link leaves the follower where it is (model it touching the cam), and the cam is a motion link: it drives in Drive Joints, plays in motion studies, works in closed loops (a tappet can be the piston of a slider-crank) and stops at contact sets. The profile may also be on the grounded part, with the follower’s guide on a turret that the cam joint turns. joint.list gives a cam’s link lift (the follower’s lowest and highest values and its rise) instead of a ratio. A profile that is not closed, a line or an arm that misses the cam somewhere, a follower that would jump, and axes that are not square or parallel are refused.

A joint moves one component against another, so its two picks must be on different components. A STEP file with several parts often comes in as bodies of one component, and picks on two of those bodies make no joint. The Joint and As-built Joint dialogs say so under the picks and offer Create Components from Bodies: each picked body gets a component of its own and stays where it is, the picks are kept, and the joint previews. joint.create refuses such origins with the same advice.

Joints may close a loop, as in a four-bar linkage or a slider-crank. When one joint of the loop is driven, the other joints in the loop follow so that every joint still meets, and the mechanism stays in the assembly mode it started in (an open four-bar does not flip to the crossed one). If the motion would take a loop past the point where it can still close, for example driving a rocker beyond its swing, the motion stops at the last position where the loop closes, and the drive reports stopped_by_loop. A loop that cannot close at all is listed under conflicts and nothing in it moves. joint.list reports how many degrees of freedom each part still has with its loops closed: the moving parts of a four-bar have one between them, and a loop locked by a rigid joint has none. A joint a motion link drives adds no freedom of its own: two meshed gears on a fixed housing have one between them, each turning with the driving gear. The Drive Joints dialog says when a drive stops short and why. While it previews, a row under the values gives the value the joint reached and the reason (the loop that can’t close further, or the two parts a contact set keeps apart), and after OK the status line repeats it. The value box keeps what you typed. The dialog opens on the joint picked in the browser, or on the first joint that moves when none is picked.

Drag a part that joints place and it moves as far as its joints allow: the point you grabbed follows the pointer in the plane through it that faces the view, the joints on the part’s chain take the values that bring it nearest, within their limits, through motion links, and with closed loops kept closed, and a four-bar’s rocker stops at the end of its swing. The whole drag is one undo step. A grounded part, or one its joints hold fixed, is not moved; a drag on it draws a selection box. Each frame runs joint.reach, which scripts and agents use directly to put a part’s point somewhere: joint.reach {occurrence, point} moves the joints on its chain from where they are to the nearest pose (the arm keeps its elbow up or down, and a linkage stays on its branch). at picks the part’s point (the centre of its bodies by default), z with axis also turns one of its axes, and joints names the only joints to move (the other joints of a closed loop they are in follow). The answer says whether it reached the point and how close it got (error_mm, error_deg), and when it falls short, the limits it rests on (stopped_by_limit) or the loop that stopped it (stopped_by_loop). Contact sets are not checked by a reach; drive the joints for that.

The solver places what it reaches from the root and from grounded components. A chain of joints that is joined to nothing grounded is never placed, so driving one of its joints moves nothing. The drive says so: joint.drive answers moved: false with a reason and the unconnected part (“Pedestal:1 is not connected to anything grounded”), the Drive Joints dialog shows the same row, motion.play lists such joints under unmoved, and joint.list names every such chain under floating. Ground a part of the chain, or join it to a grounded part (a rigid as-built joint keeps it where it is), and it moves.

joint.drive answers the joint’s values as joint.list gives them, in degrees and millimetres with their units and limits, and moved says whether any part moved. A joint’s origins are mated by default: B’s z axis is turned against A’s, as two faces meet. flip: true lines them up instead, so two origins given at one point with the same z and x stay as they are. A point origin without x takes its x from z alone, so a part turned about z turns back when it is joined: give x on both origins, or use joint.as_built, to keep it where it is. Without an angle, a new joint (other than a relation, which starts where its parts are) takes no turn or a half turn about its axis, whichever turns B less: two faces that already meet, a plate under a panel, stay as they are instead of B spinning about their shared normal. For scripts that drive many steps, quiet: true leaves the per-part degrees of freedom out of joint answers, and motion.play with quiet: true gives the poses of only the parts that moved.

Motion Trace (motion.trace, on the ASSEMBLE panel) draws the path a point of a part takes while a joint is driven or a motion study plays, such as the coupler curve of a four-bar. Pick the point on the part (a face centre, a circle centre, an edge midpoint or a vertex), then either a joint with the values to drive it from and to (a turn of the joint from where it is, at first) or a motion study. The path previews in the viewport as the mechanism would move: closed loops follow, and the path ends where a loop can no longer close or a contact set stops the motion, which the dialog says under the values. OK keeps the path as a 3D sketch spline in a new sketch of the root component (Motion Trace1, …), where no joint moves it, and it can serve as a sweep or pipe path. New traces retain their source and update when the mechanism’s joint origins, limits, motion links or driving conditions change. Playing or dragging the mechanism does not redefine the trace: it keeps the assembly branch from when it was made. Unrelated parameter and display changes do not resample it. A path that comes back to where it started is kept as a closed, smooth spline. motion.trace {occurrence, point, joint, from, to, index?, samples?} (or {occurrence, point, study, samples?}) answers the world points without changing the design, and motion.trace.keep with the same parameters (and name?) keeps them. point_ref can retain a joint-origin snap (such as {occurrence, face: [x,y,z]}), so the point follows its resized face; without it, the point is fixed in the part’s coordinates. The dialog retains its face or circle snap. motion.trace.edit {sketch, from?, to?, samples?} changes a joint trace’s driving conditions; joint with from and to, or study, replaces its motion source. Expressions in from and to follow parameter edits. Study traces follow edits to the referenced study, including its step count when Samples was left at the default. auto_update: false in motion.trace.keep makes a fixed curve; in motion.trace.edit it detaches an existing association, preserving the curve. Curves saved by earlier versions remain fixed. motion.list includes the associations and their errors. If a source is lost, the sketch warns and keeps its last successful curve until the source is restored or the association is detached. Changes and their generated curves undo and redo together. A trace takes 2 to 500 samples: 100 by default for a joint, one per step for a study.

The Chebyshev linkage demo builds a complete four-joint mechanism, plays a saved study and demonstrates the same retained trajectory updating after a link-length or driving-range edit.

Export Animation… in the Motion Study dialog saves the study as an animated GIF, drawn from the viewport’s camera as the study plays (the keys in the dialog count, even before OK). motion.export_animation does the same for scripts, the CLI and agents, without a window: give a study, or a joint with from and to (and index?) to animate a drive, and a path. A path ending in .gif gets an animated GIF that loops; any other path is a folder that gets the frames as frame-0001.png, frame-0002.png, and so on (made if missing; frames of a longer earlier export there are removed), for a video tool. The web app writes GIFs only. samples is the number of frames (one per step for a study by default, 100 for a joint), width and height their size in pixels (800 × 500 by default, at most 4096 a side), and fps the GIF’s speed (20 by default); grid: false and axes: false leave out the ground grid and the origin’s axes. camera takes the reply of the control channel’s ui.camera (or any of its fields; the rest come from the view view.apply chose, or the home view framing the model). The frames are drawn by the same renderer as solidcraft-cli snapshot, on a copy of the design, which is not changed. A GIF keeps one palette for all its frames, so colours don’t flicker, and stores only what changed from one frame to the next. Frames are capped: at most 1000, and a GIF holds at most 2^26 pixels of frames (about 160 frames of 800 × 500). An exploded view animates the same way: give its name or id as view (instead of a study or a joint) and the parts come apart at a steady pace, from the assembled placement to where the view shows them; back: true brings them back together, exploded at the middle frame. That takes 41 frames by default, or 81 with back, and the home view frames the parts both assembled and exploded. As with explode.show, the design keeps its placements.

Motion analysis. The Motion Study dialog’s Study box opens a saved study to edit, and by its timeline the dialog charts what the study does. Plot picks a moving joint value (a joint picked in the Browser is picked here too), or Picked point once a point is picked in the dialog’s Point box, and Quantity whether to show its value, velocity or acceleration; the chart runs over the study’s steps with a cursor at the step shown, and a click or a drag on it moves to that step. Values are in degrees and millimetres; rates are per step, or per second once Duration says how many seconds the whole study takes. A picked point charts its distance from where it starts, its speed and the size of its acceleration. Check collisions plays the study and lists the pairs of parts that pass through each other, with the steps where they do (a click on one shows the first of them); those steps are shaded on the chart. It checks the parts picked in Components (against the ones picked in Against, when there are any), else every component, or the pairs of the design’s contact sets (a choice beside the button once it has any).

motion.plot {study, joints?, occurrence?, point?, samples?, duration?} answers the same numbers: for each joint value its value, velocity and acceleration per sample (with their units), and for a point its position, velocity and acceleration vectors, speed and acceleration_norm. The study plays as motion.play places it (closed loops follow, contact sets stop it), each sample from the one before, on a copy: the design is not changed. Velocities and accelerations are finite differences between samples, central inside and one-sided at the ends, so they are as smooth as the samples are dense (up to 1000, one per step by default). Angles are unwrapped, so a joint that turns on past half a turn keeps counting. motion.interference {study, occurrences?, against?, contact_sets?, samples?, budget_s?} answers each colliding pair with its first_step, last_step, the steps ranges where they overlap, the most volume they share at a sample (volume_mm3, worked out exactly from the bodies), the first sample sharing that much (deepest_step) and how far into each other they reach (depth_mm, an estimate from points of the bodies’ contact meshes). When the volume could not be worked out at every sample they overlap (more than 16 colliding pairs, or past the check’s 256 body intersections), volume_mm3 and deepest_step are null and not_measured says why. It plays the study as motion.play does, but contact sets don’t stop it, and parts are tested at the samples only: a part that passes right through another between two samples is missed. Parts are told apart by occurrence, as contact sets tell them apart: an occurrence inside a sub-assembly placed twice is one part, so its two copies are not checked against each other. A check takes at most 256 bodies; on a large design, list the occurrences to check (an arm, with against the parts it must miss). When the work runs out it says truncated and the step it checked_to, and it is not clear, even when nothing collided in the steps it checked. A mechanism with detailed parts, such as a gear train, can need more than that fixed work budget: give it budget_s (seconds, at most 86400) and the check runs until that time instead, looked at between samples. joint.sweep {joint, from, to, index?, step?, refine?, occurrences?, against?, contact_sets?, report_gaps?, apply?} drives one joint value from from toward to and answers how far it gets before parts touch: stopped_at (the last value clear of contact), stopped_by (the two parts and the point at which they touch, or "loop" where a closed loop can no longer close, or "limit"), and with report_gaps the nearest gap at every sample and the nearest each part came to another (min_gap). The samples are fine enough that no part passes through another between two (or step apart), and the first contact is bisected until no part moves more than refine (0.01 mm by default) across it. Gaps are distances between the parts’ tessellations (gap_method: "mesh"): exact for flat faces, within gap_tolerance_mm for curved ones. Pairs already touching at from, such as parts joined face to face or a door resting on its frame, are listed in touching_at_start and stop the sweep only once they pass into each other, deeper than they start. The sweep runs on a copy (closed loops follow, contact sets don’t stop it) and leaves the design as it was unless apply is true.

Find Limits. In Edit Joint, the Limits section’s Find Limits button fills in each value’s Minimum and Maximum from where the parts the joint moves first touch another part, turning or sliding each way from where the joint is now, and ticks its limits. It searches the joint as the dialog has it (a type, flip or offset changed but not yet applied included), on a copy: nothing changes until OK, so the values can still be rounded or edited. They are rounded toward the joint’s value (a tenth of a degree, a hundredth of the length unit) so they stay clear, and a line under each value says what stopped each side. An angle is searched a turn each way: a side that meets nothing is set a turn from the other side, or half a turn each way when nothing is met at all. A distance is searched as far as the parts reach; a side that meets nothing there keeps what it had, and the limits stay off until both sides are found. Parts already in each other where the joint is now are named, and stop the search only once they go deeper. The search runs while the window goes on (Cancel drops it). joint.find_limits {joint, index?, refine?, step?, occurrences?, against?, contact_sets?} answers the same: for each value its min and max (null where a distance meets nothing), what stopped each side (min_by, max_by: the two parts and where they touch, or "loop", "limit" (the limit of another of its values, or of another joint through a motion link), "turn" or "range"), the range searched, and the parts touching_at_start and overlapping_at_start (with depth_mm). The joint’s own limits are left out of the search, closed loops follow, and each side is bisected to refine as joint.sweep does.

Insert Design (component.insert_design {path, translate?}, on the ASSEMBLE panel and in the File menu) adds a component linked to another saved design, so each part can live in its own document. When other designs are open in tabs, the button first lists them under Open designs. One click inserts that design; a design not saved yet is greyed out, as it has no file to link; one with unsaved changes inserts its saved file, and the status line says so. Browse… at the end opens the file picker, as the button does when no other design is open. Preferences › Design › Insert Design lists the designs open in other tabs first turns the list off. A STEP or mesh file picked there is inserted as Insert STEP or Insert Mesh would. The part’s solid bodies come in as they sit in that design, and they are kept in the assembly, so it opens even without the part file. The link is stored relative to the assembly’s folder once the assembly is saved. For scripts, translate: [x,y,z] places the design’s origin in millimetres from the destination parent’s origin (the active component unless parent is supplied). It defaults to [0,0,0]. With reuse: true, insertion reuses the existing linked component but uses the same coordinates; it does not inherit another occurrence’s position or rotation. For example, inserts at [20,0,0] and [40,0,0] place the two origins at 20 and 40 mm, even when the second reuses the link. After the part file changes, Update Links (component.update_links) reads it again. An assembly that opens, or whose tab comes back to the front, does that by itself for the parts whose files changed since it last read them, whether the part was saved in another tab, another window or by a checkout. The parts it reads come in as one step, so one Undo takes them all back. A part that cannot be read keeps its bodies, is tried again the next time, and holds up no other. Update changed linked designs when a tab comes back (Preferences, Design, update_changed_links) turns this off. To edit the source, right-click a linked component or occurrence in the Browser and choose Open Linked Design. It opens the part in another tab and reports if its file is missing. Break Link (component.break_link) keeps the bodies and forgets the file, and component.links lists the links, whether their files are found, and whether they changed since the assembly last read them (changed). When a file can’t be reached it also gives a state (missing, unreadable or not_allowed) and a reason. A linked component holds the part’s bodies only: its timeline, parameters and sheet-metal data (flat pattern, bend table) stay in the part’s own design.

To edit a linked part where it sits, double-click it, in the Browser or on one of its faces in the view, or choose Edit in Place from its menu. Its design opens in a tab of its own with the rest of the assembly drawn faded around it, at the placement you picked, and the part’s own history, Browser and commands work as usual. Update Links there also reads the assembly’s links again and redraws it. Finish Edit (component.finish_edit), at the right end of the MODEL tab, or the mouse’s back button, saves the part and goes back to the assembly, which takes up the change; the part’s tab closes again when the edit opened it. Run as a command (by an agent, for example), it does not save: the part must be saved first. Activate still makes a linked component the active one without opening it, and a double-click on a component that is not linked activates it. Double-click a component to activate it, or to edit a linked one in place (Preferences, Design, double_click_components) turns this off: a double-click in the Browser then activates every component, and one in the view does nothing.

If a source file moves or is renamed, right-click its linked component or occurrence in the Browser and choose Component → Relink Source (All Instances)…. Choose the saved .sc design to use. Relinking updates that component’s stored path and solid bodies for all its instances, keeping their identities and placements. Cancelling or choosing a missing, invalid, empty or self-referencing design leaves the assembly unchanged. The operation is one undo step and the new path is saved relative to the assembly when possible.

To swap only one occurrence of a component placed several times, right-click that occurrence and choose Component → Relink Source…. Only that occurrence shows the chosen design. It keeps its position, grounding and joints, which move to the same faces of the new design where it has them. It becomes an occurrence of the component already linked to that design, or of a new linked component; the other occurrences keep the old source. Scripts pass the occurrence: component.relink {component, occurrence, path}, and the reply’s component is then the one the occurrence shows and previous_component the one it showed.

Scripts use component.relink {component, path}. The reply includes previous_path, path, warnings, joint_errors and joint_conflicts. Changed geometry can invalidate a joint’s face reference: relinking reports those joints in the reply and UI status/message history; repair the joint or undo the relink. Relink does not repair or verify the intended meaning of existing joints. When linked geometry changes and the component has joints, a reminder to check those joints is also shown even if the solver reports no error: a successful solve does not prove that a joint still describes the intended connection.

STEP export writes the component tree as an assembly.

Right-click a component in the Browser and choose Component → Properties, or use Inspect → Measure → Properties for the selected placement or the active component, to inspect material-aware mass, volume, surface area, centre of mass, first moments, moment of inertia and world bounds. While the window is open, the view marks the centre of mass with the three principal axes standing out of it. Descendants and repeated instances contribute to assembly totals; pending moves are included. A component uses its first placement; occurrence.properties selects a specific instance. Root Properties measures the complete assembly. Volumes are summed, including overlapping bodies, rather than measuring their union. The same workflow is available through component.properties and occurrence.properties; see placement and measurement details.

Part properties and the bill of materials

A component definition carries its own part properties: a part number, a description and a revision, other tools’ data under a reverse-DNS key (the same rules as the design’s own properties), and free text properties such as a material grade or a finish. Every occurrence of the component shares them, Paste New copies them, and they are saved with the component. component.metadata {component} reads them and component.metadata.set {component, part_number?, description?, revision?, bom_type?, values?, properties?} changes them in one undo step (an empty value removes a field). The root component is the design itself: its properties are document.metadata’s. Standard parts (parts.insert) get a part number from their standard and size (ISO 4762 M6x20, or a bearing’s designation) and a description; Change Part Size renumbers them, keeping a field you changed yourself.

Bill of Materials (assembly.bom, on the INSPECT panel) counts the placed components:

  • levels: "flat" (the default) lists the parts at any depth, each counted through its subassemblies: a part placed once in a subassembly that is placed twice counts 2. A subassembly is listed through its parts, unless it has bodies of its own.
  • levels: "top" lists the direct children, a subassembly as one row.
  • group_by: ["part_number"] (the default) puts definitions with the same part number in one row; a definition without a part number gets a row of its own. ["definition"] gives every definition its own row.
  • group_children: true keeps what is placed inside a part with a part number in that part’s row: a bolt with a washer and a nut placed inside it is one row, the washer and the nut listed under it as children (items 1.1, 1.2…, with each per bolt and count in all) instead of rows of their own. Bolts with different contents (one with a nut, one without) are different rows. total_occurrences counts the children too, so it is the same either way. For an inseparable part, only its purchased descendants become children; a purchased part still has no children listed. Parts without a part number are not grouped this way.
  • component lists one subassembly’s parts instead of the design’s.

A component’s bill of materials type (bom_type, a part property set with component.metadata.set {component, bom_type}) decides how it counts in the assemblies it is placed in. Without one it is normal.

TypeForIn the assembly it is placed in
normalSomething you makeA row of its own; every occurrence adds to its quantity
phantomSomething just to group (a fastener set)No row: what it holds counts as if it were placed there directly, in the same rows as the same parts placed there
referenceSomething to use as a reference (a fixture shown for context)Counted nowhere, and nothing inside it is counted either
purchasedSomething you buy (a gearbox)One row; nothing inside it is counted or listed
inseparableSomething made as one piece: welded, glued, riveted and so on (a welded frame)One row; in parts only, the purchased parts inside it are listed too, and the rest of what it holds is listed in its own bill of materials

Two components with the same part number share a row only when their types agree. A component’s own bill of materials (component) lists what it holds whatever its type.

Rows are sorted by part number (rows without one come last) and hold item, part_number, description, revision, bom_type, count, definition (the component names), components (their ids) and properties, with total_occurrences beside them. path writes them to a .csv file (the columns item, part_number, description, revision, count and definition, every field quoted, and a field that a spreadsheet would run as a formula written with a ' before it so the spreadsheet shows it as text; children follow their row) or a .json file. A formula field is one that starts with =, +, - or @, their full-width forms, a tab or a line break, after any spaces and '; a plain number such as -5 is not one. A script that reads the CSV sees the ' too: a part number =1+1 is '=1+1 in the file, and '=1+1 is ''=1+1. To get the value back, take off one leading ' when what follows starts a formula, as solidcraft_doc::csv::unguard does, or read the .json file, which holds the values unchanged. The ' holds only while the file is as SolidCraft wrote it: a spreadsheet that saves the file again as CSV can drop it (Excel does), and the formula then runs the next time that file opens. Export again from SolidCraft instead of passing on a CSV a spreadsheet has saved. The window lists the same rows, switches between the levels and groupings and shows children under their part, edits a picked row’s part number, description and revision, and exports them. Changing part properties changes no geometry, so it doesn’t evaluate the model or re-seat joints again.

Sheet metal

MODEL → MODIFY offers Convert to Sheet Metal. The SHEET METAL tab is always available (it is highlighted and selected when the active body is a sheet), and builds parts from a sheet of constant thickness under a sheet metal rule (thickness, bend radius, K-factor), set in Sheet Metal Rules (sheet.manage_rules).

  • Flange: a base flange from a sketch profile, a contour flange from an open sketch, or an edge flange from a part edge.
  • Hem returns a sheet edge back toward the sheet; no new sketch is required.
  • Fold (sheet.fold) bends the sheet along a sketch line.
  • Unfold / Refold (sheet.unfold, sheet.refold) for working on the flat part.
  • Convert (sheet.convert) turns a plate of constant thickness into sheet metal, or, with bend edges, a solid or a shelled body.
  • Create Flat Pattern (sheet.flat_pattern) shows the flat part with its bend lines and a bend table, and exports it as DXF or a bend-table CSV through the save picker.

Bends follow the rule’s K-factor, so flat lengths match what a press brake will make. Tests check flat lengths against hand-computed bend allowances.

Step by step

A tray: a plate with a flange up each long side, and its flat pattern.

1. Base flange. Sketch an 80 × 50 mm rectangle on the Top plane and finish the sketch. On the SHEET METAL tab, click Flange, then the profile, then OK: a plate at the rule’s thickness.

2. Edge flanges. Click Flange again, then the plate’s two long top edges. Type the height (20) and press Enter. Each flange bends up at the rule’s radius.

3. Flat pattern. Flat Pattern shows the part laid flat, with a dashed line for each bend, its flat size and a table of bends to export as DXF or CSV.

Rules and tools

A rule is saved in its design, and a new design starts with only the default steel rule. To use the same rules in every design, keep them as personal rules, saved in your profile: open Personal rules at the top of Sheet Metal Rules.

  • Save new keeps the rule the manager shows under a name: its thickness, K factor, bend radius, reliefs, gaps, corner relief shape and angle display. A bare number is saved with the design’s unit, so the rule makes the same sheet in a millimetre or an inch design. Thickness and other expressions are kept as typed, and are worked out in the design the rule is used in.
  • Picking a personal rule fills the manager with it: the design’s own rule of that name, or else a new rule. OK adds it to the design (or updates that rule); picking alone changes nothing.
  • The save icon beside the box saves the manager’s values over the picked personal rule. Rename and Delete change the personal rule only, never the rules already in designs.

A design keeps its own copy of each rule it uses, so it builds the same on a computer without your personal rules. The profile keeps up to 64 of them.

A copy of a sheet metal body is sheet metal too. Mirror (solid.mirror with bodies) makes the opposite hand: its own flat pattern, DXF and bend table, the mirror image of the original’s flat, with the same thickness side, bends and flat lengths, and holes where the mirror puts them. A copy from Move (copy: true) or a body pattern keeps the original’s flat pattern. Every copy takes further sheet metal features of its own. Mirroring with combine joins the two halves into one solid, which has no single sheet model yet; mirroring or patterning flange features themselves is not supported yet.

An edge flange’s height runs along the flange to its far edge. Where it is measured from is the feature’s height_from (Height From in the Flange dialog): outer (the default, Outer Sharp) starts at the outer virtual sharp, where the lines of the base’s and the flange’s outer faces meet, so (R + T)·tan(θ/2) of it is bend before the flat leg starts (θ the bend angle, R the inner radius, T the thickness); inner (Inner Sharp) starts at the inner virtual sharp, R·tan(θ/2) before the leg; tangent is for bends past 90°, where the outer sharp runs far out: it starts at the line square to the flange that touches the outside of the bend, R + T before the leg. Up to 90° tangent is the outer sharp (at 90° the two are the same line, the base’s outer face, so the leg does not jump as the angle passes 90°). A height shorter than what lies before the leg is refused with the smallest one that fits. With a 2 mm sheet and a 2 mm radius, a 90° flange 20 high has a leg of 16 from the outer sharp or the tangent and 18 from the inner sharp; at 120° it has a leg of 13.07 from the outer sharp, 16.54 from the inner sharp and 16 from the tangent.

{"command":"sheet.flange","params":{"edges":[[50,0,2]],"height":20,"height_from":"inner"}}

Where an edge flange’s bend starts is its position (Bend Position in the Flange dialog): how far inside the picked edge the base is cut back. inside (the default) puts the outer virtual sharp on the edge, (R + T)·tan(θ/2) inside it, so at 90° the flange’s outer face is flush with the edge’s face; outside (sheet.flange also takes bend_outside and saves it as outside) starts the bend at the edge, which leaves the base whole and the bend wholly outside; middle cuts back half of inside; material_outside puts the inner virtual sharp on the edge, R·tan(θ/2) inside it, so at 90° the flange’s inner face is flush with the edge’s face and its material is outside; tangent is for bends past 90°: it cuts back R + T, so the outside of the bend is tangent to the edge’s face, touching it a quarter turn into the bend with nothing of the bend past it. At 90° that is inside. Below 90° the outside of the bend never turns parallel to the edge’s face, so tangent cuts back (R + T)·sin θ instead: the bend’s end is on the edge’s face and the leg leans on out past it.

{"command":"sheet.flange","params":{"edges":[[50,0,2]],"height":20,"angle":"120 deg","position":"tangent"}}

An edge flange takes its setback off its own edge only. Where that edge ends at an inside corner of the base (the base runs on past it, like the leg of an L), the rest of the base stays, and the rule’s bend relief (relief_width and relief_depth; Relief Width and Relief Depth in Sheet Metal Rules) is cut into it beside the bend’s end: a slot the relief width wide, from the relief depth before the bend line to the relief width past the flange’s far edge in the flat pattern. The bend and the flange are then free of the base, folded and flat. A relief that can’t be cut (a width under 0.01 mm, one as wide as the base beside the bend, or a slot that would cut the base apart) is left out, and the flange feature reports a warning saying where.

Where a cut-out has taken part of an edge away (a corner cut off the plate, a notch in the middle of an edge), an edge flange picked on that edge runs along the stretch the cut-outs leave nearest the pick, and takes its setback off that stretch only. The base on the other side of a notch keeps its edge, and a pick there, in the same feature or a later one, makes a flange of its own. Where the base is still left beside the bend’s end at such a cut-out, it gets the bend relief as above. An edge that cut-outs have taken whole is refused.

The setback is cut out of the base’s outline as it was drawn: where the bend line crosses an arc of the outline (a plate with rounded corners, bent inside or in the middle), the arc stays an arc and ends there, in the flat pattern and the DXF. A free-form curve (an ellipse, a spline) in an outline that a setback cuts is followed by tangent arcs within 0.001 mm.

Where two bends of one edge flange feature meet at a corner of the base, the rule’s corner relief shape (corner_relief_shape; Corner Relief Shape in Sheet Metal Rules) says what is cut there. notch, the default, cuts nothing more: the bends end square at the corner, which leaves a step in the flat pattern between each bend’s end and its flange’s closed end. tear cuts no relief either, and no step: both bends run to the corner where the two bend lines meet, and each bend’s end is cut straight, in the flat pattern, from that corner to where its flange’s closed end starts. Folded, the two bends touch at the corner and open to the rule’s gap where the flanges start; the bend table lists a torn bend between the middles of its two ends. A tear whose cuts would cross in the flat pattern, or that would run one bend into the other folded (flanges at very different angles), is left out with a warning and the corner keeps its notch. round and rectangular cut a relief the size of the rule’s corner relief across, centred on the corner where the two bend lines meet: out of the base a round with half that size as its radius, or a square with half that size as its side, and both bends stop where it begins, cut straight across. The flange panels keep their ends. The relief is in the folded part, the flat pattern, the bend table (the bends are shorter) and the DXF (a round relief is an arc). At a corner that isn’t square the round follows the angle of the base and the square becomes a kite, its two cuts square to the edges. Changing the shape in a rule changes the parts made with that rule. A relief that doesn’t fit (under 0.01 mm across, leaving nothing of a bend, or reaching as far from its corner as another edge of the base is) is left out, and the flange feature reports a warning saying where. The reliefs at all the corners are cut together, so a corner’s relief does not depend on the order the corners are cut in; two of them that would overlap are both left out, each with a warning saying which the other is.

{"command":"sheet.manage_rules","params":{"name":"Sheet 2 mm","thickness":"2 mm","k_factor":0.5,
 "bend_radius":"1 mm","corner_relief_shape":"round","corner_relief":"8 mm","active":true}}

The rule’s shape and size are the default. An edge flange feature can choose its own for the corners its flanges make: corner_relief_shape (notch, round, rectangular or tear) and corner_relief (the size across, a length or an expression) in sheet.flange, or Corner Relief Shape and Corner Relief in the Flange dialog. In the dialog, Rule as the shape and an empty size take the rule’s; either one left out follows the rule while the other is the feature’s. Corners are relieved only between the flanges of one feature, so a feature’s choice never meets another’s at a corner, and two edge flange features of one part can have different reliefs under one rule. A base or contour flange takes its corners from the rule only and refuses both parameters. The flange gap and the rule’s other values stay with the rule for every flange. The feature’s choice is saved with the design (file format 23).

{"command":"sheet.flange","params":{"edges":[[60,0,2],[120,45,2],[60,90,2],[0,45,2]],
 "height":20,"corner_relief_shape":"round","corner_relief":"6 mm"}}

Where the edge flanges of one feature meet at a corner of the base, their panels are closed toward each other at any bend angle: each end is cut square to the sheet, on a line in the flat chosen so that the edges nearest each other (on the inner faces) run parallel, the rule’s gap apart, either side of the plane halfway between the flanges. Flanges at 72° get slanted ends in the flat; corners that aren’t square close the same way, and at 90° it is the corner the rule always gave. A corner that can’t be closed (a gap under 0.01 mm, flanges bending opposite ways, ends that would overlap in the flat pattern) stays open, and the flange feature reports a warning saying where. A flange added by a later feature leaves the corner as it was: the earlier flange keeps its length and the new one ends at its bend.

The flanges of one edge flange feature can each have an angle and a height of their own. An entry of edges is a point [x, y, z], which takes the feature’s height and angle, or an object {"point": [x, y, z], "angle": "105 deg", "height": "30 mm"} whose angle and height (either one, or both) replace them for that edge. They are expressions like the feature’s own, so parameters work, and each is a feature input of its own (Edge angle, Edge height) after Height and Angle. The corners between such flanges close like those between equal ones: the ends run toward each other the rule’s gap apart, either side of the plane halfway between the two flanges, and a taller flange’s end keeps that line up to its own tip. This is how to close a corner between flanges at different angles, because flanges of separate features don’t close toward each other. In the Flange dialog, picking more than one edge shows a row per edge with an angle and a height box; an empty box takes the value above.

{"command":"sheet.flange","params":{"edges":[[60,0,2],{"point":[120,45,2],"angle":"90 deg"},
 {"point":[60,90,2],"angle":"105 deg","height":"30 mm"},{"point":[0,45,2],"angle":"120 deg","height":"25 mm"}],
 "height":38.6,"angle":"75 deg"}}

One edge can also be split into segments: segments lists stretches of the picked edge, each {"from": 0, "to": 0.5, "angle": "90 deg", "height": "20 mm", "flip": false}. from and to are fractions of the stretch the sheet still has there (what cut-outs leave), from the end where the outline enters the edge, so the first from is 0, the last to is 1 and each to is the next from: segments divide the edge, they never leave material out (a set-back is the other thing). Consecutive segments that match in angle, height and side make one bend; the others make a bend each, with its own setback, leg and side, so the base’s outline steps where their angles differ and the flange’s far edge steps where their heights differ. A segment’s flip replaces the feature’s flip outright: false is the default direction and true the other one, whatever the feature’s flip is, so it is not a turn relative to the feature. No bend relief is cut where two segments meet, and where the two stretches bend to opposite sides the blank is cut along their junction, from the inner bend line out to the nearer stretch’s far edge, in the flat pattern and the DXF: a blank still joined there cannot fold one stretch up and the next down, while same-side stretches stay one piece. Stretches at different angles have different setbacks, so their bend lines are offset: the cut starts at the inner of the two, so it covers the strip between the bend lines, over which the folded part separates. Its outer end is the nearer far edge: past that only the longer stretch’s material remains, so there is nothing of the other stretch left to part. Where a cut-out took that far edge away, the cut is clipped to the material that is left: it runs from the bend line to the nearest boundary the junction line meets (a notch’s edge, for one), so the blank is parted as far as its remaining material allows. Only the stretch of the junction line from the bend line out to the junction’s own tip belongs to that junction: past the tip the line runs on through material the bend never reaches (another arm of a C-shaped blank, for one), and that material keeps no cut. A straight boundary and a circular one are met exactly, so a round notch ends the cut on the arc itself and the DXF splits the arc there. then is refused on a split edge: bend each segment’s far edge in its own feature. Stretches that meet within 1e-9 (the junction tolerance, SEGMENT_JOIN_EPS) are one partition: the junction is snapped shut whatever the two stretches’ angle, height or side are, so no gap survives into trimming or the flat pattern, and only stretches that match make one bend. A stretch narrower than that tolerance is no bend of its own: the run before it takes that stretch (its angle and height do not build), so the bends still tile the edge with nothing left uncovered and no reversed span reaches the flat pattern. That includes stretches at the very start of the edge, with no run before them: the bend that follows them takes them and still starts at the edge’s start, so the first bend covers the edge from 0 and the last ends at 1. The whole stretch must lie on one straight line of the outline, with no gap in it: a stretch along a curve is refused (its fractions would only follow chords), a stretch across a notch or a step is refused too, and a straight edge a cut-out has shortened can still be split because the stretch that is left lies along the same straight line. In the Flange dialog, each picked edge has its own Segments list, Split Edge adds one more equal stretch (or splits the last one on an edge the design already split), removing a stretch keeps the edge whole (the gap is bridged from the stretches beside it, never from the removed row, so a boundary the user did not touch does not move, or the whole edge when it was the only one), and editing a feature keeps every edge’s fractions, angle, height and side, an explicit flip: false included. The dialog refuses then on a split edge by name, rather than dropping the chain.

{"command":"sheet.flange","params":{"type":"edge","edges":[{"point":[100,0,2],"segments":[
 {"from":0,"to":0.5,"angle":"90 deg","height":"20 mm"},
 {"from":0.5,"to":0.75,"angle":"90 deg","height":"15 mm"},
 {"from":0.75,"to":1,"angle":"45 deg","height":"15 mm","flip":false}]}],
 "height":"20 mm","angle":"90 deg"}}

An edge flange can bend more than once: then lists further bends, each an angle and a height, and each is a flange on the far edge of the one before it, made in the same command and undone with one step. It is the way to make a flap with two or more bends that is part of the same sheet (a Contour Flange is always a sheet of its own). The example bends the end edge of a wall 45° outward, then 45° again, the second leg 80 mm long. Each bend is a flange feature of its own in the timeline, so each can be edited there. In the Flange dialog, an edge flange has Further Bends under Bend Position: Add bend adds a row with an angle and a height, and the bin beside a row takes it out.

{"command":"sheet.flange","params":{"edges":[[1,0,20]],"height":40,"angle":"45 deg",
 "then":[{"angle":"45 deg","height":80}]}}

A further bend turns the way the one before it did. "flip": true on a bend turns it the other way, which makes an offset (a jog): bend 45° out, then 45° back, and the last leg runs parallel to the first. In the dialog each further bend has a turns choice, Same way or Other way.

When the shape is easier to draw than to count out, Path Flange (sheet.path_flange) makes the same chain from a sketch. Sketch an open chain of lines on a plane across the edge (square to it), starting at the edge, then pick the edge and the lines. Each line becomes a flange as long as the line (measured like an edge flange’s height: to the outer face from the virtual sharp), and each corner a bend. The first line’s angle to the sheet sets the first bend, and the path’s turns set which way each later bend goes, so a jog is just a path that turns back. Changing the sketch changes the flap. It is one feature in the timeline and part of the same sheet. Lines that run straight on from each other count as one; a line that does not turn from the sheet is refused, and so is a line too short for its bend. Arcs are not supported yet.

{"command":"sheet.path_flange","params":{"edge":[1,0,20],"sketch":"Path","curves":["p1","p2"]}}

A flange can also stand on the far edge of another flange: a return, such as a lip turned in along the top of a tray’s walls. It runs along that edge from end to end, so it follows the closed end of the flange it stands on, slanted or not. Two returns of one feature whose flanges meet at a corner of the base are closed toward each other as well, with the rule’s gap. They are mitred either side of a plane that runs along the mean of the two panels’ normals, bisects their bend lines and passes where the lines their panels start on cross. Returns that lie in one plane (level returns on a tray’s walls, whatever the walls’ angle) then meet on the corner’s bisector, like the sides of a picture frame. Each panel ends on a slanted line in the flat. Each bend stops square where all of it keeps half the gap from that plane, at the latest at the end of the flange it stands on, which can leave a short stretch of that flange’s edge bare. Returns turned opposite ways pass each other. A return that meets no other at a corner ends with the flange it stands on; where the flange beside that one leans in over it, the return is cut back until it keeps as clear of that flange as the end of the flange it stands on does. Returns that can’t be closed stay open with a warning, as on the base. Returns of separate features don’t close toward each other either: make the returns round a corner in one feature. A corner relief or a tear changes none of this: it leaves the flanges’ closed ends as they are, so the returns on them close over a relieved or torn corner as over a whole one.

{"command":"sheet.flange","params":{"edges":[[60,2,20],[118,45,20],[60,88,20],[2,45,20]],
 "height":12,"angle":"90 deg"}}

(The four returns of a 120 x 90 tray in 2 mm sheet with 90° walls 20 high: each point is the middle of a wall’s far edge on its inner face, so the returns turn in over the base.)

A flange can stand on a straight side edge of an existing flange as well. Select that edge and the adjoining edge of its supporting panel in one feature to close the new flanges across the existing bend. The panels are mitred with the rule’s gap; round and rectangular corner reliefs cut a rounded or rectangular slot across the existing bend zone in the flat material. The slot’s width is corner_relief, and it survives unfolding, refolding and DXF export. notch keeps the gap without another cut. A tear across an existing bend is not supported yet and leaves the notch with a warning. A mitre that would overlap the flat pattern stays open with a warning. Side-edge setbacks on curved panels or on only part of an edge left by a cut-out are not supported yet. The edge connecting a panel to its existing bend is not a free edge. See examples/regressions/sheet-side-flange-corner.json for a small reproduction.

Convert also takes a solid or a shelled body: pick the face that stays fixed and the sharp edges that become bends (bends; Bend Edges in the dialog). The flat faces reached from the fixed face across those edges become the sheet. A shelled body keeps its walls and their thickness; any other solid’s faces become the outside of the sheet, with the rule’s thickness taken into the body. Each sharp edge stays where the faces of its bend would meet, so the part keeps its envelope and the flat pattern is the outside lengths less a bend deduction per bend, 2·(R + T)·tan(θ/2) − θ·(R + K·T). A bend can be picked on either skin of a shell; bends turn either way, follow one another and take any angle (the walls of a tapered tray at 72°). An edge between two faces of the sheet that is no bend is cut open, each side half the rule’s gap from the plane halfway between the two, like the corners of a tray. A bend drawn as a chamfer (a flat strip between two faces, the chord of the bend’s arc, as models without bend radii draw one) is picked in the middle of the strip: the two faces then meet at their virtual sharp in one bend of the rule’s radius, and the strip is no face of the sheet. A bend modelled with its radius (a cylinder between two flat faces tangent to it, as sheet metal parts from other systems come, or a filleted edge) is picked anywhere on its cylinder, on either skin, and converts the same way: the sheet is bent there with the rule’s radius and K-factor, not the modelled radius, so the flat pattern is the one the rule makes. The result is an ordinary sheet body: edge flanges, cuts, Unfold, the flat pattern and its exports work on it, and it follows the solid when a parameter changes (the edges and the fixed face are found by their names). Without bend edges Convert expects a plate, as before. Either way round walls (holes, slot ends, rounded corners, notches) stay circles and arcs in the flat pattern and the DXF, also where a STEP file writes one in pieces, and the holes and slots of every face of the sheet are its cut-outs, where the bends put them. An engraving in a face of the sheet (a recess with a flat floor, at most half the thickness deep) becomes a pocket of it, as a plate’s does (below), on either skin of a shell. Not converted yet: curved faces other than bends (rolled sheet), with bend edges a hole or an engraving that reaches into a bend or a pocket deeper than half the thickness (cut them after converting), and bends that close a ring.

A plate may carry recesses in one of its faces, such as an engraved label or a counterbore: a recess with a flat floor and walls square to the plate, at most half the plate’s thickness deep. Each becomes a pocket of the sheet (see the pockets below), so the converted part keeps it: the sheet is as thick as the plate, the flat pattern is the whole plate with the recess listed under pockets (its outline, side and depth), and the DXF draws its outline on the pocket layer of its depth, apart from the cut contours, for a laser to mark rather than cut. The face the recesses are in can be the one picked or the other. A recess deeper than half the thickness, recesses in both faces, and a plate whose recesses are larger than the face around them (a thinner plate with raised parts) are refused with the reason. A recess lies in an opening of the face it is cut into, so the faces of a bent part’s legs are none: a bent part is refused as no plate, and converts with its bend edges. A bend across a pocket is refused as for any pocket, so fold or flange clear of an engraving.

{"command":"solid.box","params":{"length":100,"width":60,"height":30,"body_name":"Tray"}}
{"command":"solid.shell","params":{"faces":[[50,30,30]],"thickness":2}}
{"command":"sheet.convert","params":{"body":"Tray","face":[50,30,0],
 "bends":[[50,0,0],[100,30,0],[50,60,0],[0,30,0]]}}

The face can also be the name model.faces lists for it ("face":"F1:+z"), which saves computing a point on a face with holes or a curved outline: model.faces gives every face a point that lies on it.

For a shop drawing or spreadsheet, sheet.export_bend_table returns CSV by default; choose format: "json" for structured data. It includes the sheet rule, thickness, bend angles, radii, allowances and flat centre-line coordinates. Each bend has its bend angle (angle_deg) and its included angle (included_angle_deg, below; the CSV’s last column, so the columns before it keep their places). Lengths use millimetres and angles use degrees. A bend is listed where its centre line has material: one that a cut-out has shortened gives the length and ends it has left, and one that cut-outs split gives a row for every piece, all with the bend’s number (the flat pattern and the DXF draw the same pieces as bend lines). Supply path to save it, or omit path to receive the content through the command response. body defaults to the last sheet body. Exporting does not add a timeline entry or consume an undo step.

{"command":"sheet.export_bend_table","params":{"format":"csv","path":"bracket-bends.csv"}}
{"command":"sheet.export_dxf","params":{"path":"bracket-flat.dxf"}}

The DXF is an AutoCAD 2000 (AC1015) drawing in millimetres, made for a laser-cutting shop’s nesting or CAM software: each kind of line is on a layer of its own, with a colour (AutoCAD’s index) and, for the bends, a linetype that the file defines.

LayerColourLineHolds
OUTLINE7 white or blackcontinuousthe outer contour of each piece of the sheet
CUT4 cyancontinuouswhat is cut out inside it: holes, slots, and the loose pieces a cut leaves (the middle of a ring)
TEXT2 yellowcontinuousthe letters that text cut through the sheet, with their counters; only with "text": "cut", the default
ETCH6 magentacontinuousletters as single contours, to be marked and not cut: "text": "etch", and always text machined to a depth
BEND_UP, BEND_DOWN1 red, 5 blueDASHEDthe bend centre lines, by direction
BEND_NOTES8 greycontinuousthe note beside each bend line, only with "bend_notes": true
POCKET_…3 greencontinuouspockets, a layer for each depth (see below)
PERFORATION3 greencontinuousthe holes of a perforation field

OUTLINE, CUT, BEND_UP and BEND_DOWN are always in the layer table, the others when the sheet has something for them. The inner contours (CUT, TEXT) are written before the outline, so a program that follows the file’s order cuts inside-out.

A round hole is a CIRCLE, a circular arc an ARC, a whole ellipse one ELLIPSE and an arc of an ellipse an elliptical ELLIPSE; the pieces of a spline, the outlines of text and the other conics are SPLINEs of their own degree, so no curve is written as chords. For a machine that reads only lines and arcs, pass "curves": "arcs" to sheet.export_dxf: the free-form curves are then tangent arcs and lines within 0.001 mm.

Letters are told from other cut-outs by where they came from: a cut whose sketch holds text (Sketch Text) marks the cut-outs it makes as letters, counters included, while the design is built; nothing more is saved in the design file. A letter that another cut-out overlaps, or that the sheet’s edge clips, has become one shape with it and stays on CUT.

{"command":"sheet.export_dxf","params":{"path":"tray-flat.dxf","text":"etch","bend_notes":true}}

With "bend_notes": true each bend line gets a note on BEND_NOTES, B1 UP 90° R1 (the bend’s number, its direction, its angle and its inner radius; %%d is AutoCAD’s degree sign). It is 3 mm high ("note_height" changes that), centred half a height beside the middle of the line, parallel to it and readable from below or from the right. A bend that cut-outs split has a note for each piece. Notes are off by default, because a program that cuts or marks every layer would mark them.

For a program that cuts every line it is given, "bend_lines": false leaves the bend lines out (BEND_UP and BEND_DOWN are then empty), and "layers": "single" writes every entity on layer 0 instead of a layer for each role.

A bend’s angle is how far the sheet turns from flat. Many press brakes are set by the included angle instead: the angle left between the bend’s two sides, 180° less the bend angle. A 90° bend is 90° either way, but a 58° bend is set as 122°. The rule’s angle_display (Angle Display in Sheet Metal Rules) says which one the DXF’s bend notes give: bend (Bend, the default) or included (Included). Under an included rule the note of a 58° bend reads B1 UP 122° R1. The bend table gives both angles whatever the rule says, in columns of their own (angle_deg and included_angle_deg), so neither is taken for the other. The setting does not change the flange angle yet: angle in sheet.flange and in the Flange dialog is always the bend angle, so a 122° included angle is a flange at 180 deg - 122 deg. A design keeps the setting in its rule, so templates made from it read their bends the same way.

{"command":"sheet.manage_rules","params":{"name":"Sheet 2 mm","angle_display":"included"}}
{"command":"sheet.flange","params":{"edges":[[50,0,2]],"height":20,"angle":"180 deg - 122 deg"}}
{"command":"sheet.export_dxf","params":{"path":"bracket-flat.dxf","bend_notes":true}}

A design with several sheet bodies exports one file for each with "per_body": true: the files are <path>-<body>.dxf (characters a file name cannot hold become _; two bodies that would share a name get _2, _3…), and the reply lists them in files. Without a path it lists their DXF texts instead.

Base-flange profiles keep their interior holes. Through cuts that cross a sheet edge become notches in the flat outline; overlapping and repeated cuts remove their combined area once. The flat preview, DXF and unfolded solid use the same material boundaries. Lines and circular arcs remain exact. An ellipse, a spline or text stays the curve it was drawn as in the flat pattern and the unfolded solid; the folded solid follows it with tangent arcs within 0.001 mm, and the point lists write it as chords within 0.001 mm (the DXF keeps the curve, see above). Flat-pattern JSON lists each cut-out twice, in the same order: as points (cutouts), and exactly (cutout_shapes): a round hole as {"circle": {"center", "radius"}}, any other as its line, arc (centre, radius, start and sweep in degrees) and polyline segments (one polyline for each ellipse quarter, spline or glyph curve).

Cuts can leave separate material pieces, including the centre of a ring cut. These pieces remain one logical sheet group through Unfold / Refold and export as separate flat outlines. A cut that parts a piece from the base (a flange cut off along its bend, the part of a ring’s inside on a flange’s edge) keeps it too, folded or flat, and its warning says how many pieces are no longer joined to the base, their area and where they lie in the flat pattern. Islands inside other material (a ring’s centre, a letter’s counter) are not counted. Large profiles that exceed the geometry processing budget report an error instead of exporting a partial outline.

A Box cut, or a Cylinder cut whose axis is the sheet’s normal, machines the sheet like the extrude of its footprint: through the sheet it is a cut-out (a cylinder a round one), and part way it is a pocket. A primitive cut at any other angle, or across a folded bend, is refused.

Hole machines the sheet the same way from a flat face of any panel, the base or a flange, on either side, folded or unfolded: square to that panel, a through hole (plain or tapped) is a round cut-out of the flat pattern and a CIRCLE in the DXF, and a counterbore or a blind flat-bottomed hole is a round pocket. A hole slanted to its panel or drilled into the edge of the sheet, a countersink and a drill point are refused.

Parallel, untapered extrude cuts also preserve shallow pockets through Unfold / Refold. The pocket floor and side walls remain solid geometry; overlapping cuts from opposite sides become a through hole wherever they remove the whole thickness. Flat-pattern JSON lists pockets with their side, depth and bottom/top thickness coordinates in millimetres. DXF pocket boundaries use POCKET_BOTTOM_Z…_TO_…_MM, POCKET_TOP_Z…_TO_…_MM, or POCKET_INTERNAL_Z…_TO_…_MM layers. These are machining operations, so do not treat every pocket layer as a through-cut contour when importing the drawing into a CAM tool. (Text machined to a depth is a marking, on ETCH.)

Extrude Intersect on a straight base or an unfolded sheet retains the material inside both the current tool footprint and its depth interval, including interior holes, detached islands and earlier pockets. A shallow intersection records the removed layers above and below the retained material as top and bottom pockets. The sheet rule keeps the original stock gauge; the remaining solid can be thinner. A completely empty intersection removes the sheet and its metadata together. DXF records pocket depth in its layer names; importing that DXF as ordinary sketch geometry does not reconstruct the machining depths automatically. Each positive retained or removed depth layer must meet the kernel’s 0.000001 mm extrusion resolution; smaller layers report an error rather than being rounded away. To Object can retain a tool that trims to a straight prism, using its actual trimmed footprint. A curved tool with varying depth is refused before changing sheet geometry. Pocket profiles can move with planar panels when forming, but a pocket crossing a bend zone cannot yet be refolded. Machining folded bend material, tapered sheet cuts and other unsupported 3D sheet Cut/Intersect operations report an error before changing the design; they cannot silently restore removed material during a later sheet rebuild.

Other features that change a sheet body’s material without the sheet knowing it (a fillet or chamfer on a corner, an extrude joined onto it, a Mirror or Combine joined into it) are not in the flat pattern yet. Such a feature warns, and while the body carries its change the flat pattern, the DXF and the bend table are refused with an error that names it, rather than writing a part that does not match the body. A later sheet metal feature rebuilds the body from its sheet features and says that the change is not kept. Moving the body, splitting its faces and cosmetic threads leave the flat pattern as it is.

A sketch can be drawn on a flat face of the sheet: the base or a flange, on either side. Pick the face (or give sketch.create the point plane: {face: [x, y, z]}) and the sketch attaches to that panel instead of to a certified source face. It keeps the face’s name, sheet:<body>:<panel>:<top|bottom> (the panel is base or the flange’s feature tag), and finds the face again whenever the sheet is rebuilt: after a parameter change such as the thickness or a flange angle, through Unfold / Refold, after the file is reopened and when the body is renamed. Sketch coordinates are the flat pattern’s, so a point drawn on a flange lies where the flat pattern and its DXF have it, and on the bottom face y runs the other way, as seen from below. When the face is gone (its flange is suppressed or deleted) the sketch reports a lost attachment instead of moving to another face; Redefine Sketch Plane attaches it to another face. fixed: true still freezes the picked plane in space.

Every face of a folded sheet has a name of that kind, which model.faces lists as sheet_face: <panel>:top and <panel>:bottom, <panel>:rim.<edge> for a rim (a base sketch line’s id, or a flange’s far, side0, side1; rim.bend0/rim.bend1 for a bend’s flat ends and rim.ext0/rim.ext1 where a flange runs on past its bend toward a neighbour), and <flange>:bend.inner / <flange>:bend.outer for a bend’s curved faces. A command that takes a face name takes it too (sheet:Body1:F3.1:top), and finds the face from the sheet itself, so the name stays with that face while the sheet’s sizes change and other flanges come and go. Faces the sheet does not describe (a hole’s wall) have none.

Plastic parts

MODEL → CREATE lists the plastic enclosure features:

  • Boss (plastic.boss): screw bosses with a hole, fillets and optional ribs.
  • Lip / Groove (plastic.lip): the mating lip and groove along a shell’s opening.
  • Snap Fit (plastic.snap_fit): a cantilever snap hook and its catch.
  • Rest (plastic.rest): a pad for a part to rest on.
  • Plastic Rules (plastic.manage_rules, plastic.assign_rule): wall thickness, draft and clearance per material, used as the features’ defaults.

Plastic Rules are in MANAGE → RULES. Rib and web in MODEL → CREATE complete the set. These features are checked against hand-computed geometry.

Import and export

Everything here starts from the File menu at the top left.

The File menu, numbered 1 to 5 The File menu, numbered 1 to 5

1 Open a design (or a STEP, IGES or mesh file as a new one) · 2 Insert a file into the open design · 3 Save · 4 Export: meshes, STEP, a DXF of the view, URDF, JSON · 5 Design properties

FormatReadWrite
.sc (versioned container or JSON design; .solidcraft files from earlier releases still open)yesyes
.json (self-contained plain JSON design)yesyes
STEP (AP203, AP214, AP242)solids, assemblies, units, names, coloursAP242 with names, colours and the component tree
IGES 5.3manifold solids, or trimmed surfaces sewn into solidsmanifold solids with names and colours
FreeCAD .FCStdsketches and features (Pad, Pocket, Revolution, Groove, Fillet, Chamfer, primitives, Mirrored, patterns), else solids, through cadmpeg
Rhino .3dmsolids, through cadmpeg
3MF, STL, OBJmesh bodiesyes
glTF 2.0 (.glb, .gltf with its .bin files)mesh bodies in the node tree’s components, materials as appearances.glb: placed bodies with their colours, metallic and roughness, in metres with Y up
3MF with face tagsas 3MFeach triangle tagged with its B-rep face, exact coordinates
URDFthe assembly as a robot: links with meshes, mass and inertia, joints from the assembly’s joints
DXF, SVGinto a sketchsketches and flat patterns as DXF
PNG, JPEGcanvases and decals

File → Export GLB… (file.export with a .glb path) writes one binary glTF file for web viewers, renderers such as Blender, and game engines. Each body is a node named after it, where the design places it. Its colour, metallic and roughness become a glTF material, and a face with a look of its own gets that look. A body with no colour gets the Render workspace’s grey. The file is in metres with Y up, as glTF requires, through one root node. A transparent body or face, and a body that a render setup gives an image texture or optics, are refused with a message that names the body, since the file would show it wrongly; leave such a body out with bodies. A refused export leaves a file already at the path as it was.

Opening a STEP, IGES, FreeCAD or Rhino file creates a design with the file’s bodies as a base feature, and inserting one adds them to the current design. Later features build on them. A FreeCAD file’s sketches and features are rebuilt as SolidCraft features instead, so its dimensions can be edited; when one cannot be, its bodies come in as a base feature and a warning says why (Preferences › General › FreeCAD files turns the rebuild off). Mesh bodies render, measure, move and export, but solid features need B-rep bodies.

The stand-up offer for STEP and IGES files remembers your answer for unchanged files; Preferences › Design lets you turn this memory off or forget all remembered answers.

Reopening an unchanged STEP or IGES file reuses its untouched import from a local cache, keyed by the file’s SHA-256, import options and application build. It uses the same lossless, validated model cache as saved designs; a missing, damaged or incompatible entry imports again. Later edits and stand-up rotations are never stored in it, and your remembered stand-up answer still applies when you reopen. The cache keeps at most 512 MiB and 500 entries, removing the oldest first, in SolidCraft’s data folder across updates. Preferences › Design › Reuse imports I’ve opened before is on by default; turn it off to import every time, or use Clear beside it to remove cached imports without forgetting stand-up answers.

Every imported file (STEP, IGES, 3MF, STL, OBJ or glTF) comes in as a new component named after the file, so its bodies never mix with yours. A STEP assembly keeps its structure: its parts and sub-assemblies become nested components, and a part used several times is one component with an occurrence for each place. The imported assembly is rigid: moving its component moves everything, and its parts don’t move on their own. Right-click the component and choose Placement → Unlock Contents to joint the parts one by one. Undo removes a whole import in one step.

A glTF file (.glb, or .gltf with the .bin files it names in the same folder) comes in the same way: its nodes become nested components, a mesh placed several times is one component with an occurrence for each place, and the whole import is rigid until you unlock it. glTF is in metres with Y up, so the import is scaled to millimetres and turned to stand Z up (up: "z" keeps the file’s axes). Each material’s base colour, metal and roughness become the body’s appearance, so the model looks right in Render; textures, skins and animations are not imported. Scaled or mirrored nodes are baked into their meshes. Compressed meshes (Draco, meshopt) are refused with a message: save the file uncompressed first.

A 3MF project saved by a slicer comes in too, also when it keeps its objects in model parts of their own inside the file (the 3MF Production extension): each object on the build plates becomes one mesh body, with all its volumes merged where the slicer placed them. Each body is named after its object: the name in the model, else the one the slicer keeps in its project settings (Bambu Studio’s Metadata/model_settings.config), else Mesh1, Mesh2, … Other slicer-only data (modifier, negative and support volumes, painting, per-object settings) is not read: modifier and negative volumes come in as ordinary solid geometry.

File → Export STL… and File → Export 3MF… (also Save As Mesh on the Insert tab’s Files panel, and in the right-click menu of a body) open the same dialog with that format chosen; it saves the selected bodies as STL, 3MF or 3MF with face tags (below) to a file you name, and you can change the format there.

A 3MF has one object per body, so a slicer arranges the bodies of a multi-body part apart on the bed. Scripts and the control channel can keep each component together: file.export or file.save_mesh with objects: "component" writes every placed component that holds two or more bodies as one 3MF object whose parts are those bodies. PrusaSlicer, OrcaSlicer, Bambu Studio and Cura then load it as one object with a part per body, and each part can take its own filament. The design’s own bodies, and a component with a single body, stay objects of their own.

File → Send to Slicer (also on the Insert tab’s Files panel, and in the right-click menu of a body) is the short way: it saves the selected bodies as a 3MF in millimetres, without face tags, and sends it to a slicer. It always asks which one and pre-selects the slicer you used last time, so the usual case is one click or Enter. The list shows the slicers found on the computer with their icons; Custom… adds any other program to the top of the list (kept between runs; hover an entry you added and press Delete to remove it). On Linux only, a program whose desktop entry says it opens 3MF or STL files is listed even if SolidCraft doesn’t know it by name. Windows and macOS list the known slicers only, found in the usual program folders and on PATH. It falls back to an STL if the 3MF can’t be written. Scripts and the control channel use file.send_to_slicer with a slicer, which sends without a dialog. They can start only the slicers in the list and programs you added under Custom…; any other program path is refused, and an AI agent’s request to save a mesh or send it to a slicer asks first and names the slicer.

A body can have a print face: the flat face it should lie on in the slicer. Select the face and press Ctrl+B (or Print Face in the face’s right-click menu); press it again to clear it. The Send to Slicer window shows the print face of each body being sent in its Print face row. A face you pick there in the viewport becomes the print face of its body, in place of the one it had; take a face out of the row to clear its body’s. Each send then turns a copy of the body so the print face points down at Z = 0, with its centre of mass over the origin; the design itself does not move. The face is tinted and outlined in the viewport, and a copy made with Move/Copy or Mirror keeps it. A print face that is gone or no longer flat is reported, and the body is sent as it lies. Curved faces and mesh bodies can’t be print faces. Scripts and agents can write the same laid-down copies to an STL or 3MF file without a slicer: file.export_stl, file.export_3mf, file.export and file.save_mesh take lay_on_print_faces: true, and the reply names the bodies that were laid on their print face. Known limitation: after a shell of a body that is already shelled, no face of that body can be marked, as no sketch can be attached to one either; this needs kernel work in solidcore.

The list also has a plain EasyPrint (Printables, in the browser) row, and it is the one entry that works in the browser build too. EasyPrint is Printables’ slicer, so choosing it uploads the selected bodies (a 3MF, 100 MB at most) to Printables’ servers and opens the model there in your browser. The link expires after 30 minutes, and you need to be signed in to Printables. SolidCraft asks once before the first upload and remembers that you agreed. Nothing is uploaded unless you pick this row. SolidCraft isn’t affiliated with Printables. Scripts use file.open_in_easyprint, which refuses unless it gets consent: true, and an AI agent’s request to use it asks first.

Files sent to a slicer go to a folder only you can read: ~/.cache/solidcraft/slicer on Linux, ~/Library/Caches/SolidCraft/slicer on macOS and %LOCALAPPDATA%\SolidCraft\slicer on Windows. The name stays the same for the same design and the same bodies, so sending again replaces the file the slicer has open: use the slicer’s Reload from disk to pick it up. If Windows won’t let the file be replaced because the slicer holds it, the new copy gets a number (name-2.3mf) and the status line says so. Files older than seven days are deleted the next time SolidCraft starts.

If the slicer is already running, PrusaSlicer, SuperSlicer and Cura load the file in the open window instead of starting another one (they are started with --single-instance). Other slicers, Bambu Studio and OrcaSlicer among them, are started as they are, so they may open a second window. You can turn this off in Preferences → General, except for PrusaSlicer 3.0, whose launcher can’t hand a file to a running window without it. Scripts pass single_instance: false to file.send_to_slicer or file.save_mesh.

PrusaSlicer 2 and 3 behave differently, so SolidCraft reads which one an install is from the version in its installed metadata (a Flatpak’s own files, or the metainfo file of a package); it never runs the program to ask. PrusaSlicer 2 is sent the file with --single-instance. PrusaSlicer 3 is started on its own first, SolidCraft waits up to ten seconds for it to be ready, and then sends the file. If the version can’t be read, the command decides: the slic3r-app-launcher launcher is treated as PrusaSlicer 3, and any other entry is left as it is.

Closed meshes can be rebuilt as solids with the mesh.convert command (command palette, scripts and the control channel): a mesh with a constant cross-section as analytic planar and cylindrical faces, any other closed mesh as the faceted solid its triangles describe.

File → Export 3MF… with the format changed to 3MF with face tags (file.export or file.save_mesh with format: "3mf-tagged") writes an ordinary 3MF in which every triangle also names the B-rep face it lies on and every coordinate is the tessellation’s own double-precision value. Meshing and analysis tools can use the tags to keep a model’s faces (for loads, boundary conditions or feature edges); other 3MF readers ignore them. The format is specified in docs/formats/3mf-face-tags.md.

File → Export URDF… (file.export with a .urdf path) writes the assembly for robot simulators and ROS: a link per component placement with its mesh, mass and inertia, and the assembly’s joints, so the parts move in the simulator as they do in SolidCraft. The meshes are written as STL files next to the .urdf. See docs/urdf.md.

Saves are atomic: a crash at any moment leaves either the old file or the new one, and the replaced version is kept as <file>.bak. A save keeps the file’s permissions, and a design opened through a symbolic link (a synced folder linked into a project) is saved into the file the link points to, so the link stays. Unsaved changes in every open tab are autosaved to separate entries in a recovery folder, and the next launch offers to recover each design. Saving or closing one tab removes only its recovery entry. Older design files are upgraded on open, and files from a newer version open with a warning.

Save writes a container by default; files named .json keep JSON on subsequent saves. Save As offers a container or plain JSON on desktop; Save As JSON is also available in the File menu on desktop and web. Both start from the name of the open file, or from the design’s new name when it was renamed since it was opened or saved; a new design starts from its own name. Choose the .json extension for plain JSON. Export Design as JSON writes an independent, self-contained copy without changing the current file. Scripts can explicitly pass "format": "json" to file.save or file.save_as even with a .sc name. Opening detects the format from the contents, so existing JSON designs keep working. Containers favor fast reading and saving by default. Scripts can request "compression": "compact" when file size matters more than CPU time.

Containers deduplicate identical STEP, mesh and image payloads. Mesh coordinates retain their original double precision; this is not a conversion to STL. Geometry still rebuilds from the timeline when opening. When a saved display preview is available, desktop Open shows it while checking geometry; editing starts after the complete rebuild. Cancelling keeps the current design. Previews cannot be edited or measured. Scripts can opt in to a disposable geometry cache with cache: true on warm saves; saving never rebuilds cold geometry just to create one. Ordinary saves and browser downloads default to cache: false. An additional display-mesh cache remains opt-in with preview: true. Unsupported or mismatched caches are ignored. Browser opening still rebuilds synchronously. Warm saves with explicit cache: true can also include a small PNG thumbnail. It travels with the file, so Home can show the recent design without reconstructing it when no local picture is available. Missing or incompatible pictures use the usual thumbnail generation. The default cache: false omits the portable thumbnail too. The repository’s docs/design-container.md describes limits and encoding details.

Designs are saved in the newest format this version of SolidCraft knows ("format": "solidcraft/N" in the file). It opens designs of every older format and upgrades them as they open. A design from a newer version opens too, with a notice saying so: what this version can read is read, and a feature it can’t read stays on the timeline with an error saying so, so you can see what is missing. Saving such a design keeps what this version can’t read and the file’s format number, so the newer version finds its work again. Development (nightly) builds save a format number no release reads yet.

Sketch patterns retain their live direction-line references and placement settings when saved. Older patterns keep their fixed vector directions and original placement defaults.

Upgrading keeps what a design meant. Designs keep recorded body transfers, explicit 3D curve coordinate spaces and certified face bindings, alongside semantic configuration styles, associative free-form offsets and cylindrical emboss selections. Older ownership maps, geometry and expressions retain their saved meaning. Legacy 3D curves with retained associations receive their producer’s coordinate-space marker without changing any saved points; detached curves retain world coordinates. A lost cross-component association refreshed by an older build may need an explicit relink or refresh when its source returns. Loading never guesses a numerical correction. Association records, coordinate arrays and placements are bounded and checked.

Large files

SolidCraft opens STEP, IGES and the other CAD files it imports up to 512 MB, and design files up to 256 MB. A larger file is refused before it is read, and the message says how to raise the limit. In the desktop app you can raise both:

  • Preferences › General › Largest file to open sets each limit in MB, from its default up to 16 GB (16384 MB). It is kept between runs; Restore page defaults on the General page puts back 512 MB and 256 MB.
  • --max-file-size SIZE at launch (solidcraft --max-file-size 2GB big-assembly.step) raises both limits for that run only, over the preference, without saving anything. SIZE is a number with MB or GB (2GB, 1.5GB, 1500MB; 1 GB is 1024 MB), at most 16GB. A size below a default leaves that limit at its default. A size the app can’t read stops it from starting, with the reason. solidcraft-cli takes the same flag with any command.

Before you raise them:

  • Memory: a file is read whole, and what is built from it is held in memory beside it. Expect several times the file’s size in RAM, more for finely detailed parts, and a long open and a slow view for an assembly with hundreds of thousands of faces.
  • Saving: a design keeps the text of a STEP import, compressed, and the design limit applies to the saved file. An import whose compressed text doesn’t fit in a design file is refused when it is imported, so it never opens and then fails to save; raise the design file limit too, or open the file with --max-file-size, which raises both. Opening that design again needs the same limits.
  • Web app: the browser version keeps 512 MB and 256 MB: a browser tab can’t hold larger files. Its Preferences show a note instead of the setting.

The control channel sets the same preference with ui.prefs {"max_step_file_mb": 2048, "max_design_file_mb": 1024}.

Export file names

Preferences → Design → Export file name sets the name that File → Export, Save As Mesh and a body’s right-click Export… offer in the save dialog. The setting is a template. Each placeholder in it is replaced:

PlaceholderReplaced with
{design}The design’s name
{body}The exported body’s name. When an export holds several bodies, the first one
{project}, {part_number}, {revision}, {author}, {organization}, {description}, {license}That design property

The help icon next to the setting lists the same placeholders. A design property that is not set is left out. Other text in braces stays as typed.

With {part_number}_rev{revision}, a design with part number RV-102 and revision C is offered as RV-102_revC.step. With {design}-{body}, the body Base of the design Bracket is offered as Bracket-Base.stl. Characters that file names cannot hold (/ \ : * ? " < > |) become _. A name that Windows keeps for a device, such as CON or NUL, gets a _ in front. A long name is cut to 200 characters and 240 bytes, so it fits with its extension. Left empty, File → Export offers the design’s name, and Save As Mesh and a body’s Export… offer the body’s name. The control channel sets it with ui.prefs {"export_name": "{design}-{body}"}.

Design properties

File → Design Properties… (also under Document Settings in the Browser, and on the top component’s right-click menu, under Design) records who made a design and what it is: project, part number, revision, author, organization, description and licence. OK applies the changes as one undo step. Scripts and agents use the same commands: document.metadata reads them, document.metadata.set (key and value, or values: {key: value, …}) and document.metadata.remove (key or keys) change them. document.inspect and the MCP inspect_design tool include them.

Exports carry them where the format has a place for them:

FormatWritten
STEPauthor and organization in FILE_NAME, description in FILE_DESCRIPTION (on one line)
IGESauthor and organization in the global section
3MFproject as Title, author as Designer, description as Description, license as LicenseTerms

The part number and revision have no standard place in these formats and stay in the design.

In the file they are one optional top-level metadata object; a design without properties has none, and is saved exactly as before:

"metadata": {
  "project": "ACME enclosures",
  "part_number": "ACME-1042",
  "revision": "B",
  "author": "Jane Doe",
  "organization": "ACME",
  "description": "Vented case for Raspberry Pi 4",
  "license": "CC-BY-4.0",
  "com.acme.description": { "shop_id": 88123 },
  "org.printfarm": { "slicer_profile": "PETG-0.2", "internal_id": 4 }
}
  • SolidCraft’s keys are unprefixed and hold text. Every other unprefixed key is reserved for later SolidCraft versions.
  • Other tools keep their data under one reverse-DNS key of their own (com.example.tool: two or more dot-separated labels of letters, digits, - and _), holding an object. A tool writes only under its own key and leaves the others alone.
  • SolidCraft keeps every key as it is through open, edit and save, in both the container and plain JSON, and never checks what another tool wrote: a well-known key holding something other than text ("author": 42) is kept, reads as absent, and never stops the design from opening. Typing over it in Design Properties replaces it.
  • Other tools’ keys are never exported; Design Properties lists them read-only.
  • Keep values small: document.metadata.set refuses text over 4 KiB, an object over 64 KiB or nested more than 32 levels deep, and more than 256 keys or 256 KiB in all. Binary data belongs in a file next to the design.

Design properties are first saved in format 21. Inserting or linking another design doesn’t copy its properties; Save As keeps them. A command script of the design (timeline.export_script) sets them in one step.

Designs in shared or synced folders

A design kept on a network share or in a synced folder (Dropbox, OneDrive, Syncthing, Google Drive) can be open on two computers at once. The desktop app watches for two writers:

  • Open elsewhere. While a design is open, SolidCraft keeps a small lock file next to it, .~<file>.lock (computer, user, SolidCraft version, when it was opened), refreshes it every minute and removes it when the design closes. Opening a design that another SolidCraft has open says so: Open Read-Only (look at it; Save asks for a new file), Open Anyway or Cancel. A lock not refreshed for three minutes (left by a crash or a lost connection) is ignored. Lock files never show in Open or the recent lists, and a design in a folder you can’t write to still opens.
  • Changed on disk. SolidCraft remembers the file’s size and modification time when it opens or saves a design. If another program or computer wrote the file since, Save asks first: Save a Copy (the default), Overwrite or Cancel.

Scripts, the CLI and the control channel never get these questions. doc.open opens the design and reports open_elsewhere in its result (read_only: true opens it read-only), and file.save over a file that changed on disk fails with an error until it is given overwrite: true.

Saving in the web app

In Chrome, Edge and Opera, Save As opens your browser’s save dialog and writes the .sc file to the folder you choose, and Open opens the browser’s open dialog. Save then writes back to that same file. The browser remembers the file, so after you reload the page the first Save asks once for permission to write to it, and carries on. A design you open from your computer saves back to the file you opened.

In Firefox and Safari, which can’t write to a folder, Save and Save As keep designs in this browser’s private storage instead, and the app says so. Clearing the site’s data can remove those designs. Download a copy to keep it outside the browser or move it to another device.

To get a copy as a download in any browser, choose File → Download design. It holds the current design, including its unsaved changes. In File → Files in this browser… (and in Firefox and Safari, File → Open), Download open design does the same, and Download beside a stored file gives its last saved contents. Whether a folder chooser appears depends on your browser’s download settings.

Use Open from computer… in the same window to open a design from your computer; where the browser can write to a folder it saves back to that file, otherwise it works on a copy kept in the browser. Use Rename beside a stored file to change its name. Autosave and recovery stay in the browser’s private storage in every browser. Exports such as STEP and STL are downloaded.

By default, closing or reloading the tab while a design has unsaved changes shows your browser’s own “Leave site?” box. To keep your work instead, turn on Preferences → General → Saving → Keep unsaved work in this browser. SolidCraft then keeps a copy of each open design’s unsaved changes in this browser’s private storage, updated about a second after each change and again when the tab is hidden. Turning it on alone doesn’t skip the box: it is skipped only when Still ask when the tab closes is also off and every kept copy is current. A change made in the last moment before closing can still bring the box up while its copy is written. On your next visit, SolidCraft asks Restore unsaved design: , changed (one window lists them all when there are several). Restore opens the design with its changes, still unsaved, Open saved version opens its file as last saved, and Discard drops the copy. A copy older than the design’s last save isn’t offered. Closing a design inside SolidCraft (its tab’s ×, File → Close or Ctrl+W) still asks Save changes to ?, and Don’t Save drops its copy.

Keyboard shortcuts

Command shortcuts can be changed in File › Keyboard Shortcuts; these are the defaults. Click a key to replace it, or the + after it to give the command another key: every key it has runs it, and menus and tooltips show the first one.

Click a binding to record a new shortcut. A shortcut can contain Shift, Ctrl/Cmd and Alt/Option modifiers plus exactly one other key. Hold the modifiers, then press the final key; you can take your time assembling the modifiers before that key. The recorder also allows modifiers to arrive after the ordinary key: it previews the chord and waits one second after the last added modifier, and until all keys are released, before confirming. For example, record Shift+Cmd+M for Sketch Mirror on macOS. Escape cancels recording without changing the old binding. Pressing two different ordinary keys cancels the attempt.

The binding button shows modifiers immediately as you press them, even before adding the ordinary key. Releasing a modifier before that key removes it from the preview. A status line tells you to add a key, release all keys, or wait for confirmation; the proposed chord stays visible during confirmation. Nothing is saved while the preview is still pending.

On macOS, the editor, Help list, toolbar hints, context menus and S search display Cmd and Option. Existing settings retain SolidCraft’s Ctrl/Cmd alias for the primary modifier; Control and Command pressed together are recorded explicitly as Ctrl+Cmd. Conflicting bindings still require confirmation, and built-in reserved shortcuts remain reserved. Recording changes app settings, not the design file.

This follows the usual modifier-before-key convention documented by Apple and the key-event and modifier-state model documented by W3C. The one-second grace period is SolidCraft’s recording aid, not a standard-mandated delay; using an already assigned shortcut has no added delay. Operating systems and browsers can intercept reserved combinations before SolidCraft receives them. Choose a different chord if that happens; recording is cancelled if the app loses focus.

KeyCommandKeyCommand
LLineEExtrude
RTwo-Point RectangleQPress Pull
CCenter Diameter CircleFFillet
DSketch DimensionHHole
XConstructionMMove/Copy
OOffsetJJoint
TTrimIMeasure
PProjectAAppearance
Ctrl+ZUndoCtrl+YRedo
Ctrl+NNew DesignCtrl+OOpen
Ctrl+SSaveDeleteDelete the selection
Ctrl+Shift+SSave As
Ctrl+1Front ViewCtrl+2Back View
Ctrl+3Left ViewCtrl+4Right View
Ctrl+5Top ViewCtrl+6Bottom View
Ctrl+7Isometric View

L, R and C start their tool family (lines, rectangles, circles) in the variant you used last, as the family’s toolbar button does: after a Midpoint Line, L starts a Midpoint Line again. Pick another variant from the button’s drop-down (or the Sketch Palette) and the key starts that one from then on. The choice is kept across designs and restarts. With Pressing a tool’s shortcut again switches its mode on (Preferences › Sketch), pressing the key again before the first click steps to the family’s next variant.

One key can be bound to a sketch tool and to a model command at once: in a sketch the key runs the sketch tool, outside one the model command. Outside a sketch, a sketch tool’s key opens Create Sketch and starts the tool in the new sketch. By default a model command’s key pressed in a sketch still runs it, and one that builds a feature (E, Extrude) finishes the sketch first. With Keep model keys out of sketches on (File › Keyboard Shortcuts), a sketch and the model keep their keys apart: while editing a sketch only sketch keys and keys that work everywhere (Measure, the views, the file commands) run, and a model key such as E or F does nothing, so a stray key can’t close a heavy sketch.

Keys handled by the window itself:

KeyAction
Scommand search under the ribbon search field
Vshow or hide the selected bodies, sketches and planes; with a right-click menu open, its Show/Hide (a folder, the Origin folder, an origin plane)
F2rename the selected browser or timeline item; with several bodies selected, all of them: the first takes the name, the others name (2), name (3)…, as Rename Selected Bodies does in a selected body’s menu
F6fit the view to the model
← → ↑ ↓turn the view 15° (with Shift, a quarter turn)
Ctrl+← → ↑ ↓pan the view
PageUp / PageDownzoom in / out
+ / −zoom in / out
Ctrl+8look straight at the selected face, plane or sketch
Esccancel the running command, or clear the selection

While a value box has the keyboard (a dimension being added, a dialog field), keys type into it and single-letter shortcuts don’t fire. With Sketch Dimension (D), the box opens as soon as you click a line, circle or arc (or the second point or line), so D, click, type the value, Enter. With Preferences › Design › Step values with the arrow keys and the wheel on, ↑ ↓ in a value box, or the wheel over it, step a plain number by 1 (with Shift by 10, with Alt by 0.1); see Parameters and expressions.

Mouse and touch:

ActionResult
Clickselect (Shift or Ctrl adds)
Click again on the same spotselect the next thing under the cursor (click cycling)
Hold the left button still (long-press on touch), or right-click › Select Other…list everything under the cursor to pick from
Double-click a sketch curve or edgeselect its chain or tangent loop
Right-click in the view with a dialog opengo on to the dialog’s next empty selection box; once every box has something, OK. An empty box the feature needs stays, and the status line says what to pick (off by default; turn it on in Preferences › General › Dialogs › Right-click confirms dialog inputs)
Double-click the middle buttongo Home: the home view, framing everything shown (Preferences › Navigation › Middle double-click: Home, Fit or Off)

To zoom by dragging, assign a key to Drag to Zoom in File › Keyboard Shortcuts. Hold the shortcut, then drag up or down with the left mouse button in the viewport. Releasing the shortcut stops the zoom. The Zoom button in the navigation bar also shows the assigned shortcut.

Help › Keyboard Shortcuts in the app lists the current bindings, including any you changed.

With the Touchpad navigation preset, hold Shift while moving the pointer to pan, or Alt/Option to orbit, without pressing a mouse button. Vertical scrolling zooms. These viewport gestures are separate from rebindable command shortcuts; mouse-button selection and editing keep their existing modifiers.

Sketch modifiers

While drawing or dragging a sketch point:

KeysAction
Ctrl/Cmd or Alt/Option (default)suppress auto-snap for that gesture
Shiftprefer a line midpoint when targeting (skips a curve-curve crossing)

Change the snap-suppress chord in Preferences › Sketch. Selection still uses Shift and Ctrl/Cmd to add or toggle (Preferences › Selection can make Shift add-only).

Updates and privacy

SolidCraft tells you when a newer release is out. It never updates itself without asking, and the check sends nothing about you or your designs.

What you see

When a newer stable release exists, a small accent-coloured pill reading Update to 0.14.0 (the new version) appears in the title bar next to Report a bug. Nothing pops up by itself: clicking the pill opens a small window with the new version, its release notes and three choices. In a narrower window the pill shortens to Update so the document tabs keep their room, then to its icon alone.

  • Download opens the file that fits your installation (see the table below) in your browser, or the release page when there isn’t one.
  • Skip this version hides the pill until an even newer release comes out.
  • Remind me later hides it for three days.

SolidCraft checks every time it starts, so a release that came out since you last opened it shows up straight away, and again once a day while it stays open.

Preferences ▸ General ▸ Updates has Check for updates automatically (on by default) and Check now, which checks right away and shows the window even for a version you skipped.

Help ▸ Check for updates runs the same check, including from the macOS menu bar. The window shows progress, offers the installation or download when a newer version exists, or says SolidCraft is up to date. If the check fails, it shows the error instead. Manual checks work even when automatic checks are turned off; builds that cannot check explain that in the window.

Updating in one click

Where SolidCraft can replace itself safely, the window also has Update now:

Installed asUpdate now does
AppImageDownloads the new AppImage next to the old one, verifies it, renames it over the old file and offers Restart now; a file named like a release (solidcraft-<version>-linux-<arch>.AppImage) takes the new release’s name, so tools such as Gearlever keep finding its update data, and any other name is kept
Windows installer (.msi)Install (closes SolidCraft): downloads and verifies the .msi, closes SolidCraft (asking about unsaved designs first) and starts the installer
Windows portable .zip or .exeDownloads and verifies the package, swaps in the new solidcraft.exe (and solidcraft-cli.exe) and offers Restart now; the old file is removed at the next start
macOSDownload and open: downloads and verifies the .dmg and opens it; quit SolidCraft, then drag the new SolidCraft to Applications to replace the old copy (macOS won’t replace an app that is running)
Flatpak, Snap, .deb, .rpm, .tar.gzNothing: your package manager updates these (the window shows the command), or you unpack the new archive

Every download is verified before anything changes: its SHA-256 must match the release’s SHA256SUMS.txt, fetched over HTTPS from the same release, and, once the project signs its releases, that list must carry a valid signature by SolidCraft’s release key that names the release (see releasing). A download that doesn’t verify is deleted and the installed copy is left as it was; Download still gets the package for a manual install. On macOS SolidCraft opens the disk image rather than replacing the app itself: you drag the new app to Applications, as you did the first time. The app and the disk image are signed with a Developer ID and notarized by Apple, so macOS opens the new copy without a warning.

Update now needs write access to the installed file: an AppImage in a folder you can’t write to fails with a message, and Download is the way to update it.

Updating from 0.11 to 0.14

SolidCraft 0.11.0 to 0.14.0 only accept packages from the project’s old GitHub address. Since the repository moved to github.com/tangency-labs/solidcraft, every release names the new address, so these versions see that a newer release is out but find no package in it: Update now can’t install it, and Download opens the release page.

Download 0.15.0 or later once yourself, from the release page or the download links, and install it over the old version as you first installed it. From 0.15.0 on, SolidCraft accepts either address, and Update now works again. Package managers (Flatpak, Snap, WinGet, .deb, .rpm) aren’t affected.

What is sent

One HTTPS request each time SolidCraft starts, and once a day while it stays open:

GET https://api.github.com/repos/tangency-labs/solidcraft/releases/latest
User-Agent: SolidCraft-update-check
Accept: application/vnd.github+json
X-GitHub-Api-Version: 2022-11-28

Update now then downloads SHA256SUMS.txt (and its signature) and the one package from the release’s own github.com/tangency-labs/solidcraft/releases/download/… links, with the same User-Agent. That’s all: no version number, no machine or user identifier, no usage data, no cookies. GitHub sees the request like any other visit to the API (your IP address, the time); see GitHub’s privacy statement. SolidCraft compares the answer with its own version on your computer.

  • Offline, or GitHub doesn’t answer within 15 seconds: nothing is shown, and the check is tried again an hour later (or at the next start, whichever comes first).
  • GitHub’s rate limit for anonymous requests (60 an hour per address) is respected: when GitHub says to wait, SolidCraft waits that long, Check now and the check at start included.
  • Turning off Check for updates automatically stops the checks; only Check now asks.
  • Setting the environment variable SOLIDCRAFT_UPDATE_CHECK=0 turns all checks off, for example on managed machines.

Builds you compile yourself, the Flatpak and the browser app never check, and Preferences shows no check controls for them. The browser app updates itself: reload the page to get the latest version. When the network is unavailable, or the update can’t finish downloading in time, the saved version opens instead, and the download continues for the next reload.

Managed by a launcher

When a launcher such as the ArtCraft Launcher installed SolidCraft, it also updates it. SolidCraft notices this in two ways:

  • the environment variable SOLIDCRAFT_MANAGED_BY is set; its value is the launcher’s name, for example ArtCraft Launcher, and
  • the ArtCraft Launcher’s installed.json (in its per-user data folder, read-only) lists this SolidCraft: its recorded path is this executable, this AppImage or .app, or the folder this executable was unpacked into.

SolidCraft then still checks for a newer release, and the pill and Check now work as before, but the window says Update available: update SolidCraft in ArtCraft Launcher and offers only Skip this version and Remind me later. There is no Update now, Download or AppImage replacement, so the launcher’s record of what is installed stays right. Without the variable or a record, nothing changes. Setting SOLIDCRAFT_UPDATE_CHECK=0 still turns the checks off.

Which file Download gets

Releases attach the main packages under fixed names (see releasing), and SolidCraft picks the one matching how it was installed:

Installed asDetected byDownloadHow to update
AppImageAPPIMAGE is setsolidcraft-linux-<arch>.AppImageReplace the old AppImage with the new one
FlatpakFLATPAK_ID is setdoesn’t check (see below)flatpak update io.tangencylabs.solidcraft
Snap from the Snap StoreSNAP is setrelease pagesudo snap refresh solidcraft (snapd also refreshes by itself)
Snap from a downloaded fileSNAP_REVISION starts with xsolidcraft-linux-<arch>.snapsudo snap install --dangerous ./solidcraft-linux-<arch>.snap
.debunder /usr, dpkg knows solidcraftsolidcraft-linux-<arch>.debsudo apt install ./solidcraft-linux-<arch>.deb
.rpmunder /usr, an rpm database existssolidcraft-linux-<arch>.rpmsudo dnf install ./solidcraft-linux-<arch>.rpm
.tar.gzanywhere else on Linuxsolidcraft-<version>-linux-<arch>.tar.gzUnpack over the old copy
Windows installerunder Program Files, or in the folder the installer recordedsolidcraft-windows-<arch>.msiRun the installer
Windows portablesolidcraft-cli.exe beside itsolidcraft-windows-<arch>-portable.zipUnzip over the old copy
Windows .exeanywhere else on WindowsSolidCraft-<version>-windows-x64.exeReplace the old .exe
macOSinside SolidCraft.appsolidcraft-macos-universal.dmgQuit SolidCraft, then drag the new app to Applications

The window shows the command for Snap, .deb and .rpm installations, with a Copy button. The Flatpak has no network permission and Flatpak itself (or your software center) offers its updates, so it never checks. Release builds compare versions as semver: a nightly build counts as the release it was built after, so it’s offered only a newer stable release, and release candidates are offered their final release.

Code signing policy

Free code signing provided by SignPath.io, certificate by SignPath Foundation.

What is signed

The Windows downloads of SolidCraft built from this repository’s release workflow: the app (solidcraft.exe), the command-line tool (solidcraft-cli.exe) and the MSI installer. They are built from the public source in tangency-labs/solidcraft on GitHub-hosted runners, and SignPath verifies that each signed file comes from that build before it signs it. Nothing built elsewhere, and no third-party file, is signed under this certificate.

The macOS app is signed separately with an Apple Developer ID and notarized by Apple.

Team roles

SolidCraft is maintained by Brennen Herbruck, who holds every role:

  • Committers and reviewers: Brennen Herbruck. Contributions from others land only through pull requests that he reviews and merges.
  • Approvers: Brennen Herbruck. Every release signing request is approved by hand before it is signed.

Multi-factor authentication is required on the GitHub and SignPath accounts that hold these roles.

Privacy policy

This program will not transfer any information to other networked systems unless specifically requested by the user or the person installing or operating it.

The one exception you can turn off is the update check, which asks GitHub whether a newer release exists and sends nothing about you or your designs. See Updates and privacy for what it sends and how to turn it off. Features that reach the network, such as the Assistant with an online model, do so only when you set them up and use them.

Troubleshooting: SolidCraft won’t start

0.10.0 closes a few seconds after it starts (Windows)

SolidCraft 0.10.0 crashes at startup on Windows PCs that draw with Direct3D 12 and the system shader compiler. That covers older Intel graphics, PCs without a graphics driver (the “Microsoft Basic Display Adapter”), virtual machines and Remote Desktop sessions. Install 0.10.1 or later, which fixes it. Versions before 0.10.0 don’t have this problem.

Start in safe mode

Safe mode starts with default settings (your own settings stay as they are, for the next normal start), skips the “Recover unsaved design?” offer and draws with the software renderer.

  • Windows: run solidcraft.exe --safe-mode from a terminal in the install folder, or add --safe-mode to the end of a shortcut’s Target.
  • macOS: open -a SolidCraft --args --safe-mode
  • Linux: solidcraft --safe-mode (Flatpak: flatpak run io.tangencylabs.solidcraft --safe-mode)

Setting SOLIDCRAFT_SAFE_MODE=1 does the same. From 0.10.2, if two launches in a row stop before the window opens, the next one starts in safe mode by itself. SOLIDCRAFT_RENDERER=cpu uses only the software renderer and keeps your settings.

A design won’t open or hangs while opening

  • A design that stopped the app while it was being reopened is skipped once. If SolidCraft crashes or hangs while reopening the designs from the last session, the next start does not reopen them: it says which design the last session stopped on and opens the start page instead, so the app stays usable. Open the design yourself if you want it; the skip clears itself either way.
  • Open the previous save. Every save keeps the version it replaced next to it as <file>.bak (see Files). Copy it to a new name ending in .sc, for example bracket-previous.sc, and open that. Work saved after the previous save is not in it.
  • Check the crash reports. If SolidCraft crashed while the design was open, the newest report in the crashes folder (see the table below) usually holds a design.sc with the design as it was. solidcraft-cli crashes lists the reports.
  • Unsaved designs are in the recovery folder, not next to the file.
  • Report it. GitHub doesn’t accept .sc (or .solidcraft) attachments, so zip the file (design.sc.zip) and attach it to an issue, with the crash report if there is one.

Reset the settings

SolidCraft keeps its settings and its data in the folders below. All installed versions share them. No version changes anything outside them: no system settings, drivers or registry entries. Quit SolidCraft, then rename (don’t delete) the folders. SolidCraft starts with defaults and makes new ones. Rename them back to restore your settings, or send them to us with a bug report.

SystemSettings (app.ron, graphics.json)Data (recovery, crashes, control, thumbnails)
Windows%APPDATA%\SolidCraft%LOCALAPPDATA%\SolidCraft
macOS~/Library/Application Support/SolidCraft(the same folder)
Linux~/.local/share/solidcraft(the same folder); thumbnails in $XDG_CACHE_HOME/SolidCraft or /tmp/SolidCraft
Linux (Flatpak)~/.var/app/io.tangencylabs.solidcraft/data/solidcraft(the same folder)

On Windows, paste %APPDATA% or %LOCALAPPDATA% into the Explorer address bar to open the folder. Releases before the rename used SolveCraft folders next to these; SolidCraft only reads them, so you can leave them alone.

  • The recovery folder holds autosaves of designs that were never saved. Rename the data folder rather than deleting it, so you can still open those autosaves (.sc files, or .solidcraft from earlier releases) with File > Open.
  • The crashes folder holds crash reports. Please attach the newest one to a bug report.

From 0.10.2, SolidCraft also checks the settings files at start. A file it can’t read is renamed to app.ron.bad-<time> or graphics.json.bad-<time>, and SolidCraft starts with defaults instead.

“No graphics adapter” or nothing happens at all

SolidCraft needs Direct3D 12 or Vulkan on Windows, Metal on macOS, and Vulkan or OpenGL on Linux. Without a graphics driver it falls back to the system’s software renderer (WARP on Windows, llvmpipe or lavapipe on Linux), which is slow but works. From 0.10.2, if no window can be opened at all, SolidCraft tries the software renderer once and otherwise shows a message saying what failed (earlier versions closed without a word).

On Windows, if the graphics API cannot create the window’s renderer, SolidCraft tries the remaining APIs in order: Direct3D 12 → Vulkan → OpenGL, then software rendering. The status message and Preferences › Graphics say which API failed and why. The saved graphics preference stays unchanged. With an explicit WGPU_BACKEND, a failed start shows the error and exits without switching; change or remove that variable to try another API.

Things to try:

  • Install the graphics driver from the GPU maker (Intel, AMD, NVIDIA) rather than Windows Update.
  • In a virtual machine, turn on 3D acceleration. Over Remote Desktop, try it on the machine itself.
  • Run solidcraft --crash-log to list recent crash reports, and attach the newest one to an issue.
  • On an Intel Mac (Intel Iris graphics, macOS 12), “Parent device is lost” at start comes from the Metal compiler refusing wgpu’s indirect-call validation shader. SolidCraft leaves that validation off on Intel Macs. To change it, set WGPU_VALIDATION_INDIRECT_CALL=1 (on) or 0 (off) before starting; a value you set is always kept.

The desktop stutters or freezes while SolidCraft builds bodies

Some graphics drivers hang on the shader SolidCraft uses to build bodies on the GPU, and the system then resets the GPU. SolidCraft already builds bodies on the CPU on the AMD FirePro D300/D500/D700 (Mac Pro 2013). On another GPU that does this, start SolidCraft with SOLIDCRAFT_CPU_TESS=1 set to build bodies on the CPU, and open an issue with the GPU’s name.

The interface looks blurry on Linux with display scaling

On Linux, SolidCraft runs through XWayland whenever an X display is available, because file drag and drop from a file manager only works there (niri and Hyprland stay on native Wayland when Automatic is selected). A compositor that doesn’t let X11 apps scale themselves draws an XWayland window at 100 % and stretches it to the monitor’s scale, so text and icons look soft. That happens with fractional scaling (125 %, 140 %), and on GNOME 47 (RHEL and Rocky Linux 10) at any scale, 200 % included. The diagnostics in a bug report show it as about 1 physical pixel per point at a scale of 200 %. On Hyprland with force_zero_scaling and GDK_SCALE set, an XWayland UI can instead look ~2.5× oversized relative to the desktop (#10480).

Run SolidCraft on native Wayland instead: Preferences › Graphics › Window system › Wayland, which applies at the next start (Automatic already picks Wayland on niri and Hyprland). Versions without that setting (0.21.0 and earlier) take SOLIDCRAFT_WAYLAND=1 in the environment:

SOLIDCRAFT_WAYLAND=1 solidcraft

On native Wayland, dropping files onto the window doesn’t work; use File > Open or pass the file on the command line. Wayland, XWayland, niri and Hyprland has the details.

Trackpad pinch does nothing on Linux

A Wayland compositor reports touchpad gestures only to native Wayland windows. Through XWayland there is no gesture protocol, so a pinch never reaches SolidCraft at all (two-finger scrolling still works there, because X11 reports it as a wheel). Run SolidCraft on native Wayland (see above) and pinch zooms the view at the cursor, as it already did on macOS and the browser.

SolidCraft closes right away on Windows

A graphics driver can crash the app while it starts, before SolidCraft can show anything (an old Intel Vulkan driver, igvk64.dll, did). Windows’ Event Viewer names the driver under Windows Logs › Application (“Faulting module name”).

From 0.11.1, SolidCraft on Windows starts Direct3D 12 first and loads no other graphics API when Direct3D 12 has a graphics card; Vulkan and then OpenGL are tried only on a computer without one. Earlier versions started Vulkan, Direct3D 12 and OpenGL together and preferred Vulkan.

Choose Preferences > Graphics > Graphics API to select an API for the next start. The page shows the current API separately from the saved choice. Automatic keeps the Windows order above. Vulkan enables GPU path tracing when the graphics device supports it. Direct3D 12 and OpenGL remain available for compatibility.

Use Restart now to apply the saved choice. SolidCraft asks about unsaved designs before it closes and reopens saved designs after restarting. Canceling that save prompt keeps the app open; the selected API remains saved for the next start. Canceling Preferences restores the setting from when the dialog opened.

A newly selected API gets one guarded startup attempt. If the app stops before its first frame is presented, the next normal launch restores the previous choice. A handled startup failure restores it before retrying. A failure to save the guard uses software rendering for that launch. The guard does not detect a blank image or a hang after the first frame; normal device-loss recovery still applies.

Safe mode uses the software renderer without consuming a pending API choice. An explicit launch override still takes precedence and is shown in Graphics. Changing the API in Preferences does not require commands, environment variables or editing settings files.

GPU path tracing is unavailable in a Direct3D 12 session

On Windows, GPU path tracing needs a Vulkan session. SolidCraft does not load Vulkan behind a Direct3D 12 window, because initializing another installed driver can crash the process.

In Render, select GPU path tracing, then Restart with Vulkan in the backend message. Alternatively, choose Vulkan in Preferences > Graphics > Graphics API and use Restart now. Both use the saved setting and the startup recovery described above. CPU image methods remain available.

A successful Vulkan window does not guarantee that every GPU supports the path tracer. If shader setup or a device capability still fails, Render reports that reason. The API switch does not reduce shader compiler memory use.

With OpenGL on Windows, the window has the Windows title bar above SolidCraft’s application bar instead of using the application bar as its title bar. Some OpenGL drivers (older Intel HD and UHD graphics) otherwise show the picture a title bar’s height lower than the window, so clicks land above what they point at.

SolidCraft restarted, saying the graphics device was reset

When the graphics driver stops responding, the system resets it (on Windows, “Display driver stopped responding and has recovered”), and the graphics device SolidCraft draws with is gone for good: the window can’t draw anything more. SolidCraft then closes, keeps your unsaved changes for recovery as it does after a crash, and starts again; the new window says what happened, with what the graphics driver reported, and offers the changes back. The old window closes on its own; SolidCraft doesn’t wait for the driver to release it. If the device is reset again within ten minutes of that restart, the next start uses software rendering, which doesn’t depend on the graphics driver. Updating the graphics driver from the GPU maker usually stops the resets. Laptops that lose the device on waking from sleep (often with an NVIDIA GPU that doesn’t drive the screen) can keep SolidCraft on the integrated GPU instead: Preferences › Graphics.

SolidCraft closed while the GPU path tracer was being set up

Choosing GPU path tracing in the Render workspace first has the graphics driver compile the path tracer, which can take minutes. A few drivers crash while compiling it, and the crash takes SolidCraft with it; nothing inside SolidCraft can catch a crash inside the driver. SolidCraft remembers that setup (gpu-path-setup.json in the settings folder) and doesn’t run it again by itself on that graphics adapter and driver: the Render workspace says what happened and offers Try again. CPU ray tracing still works. A driver update also tries the setup again, and so does deleting that file.

A design saved by 0.10 won’t open in an older version

Designs saved by a newer SolidCraft use a newer file format. Older versions may refuse them with “this design was saved by a newer SolidCraft” and don’t change the file: open them with the current version. Recent versions open them with a notice, and a feature they can’t read shows an error on the timeline; update to build it.

Save As says the file can’t be found (Windows)

If saving into Documents (or Pictures, Desktop and other protected folders) fails with a “File not found” message from the Windows save dialog and nothing is saved, Windows Security’s Controlled folder access is probably blocking SolidCraft. Windows usually also shows an “Unauthorized changes blocked” notification. Either save to a folder that isn’t protected, or allow SolidCraft: Windows Security › Virus & threat protection › Ransomware protection › Controlled folder access › Allow an app through Controlled folder access, then add solidcraft.exe.

SolidCraft and AI agents (MCP)

SolidCraft speaks the Model Context Protocol over stdio (JSON-RPC 2.0, one message per line; protocol revisions 2025-06-18, 2025-03-26 and 2024-11-05). An agent models exactly like a person does: every tool runs engine commands, the same ones the toolbar, palette, scripts and control channel use, and never opens a dialog.

Persistent headless and bridged sessions expose asynchronous product images through the ordinary execute tool: render.preflight, render.start, render.status, render.cancel and render.export. Keep the complete returned job ID; image bytes remain in the session until explicit PNG export. See render jobs.

For sandboxed plugins and parametric timeline features that also use run(command, args), see RFC 0002. Desktop MCP in this document remains the reference implementation today.

Start it

# Headless: an in-process design session, no window (screenshots are drawn on the GPU).
solidcraft-cli mcp

# Headless, starting from a design file or a command script.
solidcraft-cli mcp --in part.sc

# Bridged to the running app, so you can watch the agent model. Turn on the app's control
# channel first (see below); then no flag is needed on either side:
solidcraft &
solidcraft-cli mcp          # finds the running app; headless when none runs (--headless forces that)

# A fixed port, or a second app: name it.
solidcraft --control 7878 &
solidcraft-cli mcp --connect 127.0.0.1:7878

Picking geometry with execute view.pick requires a connection to the running SolidCraft app. It is unavailable with --headless or when automatic discovery starts a headless session. frame: "model" chooses the pixels of a rendered model image; it does not enable headless MCP picking. The app handles this command outside the engine registry, so list_commands and describe_command do not include it, even when connected. See ui.pick in the control protocol for pixel and ray parameters.

The app listens when turned on

The desktop app opens its control channel on 127.0.0.1:27878 (or the next free port when that one is taken) when Preferences > Assistant > Agents through MCP > “Let AI agents connect” is on. It is off for a new install; the setting and its port take effect at once. It is reachable from this computer only, and every connection must show the token made fresh for that launch, readable only by your user account (control-protocol.md). The app writes the port to control/current.port in the same folder as its token file; solidcraft-cli mcp with no arguments reads it. --control PORT and SOLIDCRAFT_CONTROL_PORT still choose the port and apply even when the setting is off. With the setting off and no flag, the app listens on nothing.

Bridged, the server finds the app’s control token in the token file the app wrote for that port (control-protocol.md); --token T or SOLIDCRAFT_CONTROL_TOKEN give it explicitly when the app runs in a sandbox whose data folder the server can’t see (prefer the variable: command-line arguments are visible to other users in the process list).

A bridged server starts even when no app listens on that port yet, so an MCP client that launches it before the app (or after the app was closed) keeps its tools. The server’s instructions then say the app was not running, and every tool call answers with an error that starts no SolidCraft app is listening at HOST:PORT and says how to start it (solidcraft --control PORT). The first call after the app starts connects to it, reading that launch’s token. When the app quits or crashes mid-session, the next call says the same; a call that was running when it went away says the app did not answer and may have quit or crashed, after running the call or not, so inspect the design before sending a change again. A batch that could not be sent says the app is not running, so it is safe to send again.

stdout carries only protocol messages; errors go to stderr. Bridged screenshots are written to a temporary file by the app and read back by the server, so both must run on the same machine.

Parameter checking

Commands with a typed parameter contract (every new command, and more of the older ones as they are converted; describe_command shows a command’s schemas) can be checked as they are called. --param-check off|warn|strict sets the level for a headless session, including a script or recipe given with --in, and for calls bridged to the app; the default is off, which changes nothing. warn runs the call and adds param_warnings to its result, such as unknown key `operaton`; did you mean `operation`? (a starting script’s warnings go to stderr). strict refuses the call, and the design is left as it was. Bridged, the server asks the app once per connection whether it checks a request’s own level (control.capabilities and param_check in control-protocol.md). If it does, every call and batch is sent with the level and the app checks it against its own commands, so a newer app’s keys are not refused. At strict, a reply that doesn’t say the app checked the call at that level is an error (it may have run unchecked: inspect the design), and the server checks the next calls itself. An older app is checked here instead, against the server’s own commands’ contracts, before anything is sent: strict sends nothing that breaks one, and a command the server has no contract for goes to the app unchecked. With such an app, use the server from the same release: an app newer than the server may take keys the server’s contracts don’t know yet, and strict would refuse them.

Which files the agent can reach

Tool calls read, write and delete files only inside the server’s folders:

  • the folder of the open design (the --in design, the design open in the app, or whatever the agent last opened or saved), but only when it lies strictly inside your home folder (or the temporary folder) and not in a hidden folder. Home itself, a folder above it (/, /home, /Users, a drive’s root), hidden folders such as ~/.config and folders elsewhere don’t count. Hidden files and folders inside the design’s folder (.env, .git) are out of reach too;
  • on the same terms, every folder the agent opened or saved a design file in during this server’s run (open, save, or doc.open, file.save and file.save_as through execute or batch, with the path given in full) and, headless, every folder a design was open in, until the server stops (the 16 most recent). So after a new design, an opened STEP file or a save-as elsewhere, the agent can still save beside the design it worked on. Such a call succeeded only because its folder was already one of the agent’s, so no folder joins just because the agent names it;
  • SolidCraft’s own recovery folder, so doc.recovery_list, doc.recover and doc.recovery_discard work out of the box (discarding deletes only real recovery entries);
  • the server’s own scratch folder (a new private solidcraft-mcp-* folder in the temporary folder), where screenshot without a path goes;
  • every --allow-path DIR (repeat it for more). A folder named here is reached in full, hidden files included.

The CLI removes its own scratch folder if it is empty when the MCP command returns, including after a startup error. A nonempty scratch folder is kept, so saved files are not deleted.

The working folder the server was started in does not count by itself, so ~/.ssh, ~/.aws, ~/.bashrc and other dotfiles are never in reach by default. stderr lists the folders at start-up and says when the --in design’s folder was left out; --allow-path adds it back.

Paths are resolved before they are compared, so .. and symlinks that lead out are refused (saving through a link writes the file it points to, so the link’s target is what counts), and a new file must go into a folder that exists (a missing folder inside an allowed one is reported as missing; it is not created and does not call for a wider --allow-path). On Windows a path must be on a local drive (C:\… or \\?\C:\…): UNC, device and NT paths in any spelling (\\server\share, //server/share, \\?\UNC\…, \??\…, \\.\…), drive-relative paths (C:file), a : in a file name (alternate data streams), reserved device names with any extension (NUL, CON, COM1.txt, …) and short 8.3 names (PROGRA~1) are refused before any file system call, and the resolved path is checked again, so a link or junction that leads to a share is refused too. This includes a sketch.text font given as a file path and the font file a design’s text names when the design is opened; installed fonts named by family still work. The server spells its own folders (scratch, recovery) with long names, so they work when the temporary folder’s path has a short name (…\NAME~1.XYZ\AppData\Local\Temp, as Windows often gives when the user folder’s name is long). A refused path is a tool error saying why.

A hard link inside an allowed folder can’t be told apart from an ordinary file, so a hard link to a file elsewhere is reachable through it; don’t keep such links in a folder you allow.

solidcraft-cli mcp --allow-path ~/designs --allow-path ~/exports
solidcraft-cli mcp --allow-any-path   # no limit (the behaviour before this option existed)

When a save (or any other call) is refused with … outside the folders this agent may use …, add the folder to the server’s args in your MCP client’s configuration (the mcpServers entries below, or what solidcraft-cli mcp setup wrote), for example "args": ["mcp", "--allow-path", "/Users/NAME/Documents/Designs"], and restart the client. An agent should ask you to do that rather than save through the app’s control port, which knows nothing of these folders.

Bridged, the server sends its folders with every request (allow_paths, and the folders the agent opened or saved a design in as design_folders, see control-protocol.md) and the app adds its own recovery folder and the folder of the design open in it, under the same rules, so give absolute paths: a relative one resolves against the app’s working folder, not the server’s. The app’s own file dialogs are never limited.

Compact output

Compact output is for agents that pay for every byte. It is off by default, and then every tool behaves as described below. Any one of these turns it on:

  • Preferences › AI › Agents through MCP › Compact output in the app, for every server bridged to it (--connect, and the agents its chat starts). The server asks the app (mcp.options on the control channel) when the agent first lists or calls a tool, and keeps the answer for the session, so the tools it listed keep their shape: a change applies to agent sessions started after it. An app that is not running yet is asked again on the next call.
  • solidcraft-cli mcp --compact.
  • The environment variable SOLIDCRAFT_MCP_COMPACT=1 (true, yes and on work too; 0, false, no, off or empty leave it off; any other value is an error at start-up).

The command line can only turn it on: with the preference on, a server started without --compact is compact too. With it on:

  • inspect_design summarises each sketch (id, name, plane, the number of points, curves, constraints and profiles, dof, status, visible) instead of returning its contents: a sketch of 720 points is some 4 KB in the summary and over 80 KB in full. sketches takes a list of sketch names or ids to return in full (as execute sketch.inspect gives them), true for all of them, or false / "summary" (the default). bodies (a list of names) keeps only those bodies and measures only them; "names" and false work as without compact output. An unknown sketch or body name is an error that lists the names there are.
  • Tool results that are JSON are compact, not pretty-printed.
solidcraft-cli mcp --connect 127.0.0.1:7878 --compact
SOLIDCRAFT_MCP_COMPACT=1 solidcraft-cli mcp --connect 127.0.0.1:7878

Tool annotations

Each tool says what it may do, for clients that ask before running tools (or auto-approve on these hints). list_commands, describe_command, inspect_design, measure, body_topology, skill_reference, crash_reports, pending and running are read-only. set_parameter, undo and redo change the design undoably, and so does check_parameters (it changes the design and puts it back: not read-only, not destructive). new_design replaces it (destructive). execute, batch, open, save, export, screenshot (whose path writes a file) and compare_picture (which reads the reference picture) can read, write, overwrite or delete files: they are not read-only, destructiveHint and openWorldHint are true.

cancel changes the design only by keeping a change from landing; it touches no files.

Client configuration

solidcraft-cli is on PATH after every package install: the Windows installer adds its folder, the deb, rpm and FreeBSD packages install it in /usr/bin, the Homebrew cask links it, the AppImage writes a small solidcraft-cli launcher into ~/.local/bin on its first run, and the macOS app links it into /opt/homebrew/bin or /usr/local/bin when it starts (Help > Install solidcraft-cli command does it with an administrator prompt when those are not writable). Two packages keep it out of PATH by design: Snap exposes it as solidcraft.cli, and Flatpak runs it as flatpak run --command=solidcraft-cli io.tangencylabs.solidcraft; use those as the client’s command (with mcp as the arguments).

The quickest way: Help > Connect AI assistants… in the app, or

solidcraft-cli mcp setup            # every client found on this computer
solidcraft-cli mcp setup --dry-run  # show what it would write, change nothing
solidcraft-cli mcp setup --client opencode --live

(mcp install is the same.) It adds a solidcraft server, using the full path of the solidcraft-cli that runs it, so nothing has to be on PATH. A client counts as installed when its configuration folder exists, and no client program is started. Supported:

--clientConfiguration file
opencode~/.config/opencode/opencode.json (or .jsonc)
claude-desktop%APPDATA%\Claude\claude_desktop_config.json, ~/Library/Application Support/Claude/, ~/.config/Claude/, and the Microsoft Store package’s folder
claude-code~/.claude.json (user scope mcpServers)
cursor~/.cursor/mcp.json
codex$CODEX_HOME or ~/.codex, config.toml (CLI, IDE extension and desktop app)
gemini~/.gemini/settings.json
copilot~/.copilot/mcp-config.json (GitHub Copilot CLI)
windsurf~/.codeium/windsurf/mcp_config.json
zedZed’s settings.json (context_servers)
vscodeVS Code’s user mcp.json

Only the solidcraft entries change; everything else in the file (other servers, comments, order) stays as it was. Updates replace the file atomically and keep the previous version as <name>.bak. A failed write leaves the configuration intact. A file that does not parse is left alone. Setup also checks for changes since it read the file and refuses a stale edit. This check narrows the race with another program writing its settings; it cannot exclude a write after the check. After an update moves the program, running setup again only refreshes the path of an entry it wrote. An entry you changed (extra arguments, another program) is left as you have it. --live also adds solidcraft-live, which bridges to the open app (port 7878, see below). Restart the client afterwards.

SolidCraft also does this by itself: when the app starts it adds the solidcraft entry to each installed client (and refreshes its path after an update), and says once which clients it connected. It never touches an entry you changed. Settings > Assistant > Make SolidCraft available to AI apps on this computer turns it off, which removes the entries it added. Runs with --control or SOLIDCRAFT_CONTROL_PORT (automation) leave your clients alone.

To configure a client by hand instead, these are the entries. OpenCode (opencode.json):

{
  "mcp": {
    "solidcraft": { "type": "local", "command": ["/path/to/solidcraft-cli", "mcp"], "enabled": true }
  }
}

Claude Code (project .mcp.json, or claude mcp add solidcraft -- solidcraft-cli mcp; a setup added under the previous name keeps working, see the compatibility notes):

{
  "mcpServers": {
    "solidcraft": { "command": "solidcraft-cli", "args": ["mcp"] }
  }
}

If a terminal doesn’t find solidcraft-cli (older Windows installers don’t put its folder on PATH; see below for where it is), give its full path (by default C:\Program Files\SolidCraft\solidcraft-cli.exe): claude mcp add solidcraft -- "C:\Program Files\SolidCraft\solidcraft-cli.exe" mcp.

Claude Desktop (claude_desktop_config.json), bridged to a running app:

{
  "mcpServers": {
    "solidcraft": { "command": "/path/to/solidcraft-cli", "args": ["mcp", "--connect", "127.0.0.1:7878"] }
  }
}

On Windows the installer puts solidcraft-cli.exe next to solidcraft.exe in C:\Program Files\SolidCraft (the portable zip has both too). Give the full path, with each backslash doubled in JSON. This configuration declares two servers: solidcraft models headless (no window; steadier for timings), solidcraft-live drives the app you have open, so you can watch the agent model. --allow-path names a folder where the agent may save and export; it must already exist.

{
  "mcpServers": {
    "solidcraft": {
      "command": "C:\\Program Files\\SolidCraft\\solidcraft-cli.exe",
      "args": ["mcp", "--allow-path", "C:\\Users\\NAME\\SolidCraft-Agent"]
    },
    "solidcraft-live": {
      "command": "C:\\Program Files\\SolidCraft\\solidcraft-cli.exe",
      "args": ["mcp", "--connect", "127.0.0.1:7878", "--allow-path", "C:\\Users\\NAME\\SolidCraft-Agent"]
    }
  }
}

Start the app with its control channel for solidcraft-live, from PowerShell: & "C:\Program Files\SolidCraft\solidcraft.exe" --control 7878, or once for every launch: setx SOLIDCRAFT_CONTROL_PORT 7878.

Open claude_desktop_config.json with Claude Desktop’s Settings > Developer > Edit Config rather than by its path: the Microsoft Store (MSIX) build of Claude Desktop can keep its data in its own package folder (%LOCALAPPDATA%\Packages\…\LocalCache\Roaming\Claude) instead of %APPDATA%\Claude, and Edit Config opens the file the app actually reads. Quit Claude Desktop completely (from the notification area) and start it again to load the servers.

Agent skills

skills/ holds five Agent Skills for agents that model through this server:

  • solidcraft-modeling covers building a new part.
  • solidcraft-editing covers changing an existing design.
  • solidcraft-checking adds checks for errors SolidCraft does not report, regression tests and porting, next to the other two.
  • solidcraft-mechanisms covers assemblies: joints placed by world coordinates, motion links, closed loops such as actuators, contact sets and motion studies, checked against analytic values.
  • solidcraft-reporting turns a problem the user hit into a bug report: it reproduces it in a fresh design, shrinks it to a minimal command script, checks the numbers with measure and a screenshot, and searches existing issues first.

They cover the workflow, the command shapes, how to verify with measure, and the known traps. With them an agent builds a part in a few batch calls and reads far less output. To install them for Claude Code, copy the folders into ~/.claude/skills/; skills/README.md has the details.

The skill_reference tool returns these documents from the copies built into SolidCraft, so an agent without file access can follow them: {"skill": "solidcraft-modeling"} returns its reference.md, the default file. Mechanisms also has reference.md, checking has reference/porting.md, and every skill has SKILL.md (editing uses modeling’s reference). It never reads a path from disk, and an unknown skill or file is refused with the available choices. SolidCraft’s own chat turns the agent’s file tools off, so this is how its agent reads the skills there. The instructions the server sends at initialize point agents at skill_reference first, then say how to plan the numbers, build in a few batch calls and verify each stage with measure; the same pointer is in the system note SolidCraft’s own chat gives its agent.

Tools

ToolArgumentsWhat it does
list_commandsfilter?, prefix?, panel?, compact?, fields?, limit?, offset?The command catalog: id, label, tab/panel, shortcut, parameter docs, enabled now. filter matches id, label, tab and panel. prefix keeps the ids that start with it ("sketch.") and panel the commands of one whole toolbar panel or tab ("Modify"), both ignoring case. compact: true keeps only id and label of each entry, about an eighth of the size; fields keeps the keys it lists instead. limit and offset page the matches, and the reply becomes {commands, total, offset, next_offset} (next_offset is null on the last page). With none of these the reply is the whole catalog, about 350 KB.
describe_commandidOne command’s parameter docs (params) and, for commands with a typed contract (every new command, and more of the older ones as they are converted), the JSON Schemas (2020-12) of its parameters (input) and of a successful result (output); null for the others. The same as execute engine.describe {command}.
executecommand, params?, verbose? (default true), expect?Run any command with JSON parameters (list_commands shows them). Failures leave the design unchanged. A feature command replies with the feature (feature, name), every body (bodies) and the bodies it created, changed and removed (created_bodies, changed_bodies, removed_bodies). verbose: false uses the same compact reply as batch. expect checks the design after the command, as it does for batch (see below).
batchcommands: [{command, params}] or path (a script file, see below), rollback? (default true), verbose? (default true), timings?, profile?, coalesce_parameters? (default false), expect?Run steps in order, stop at the first error and undo the completed steps. The reply names the failing step (failed.index, failed.command, failed.error), every result before it, rolled_back (true only when a rollback was asked for and the design is where the batch started: its completed steps were undone, or nothing ran; false when rollback: false or when the design could not go back), undone, applied (the indices of the steps whose changes are in the design) and not_run (the steps never sent). A step may name a tool instead of a command, with that tool’s arguments as params: set_parameter (runs parameters.change; value or expression), list_commands (engine.commands, narrowed by its arguments as the tool narrows it), describe_command (engine.describe), execute ({command, params}), measure (summary: true summarised as the tool summarises it), compare_picture (inspect.compare_picture, numbers only: no overlay PNG), inspect_design (document.inspect with its measure, timeline and bodies: each sketch as its counts, not its full contents, and no joints), undo, redo, export, save, open, new_design and crash_reports. screenshot, body_topology (use model.edges and model.faces), batch, check_parameters, pending, running and skill_reference can’t be steps, nor can component.edit_in_place and component.finish_edit, however a step names them, as they go to another design’s tab (send them with the execute tool on their own): such a batch fails before anything is sent. So does a step the batch would certainly refuse at its turn (#5853): a step without a command, an unknown command id (the error names the closest ids) or, with --param-check strict, parameters that break a command’s contract. A headless session checks this itself; an app checks it against its own commands when it gets the batch, and a bridged server at --param-check strict checks the contracts before it sends the batch. verbose: false keeps the reply small in large designs: feature steps leave out the full body list (created_bodies, changed_bodies, removed_bodies stay) and sketch steps the ids they repeat outside created. Bridged to an app that has engine.batch, the whole batch is one control request (one frame of the app). Older apps, and apps showing the agent cursor, get the steps one by one. timings: true adds timings: [{index, command, ms}] for every step run, the failing one included, and total_ms. profile: true also adds each step’s profile: {boolean_ms, tessellate_ms, other_ms}, its time in kernel booleans, in tessellation and the rest. Steps are timed where they run: in the app when bridged. An app without engine.batch is timed step by step, without profile. coalesce_parameters: true sets each run of consecutive plain parameter edits (parameters.change or set_parameter with a name and a value, or changes) together, so the design rebuilds once per run instead of once per edit. Steps between runs see the design up to date. Each step keeps its own result (coalesced: the run’s length), and the run is one undo step. The run skips the states between its edits, so an edit refused on its own can go through with the run’s later ones. A refused run goes step by step, naming the refused step.

expect on batch and execute turns the verify step into part of the build. An agent computes the volume, body count or bounding box a stage should produce before building it; instead of a measure call and a comparison by hand after every stage, it gives them to the call: "expect": {"volume_mm3": 59824.9, "tol": 0.002, "bodies": 1, "bbox": {"min": [0, 0, 0], "max": [140, 90, 30]}}. Every key is optional (at least one of volume_mm3, bodies and bbox); tol is relative to the expected volume (default 0.002, so 0.2 %), and each bounding-box coordinate must lie within 0.05 mm. Once the call has gone through, the server measures the design the way measure does (the total volume of the closed bodies, the body count and the box round every body) and the reply gains "expectation": {"ok": bool, "measured": {"volume_mm3", "bodies", "bbox"}, "expected": {...}, "mismatch": ["volume: measured 59680, expected 59824.9 (-0.24 %)", ...]}. On a mismatch the tool result is flagged isError: true, with the mismatch text as its first content block and the reply as the second, so the model notices; the design is kept as built (undoably, as ever), and the agent decides whether to undo, edit a feature (timeline.edit) or carry on. A batch that fails is not measured, and an expect the tool cannot use (an unknown key, a value of the wrong type, a negative tol, a bbox without three coordinates a side) is refused before anything runs. expect is an argument of the tool, not a key of a step: a step carrying one is treated as any step with a key the batch does not know. Volume and body count say nothing about which kernel built the design, which is the point: the check holds whatever builds it. | inspect_design | measure? (default true), sketches? (default true), joints? (default true), timeline? (true: the default; "summary": each feature as id, name and type, with error, warning, suppressed and rolled_back only when it has them; false: no timeline), bodies? (true: the default; "names": only the body names, nothing measured; false: no bodies); with compact output sketches? (names or ids, true, or false / "summary": the default) and bodies? also takes a list of names | On a design of hundreds of features and bodies, timeline: "summary" and bodies: "names" (or false) keep the reply small enough for an agent’s tool output. Design properties (metadata: project, author… and other tools’ keys; set them with execute document.metadata.set), parameters, timeline (type, suppressed, rolled back, error, warning, timing), bodies (volume, area, centre of mass, bbox, face/edge/vertex counts, visible; removed_later: true while a sketch is being edited on a rolled-back timeline and a later feature deletes the body, which is then not shown), sketches (curves, points, constraints, profiles, DOF, solve status, visible), joints when the design has any (as joint.inspect gives them: each origin’s occurrence, component and path, the snap as saved, the feature, face, edge or sketch curve (sketch: {id, name, curve}) its persistent names refer to, resolves and the error when it is lost; kind, offset, angle, flip, values, limits, expressions, motion links; execute joint.inspect {joint} gives one joint), selection, undo depth. Hide helper sketches, bodies or construction planes with execute browser.visibility ({items, visible} or {folder, visible}), and joint glyphs with {joints: [id or name], visible} or {folder: "joints", visible} (a hidden joint still constrains and drives; joint.list reports each joint’s visible). | | measure | body? or items?, summary? (default false) | Area (mm²), bbox, topology counts and closed per body. Volume (mm³), volume centre of mass, density and mass are reported only for closed, consistently oriented bodies (null or omitted for open surfaces). Volume/mass totals skip open bodies; total.open_bodies_skipped gives their count. Area and topology totals include all bodies. With items (one or two selections as execute takes them: face, edge, vertex, sketch_curve, profile), the picked geometry instead: faces and edges need only type, body and either point or index (from model.faces / model.edges). See below. summary: true keeps one short object per body (name, volume_mm3, bbox: {min, max}, faces, closed; a body that could not be measured keeps its error), body_count and total.volume_mm3, nothing else: what a stage check against computed numbers needs, at a fraction of the size. It applies to the bodies reply, not to items. | | body_topology | body, faces? (all, cylinders; default all) | Edges (index, midpoint, length, ends) and faces (index, name, area, centroid, normal, point, and for a cylindrical face its cylinder: origin, axis, radius). An edge midpoint is the [x,y,z] that picks that edge for fillet and chamfer. A face’s point lies on it and picks it (its centroid may not: that of a face with a hole is in the hole); a face’s name picks it for the commands that take faces (face.delete, solid.shell, solid.draft, solid.offset_face, solid.split_face, solid.replace_face, and face of sheet.convert). faces: "cylinders" replies cylinders instead of edges and faces: one entry per cylinder, {radius, origin, axis, extent, names}, the halves the kernel splits a cylinder into merged (same radius, same axis line; origin and axis as the first half’s cylinder gives them), extent the [min, max] distance along axis from origin that the body’s points on the cylinder span (the faces’ points and the ends of the edges lying on it; left out when none were found), and names the merged faces’ names, any of which picks the face. Enough to check where every hole and boss is, in a few lines instead of the whole topology. | | set_parameter | name, value or expression (number or expression), unit?, comment? | Create or change a user parameter and recompute. expression is another name for value (the one parameters.change uses): give one of them, or both only when they agree; neither, or two that differ, is an error. Reports recomputed (features rebuilt), restored (features whose earlier result for the same inputs was reused, e.g. a value set back) and features that now fail. An expression new_name = expression renames the parameter as it sets it; in any other tool’s expression parameter it adds the user parameter new_name and the input uses it (defined, definitions in the reply). | | check_parameters | set: {name: value} (a number or an expression each, as set_parameter’s value), report? (measure, cylinders, errors; default all three), bodies? (a list of body names: only those are measured and their cylinders listed; default all), summary? (measure as the measure tool’s summary: true; default false) | Try parameter values without keeping them: one edit.checkpoint, every value set in one parameters.change {changes} (one rebuild; an app without changes gets one change per name), the reports read, then edit.restore_checkpoint, so the design, its parameters and its undo history are exactly as before. The reply has values (each parameter’s value with the tried expressions in), recomputed, measure (volume, bbox, bodies and faces per body, as the measure tool reports them), errors and warnings (the features that fail or warn: {feature, name, error}) with parameter_errors (the parameters that don’t evaluate), cylinders (every cylindrical face of every body as {body, radius, origin, axis, faces, name, area}; the halves a kernel splits one cylinder into are one entry, origin is the axis point closest to the world origin and axis points so that its first non-zero component is positive) and restored: true. On a large design, bodies with the bodies a change touches and summary: true keep the reply small. A name the design has no parameter of, a value that is not a number or a string, an expression that does not evaluate, an unknown report item or a bodies that is not a list of names is an error, and nothing changes; a body name the design does not have after the change is an error after the design is put back. If the checkpoint cannot be restored the call is an error with restored: false, restore_error and a note that the tried values are still in the design: set them back before saving. Replaces the loop set_parameter, measure, body_topology, set_parameter back, which cost every agent session three to five calls and up to 16 s of rebuilds. Not a batch step. | | screenshot | view? (iso, top, front, back, bottom, left, right, home, fit, or a named view saved with view.save), camera? ({target, yaw, pitch, distance, fov?}, as view.apply), path?, width?, height?, source? (window, model), grid?, axes? | A PNG image content block. Headless: a render with the viewport’s pipelines on a headless GPU device (see solidcraft-cli gpu; with no adapter, the CPU rasterizer, without analysis colours), fitted to the model, or from the named view or camera; the reply’s renderer says which (gpu, gpu-software, cpu, with a warning for cpu). Bridged: the app window (default) or a model render with the app’s camera; a named view or camera moves the app’s viewport there first (execute view.apply does the same without a screenshot). grid: false and axes: false leave the ground grid (it lies on Z 0 and cuts through a part that straddles it) and the origin’s axes out of a model render; axes hidden with browser.visibility stay out whatever axes says. Bridged, a screenshot that gives grid or axes is a model render, which follows the app’s Grid and Origin switches for what it does not give; source: "window" with either is an error. A window that is minimised or on another macOS Space presents no frame: with no source the reply is a model render and says so (fallback); source: "window" is an error saying why, at once. Agents that don’t need the app’s chrome can ask for source: "model". | | compare_picture | reference, view?, camera?, bodies?, crop_px?, crop_fraction?, part_fills_crop?, fit? (width, height, both; default width), rows? | Compare the model’s outline with the part in a reference picture (a PNG or JPEG inside the server’s folders, read through the same path rules as screenshot). The compare is outline only: holes are not compared — both the picture’s and the model’s holes are filled. The model’s outline is inspect.silhouette seen from view (default front) or camera; the picture’s part is found by growing the background in from the border through small colour steps, filling holes and keeping the largest piece, so a backdrop’s shading stays background and labels on a board drop out; a part touching the border, a white part on a light grey backdrop, and a part on a transparent background all separate. crop_px ([x, y, w, h] in pixels) or crop_fraction (the same as fractions 0..1) takes one view off a board; with part_fills_crop: true the crop box is the part itself and no background search runs, for a crop that tightly frames the part; fit names the dimension the one uniform scale comes from, never stretched per axis (both scales by width and reports aspect_mismatch). Returns an image content block (the overlay PNG: model red, picture blue) and text: overlap (0..1) and the model’s top, bottom, left and right edges against the picture’s in mm at nine stations, positive where the model reaches past the picture, null where a station misses one of the two. As a batch step it runs inspect.compare_picture without the overlay. | | export | path, format? (step, stp, iges, igs, 3mf, 3mf-tagged, stl, stla, obj, glb, urdf), bodies?, mesh_prefix?, effort?, velocity? | STEP, IGES, 3MF or mesh export; GLB writes binary glTF in metres with Y up, with each body’s colours, metallic and roughness, and each occurrence a named node holding its part’s bodies; URDF writes the whole assembly with its meshes next to it (docs/urdf.md). | | undo, redo | — | Undo / redo one change. | | pending | — | Bridged: the result of a request that was still running when its call returned (an error starting still running:), waiting up to 60 s for it; done: false while it runs (call again), pending: null when nothing runs. See Long-running requests. | | crash_reports | id?, limit? | The crash reports on this computer, newest first; with id, the whole report (backtrace, command, recent commands, design_path/replay_path). See crash-reports.md. | | skill_reference | skill, file? (default reference.md) | Read a bundled skill document. Skills: solidcraft-modeling, solidcraft-mechanisms, solidcraft-editing, solidcraft-checking. | | running | - | What the app is running or has queued, for every client (control.running, see control-protocol.md): each request’s connection and id, method, command id or a batch’s step count, running_s and queued, never its parameters. Answers at once while the app is busy, also while another call from this client, such as a long batch, is still waiting for its reply: bridged, it is then asked on a connection of its own (its self marks no request) and its reply comes before that call’s. Ask it when a call timed out before sending a change again. Headless: always {"running": []}. | | new_design | name? | Start an empty design. | | cancel | — | Bridged: cancel this server’s request that was still running in the app when its call returned (control.cancel, see Cancelling a request). Headless: an error, as nothing is ever left running. Not a batch step. | | open | path | Open a .sc design (or a .solidcraft one from an earlier release), a STEP file (.step/.stp) as a new design holding its bodies (an Import base feature), or a 3MF/STL file as mesh bodies. | | save | path? | Save the design (to its current file without path). |

measure with items reports each item under items (length_mm, area_mm2 or position). A circle, arc, hole or other cylindrical face also gives diameter, radius, center and axis, with circumference when it goes all the way round, or arc_length and angle_deg for an arc; round says which (circle, arc, cylinder). A slot (a sketch profile of two half circles and two lines, or one half-cylinder end of a slot in a solid) gives slot: width, center_distance and overall_length. With one item these fields are repeated at the top level. With two items:

  • min (also distance_mm, with from, to and delta): the closest distance;
  • max (with max_from, max_to): the farthest distance;
  • center_distance (with center_from, center_to): when an item is round, from its centre or axis: axis to axis for parallel axes, the closest distance between the axes otherwise, and axis to plane, line or point;
  • measure: the distance the Measure panel shows first (center where there is one, else min);
  • angle_deg between two planar faces or straight edges.

Earlier fields are unchanged, so older scripts keep working.

To select geometry in the view, use execute select.set with {"items":[{"type":"face","body":"Body1","point":[5,5,10]}]} (or "type":"edge"). The point is in world millimetres and uses the same proximity tolerance as Measure. An index can replace the point; the command then supplies a point on that face or edge, including curved faces and placed components. Existing selections with both fields keep their index and point. Missing geometry or an invalid partial pick leaves the entire selection unchanged, including with "add":true. inspect_design reports the completed selections with both fields.

To change a feature after it is made, read its definition with execute timeline.inspect ({feature}: id or name) and change fields with timeline.edit ({feature, set}; e.g. {"extent": {"distance": "30"}}). The fields per feature type are listed in the book’s Solid features chapter.

To tell whether the design changed since a call, without reading all of it again, execute document.revision answers {document_id, revision}: revision moves only when the design changes, and document_id names the open design (another one after new_design or open). See control-protocol.md.

To compare design proposals with several parameter edits each, call execute with inspect.parameter_variants. It rebuilds a separate copy for each candidate. A direct call connected to the desktop window can run on a worker, like a long measurement. Use the baseline and each result’s metrics to compare volume, mass and body count; check ok and diagnostics before applying any candidate.

{"command": "inspect.parameter_variants", "params": {"variants": [
  {"name": "Lighter", "set": {"width": "45 mm", "thickness": "4 mm"}},
  {"name": "Wider", "set": {"width": "75 mm", "thickness": "6 mm"}}
]}}

Apply the selected values separately with parameters.change {changes} if you intend to edit the design. Use describe_command for the input and output schemas.

Resources: solidcraft://design (inspect JSON with measurements) and solidcraft://commands (the catalog).

Errors: an unknown tool or arguments that don’t match the tool’s schema are JSON-RPC -32602 errors (an unknown argument’s error lists the tool’s arguments); an engine failure (bad command parameters, a feature that fails) is a tool result with isError: true and the engine’s message, which the model can read and correct.

Long-running requests

The app answers a control request that runs longer than 60 seconds (a big fillet, say) with “still running” (control-protocol.md); it has not failed, and was not cancelled. The bridged server then keeps the connection, where the reply will come, and asks the app (control.pending) every half second for up to 240 seconds more, saying on stderr how long it waits; when the request finishes, the tool call returns its real result. If it still runs after that, the tool call fails with an error starting still running: that names the request and says what it still does: it will still apply when it finishes, or will still write the file (an export or a save: the design does not change), or only reads (nothing changes). Don’t send it again: call the pending tool, which waits up to 60 seconds for its result each time, or cancel it (Cancelling a request). Any other tool call first waits up to 60 seconds for it too, and is not sent while it still runs (so nothing is queued to apply unseen behind it); a request that finishes in that wait keeps its result for pending. If the connection to the app is lost meanwhile, the result is lost with it, and pending says so: inspect the design before sending a change again.

So a single tool call may block for up to 300 seconds (the app’s 60 s wait plus 240 s of polling) before it returns still running:, and a call made while an earlier request still runs for up to 60 seconds more. An MCP client needs a per-call timeout above that (say 330 s) to see that error; a client that gives up sooner only sees its own timeout, though the request goes on running in the app: then call pending (or inspect_design) before sending anything again.

A batch step that is still running stops the batch like a failure. Its rollback first waits for the step (as above), so a restored checkpoint takes the step back too; if it still runs, the reply says rolled_back: false, marks the step still_running and lists it in applied, as it will apply when it finishes.

batch rollback goes back to an engine checkpoint (edit.checkpoint) taken before the first step: the design, the active sketch and the undo history are restored exactly as they were, however long the history is. Batches that call file.new or doc.open get the design back, not its file path. (An older app without checkpoints falls back to undoing as many steps as the batch added to the undo history.) A checkpoint that a step of the batch takes stays after a batch that went through; a rollback that goes back uses it up.

A long batch, such as a polyline of hundreds of points, can be written to a JSON file and run with path instead of commands, so the steps don’t have to go through the chat. The file holds an array of steps or {"commands": [...]}, the format solidcraft-cli run takes and solidcraft-cli script writes. The server reads it (at most 32 MB), within the folders the agent may use, and runs the steps as if they had been sent inline: the same tool steps, rollback and reply. path and commands can’t both be given, and a file that can’t be read, isn’t JSON or has no steps fails before anything is sent.

Cancelling a request

A request the app is still running when its call returns (an error starting still running:) has not failed and was not cancelled. cancel cancels it at once, without first waiting for it as other calls do, on the connection it was sent on (control.cancel, control-protocol.md):

  • still waiting its turn in the app: removed, it never runs (outcome: "removed");
  • a design change or an export computing off the app’s window thread (Preferences > AI > Keep the window responsive while agents run commands): stopped, nothing of it applied (outcome: "stopped"; an export’s file may still be written). The work stops early in the background;
  • already applying on the window thread, or a file being opened: a tool error starting cannot be cancelled:. It will still finish; inspect the design before sending it again;
  • already finished: cancelled: null with its reply.

Once it is removed, stopped or finished, later calls no longer wait for it and pending says pending: null. With nothing running, cancel says nothing to cancel. Only this server’s own request can be cancelled; another client’s requests are out of its reach.

Camera and close-up recipe

The camera of screenshot (the object view.apply, view.save and solidcraft-cli snapshot --camera take too) is an orbit camera, {target, yaw, pitch, distance, fov?}, in millimetres and radians. Unlike the angle of solid.move, a bare number is not degrees here. The eye is at target + distance * (-sin(yaw) * cos(pitch), -cos(yaw) * cos(pitch), sin(pitch)), looking at target with Z up on the screen. So yaw: 0, pitch: 0 is the front view: the eye at -Y, looking along +Y, with +X to the right. A yaw of π/2 looks from -X, π from +Y and -π/2 from +X. The pitch is the elevation angle up from the XY plane, and π/2 looks straight down with +X to the right and +Y up; view.apply refuses a pitch past ±π/2. A positive roll turns the picture clockwise. The app’s View angles readout (view.angle_readout) shows the same yaw, pitch and roll in degrees. fov is the vertical field of view in radians. 0, or no fov, is an orthographic view whose visible half-height is 0.4 × distance; with a perspective fov the half-height at the target is distance * tan(fov / 2). inspect.section removes what lies on the side its normal points to: it keeps the points p with n · p < d, where d = n · origin (flip: true keeps the other side). To look into the cut, put the eye on the removed side.

To look closely at one part of a large model, such as a STEP assembly, work in a headless session. These steps change only the open design in memory:

  1. Open the model with solidcraft-cli mcp --in assembly.step (or the open tool, which also takes up and names, see step 4 and STEP import). inspect_design lists the bodies with their names and bounding boxes.
  2. Remove what is in the way with solid.remove, e.g. {"bodies": ["Cover"]}.
  3. Cut the region out of the rest with solid.box, e.g. {"corner": [40, 0, -45], "length": 20, "width": 50, "height": 45, "operation": "intersect", "targets": ["Housing", "Plate"]}. Each listed body is trimmed to the box, and one that lies wholly outside the box is removed. The box itself is not kept.
  4. If the model lies on its back (many STEP files have +Y up), open it with up: "y" (the open tool, doc.open, file.insert_step), which turns the file’s bodies a quarter turn about X (Y becomes Z). One already open is stood Z up with solid.move, listing every body you keep, e.g. {"bodies": ["Housing", "Plate"], "axis": [1, 0, 0], "angle": 90}. It turns the bodies about the world origin, and a bare angle is in degrees. A turn of +90° about X takes +Y to +Z.
  5. Colour the parts that matter with appearance.assign, e.g. {"bodies": ["Housing"], "color": "#3a7bd5"}, and hide helper bodies or sketches with browser.visibility, e.g. {"items": ["Plate"], "visible": false}.
  6. Cut a section with inspect.section, e.g. {"plane": {"origin": [50, 30, 0], "normal": [0, 1, 0]}} or {"plane": "XY", "at": 5}.
  7. Take the picture with screenshot: a camera whose target is the centre of the region and whose eye is on the cut side, source: "model", grid: false if the region straddles Z 0 (the ground grid would cut through it), and path, width and height as needed. An orthographic picture is 0.8 × distance high, so a distance of about 1.5 times the region’s size frames it. Look at the image, adjust yaw, pitch and distance, and shoot again. Headless, the picture is always a render of the model; bridged to the app, source: "model" asks for that render instead of the app’s window.

Compact replies

execute and batch default to verbose: true, preserving the full command results. For long modelling sessions, set verbose: false at the tool-argument level:

{"name": "execute", "arguments": {"command": "solid.box", "params": {"length": 10, "width": 10, "height": 10}, "verbose": false}}

With verbose: false, a created list of more than 20 ids (a drawing of many curves) is left out and counted instead: curve_count, constraint_count, dimension_count and point_count. sketch.inspect lists every id.

Feature replies omit the full bodies list while keeping feature, name, created_bodies, changed_bodies and removed_bodies. A cut can therefore report a changed body even when no body name was added or removed. Sketch replies keep new IDs under created and omit only identical top-level points, curves, constraints and dimensions lists. When a sketch command creates more points than its new curves have ends (the poles of an offset’s splines), created.points becomes created.point_count: refer to those curves by their ids. A sketch.move_points reply gives moved_count and reached (true when every point reached its target) instead of the moved list, and lists only the moves that missed under not_reached (point, to, at). Other fields and errors are unchanged; inspection commands still return their complete results. Compaction uses the engine’s reply directly, without an additional design-inspection request.

Demo: an enclosure built over MCP

examples/demo/enclosure_mcp.py is a small MCP client. It drives a running app (solidcraft --control PORT) through solidcraft-cli mcp --connect and builds an electronics enclosure one execute call at a time (enclosure_steps.json): a rounded box, shelled open at the top, a USB port cut through a side wall, four screw bosses, and then a change of the width parameter that the timeline rebuilds. examples/demo/record.sh records the run with ffmpeg, with each step’s caption burnt in.

examples/demo/enclosure_ui_mcp.py (v2) builds the same part the way a person would: the modelling goes through the app’s UI over its control channel. The pointer glides to toolbar buttons and faces and clicks them, the real dialogs open, the sketches go on the model’s faces, and the camera follows. The parameters, the sketch dimensions bound to them and two extrudes go over MCP. With --story v3 (the default) one standoff is patterned 2 × 2 in the Rectangular Pattern dialog with spacings from the parameters, and width and wall are then changed in the Parameters dialog. record_ui.sh records it and draws the pointer and the captions.

A typical session

→ {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"me","version":"1"}}}
→ {"jsonrpc":"2.0","method":"notifications/initialized"}
→ {"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"batch","arguments":{"commands":[
     {"command":"sketch.create","params":{"plane":"XY"}},
     {"command":"sketch.rectangle.two_point","params":{"p0":[0,0],"p1":[40,30]}},
     {"command":"sketch.finish"},
     {"command":"solid.extrude","params":{"distance":20}},
     {"command":"solid.fillet","params":{"edges":[[0,0,10]],"radius":3}},
     {"command":"sketch.create","params":{"plane":"XY"}},
     {"command":"sketch.circle.center","params":{"center":[20,15],"radius":5}},
     {"command":"solid.extrude","params":{"distance":20,"operation":"cut"}}]}}}
← {"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text","text":"{\"ok\": true, \"completed\": 8, \"results\": [...]}"}]}}
→ {"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"measure","arguments":{}}}
← … "total": {"volume_mm3": 22390.6, …}
→ {"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"screenshot","arguments":{"view":"iso"}}}
← {"jsonrpc":"2.0","id":4,"result":{"content":[{"type":"image","mimeType":"image/png","data":"iVBORw0…"},{"type":"text","text":"{\"source\":\"model\",…}"}]}}
→ {"jsonrpc":"2.0","id":5,"method":"tools/call","params":{"name":"export","arguments":{"path":"/https/docs.solidcraft.io/tmp/part.step"}}}

The tests in crates/mcp/src/tests.rs run sessions like this (box → fillet → hole, parameters, batch rollback, hostile arguments) and check the results only through MCP.

Agent chat

Agent chat is a side panel where you talk to a coding agent you already use (Claude Code, Codex, Google Antigravity, OpenCode, Grok Build and others), or to a model behind an OpenAI-compatible or Anthropic-compatible endpoint you choose (Ollama or LM Studio on your computer, OpenAI, Anthropic, any other URL), and have it work on the design that’s open. A coding agent runs on your computer under your own account with that agent: SolidCraft doesn’t sell or proxy a model and needs no key for it. An endpoint’s key, if it needs one, goes into your system keychain, never into SolidCraft’s settings.

Desktop app only.

Turning it on and off

Everything about it is on one page: Preferences → AI.

  • Show AI features (on by default) is the master switch. Turned off, SolidCraft shows nothing of it: no AI button, no shortcut, no menu entry, no panel, and nothing is looked for or started. The page stays, so you can turn it back on.
  • Show Assistant button in application bar (on by default): the Assistant icon in the application bar row, which stays there however wide the window is. Turn it off to keep only the shortcut and the menu entry.
  • Connect the last-used agent when SolidCraft starts (off by default): each launch starts the agent you connected last (the default one if you never connected), in the background, once SolidCraft has looked for the installed agents. The panel’s header shows Starting…, then Ready, or Connection failed with Retry in place of Connect (the agent is missing, could not start, or ended while starting); the conversation says why. A design opened in place of the empty one SolidCraft starts with keeps the agent connected, as long as nothing was asked in that empty design’s chat.
  • Open the Assistant panel when SolidCraft starts (off by default): the panel (or its own window) is shown at each launch.

Neither applies when SolidCraft is started with --control for automation.

With AI features on, the panel opens from:

  • the Assistant icon in the application bar;
  • Ctrl+Shift+A (Cmd+Shift+A on a Mac), which also works from the chat’s own text box;
  • View settings (the last button of the navigation bar under the view) → Agent chat.

Nothing runs until you need it: SolidCraft looks for installed agents (a quick --version each, in the background) when the panel or the AI page first opens, and starts an agent only when you send a message or press Connect (or at launch, when you turned that on above).

The panel

Assistant uses the shared panel layout. Narrow panels keep the conversation above the composer. A wide floating panel places the conversation beside the provider controls and composer when both columns have enough width. The conversation, unsent draft and scroll state stay with the same Assistant when its placement changes. The shared header reports the agent state and pending approvals. The Assistant’s own native window keeps its existing layout.

Assistant fills the available side height. The compass and navigation bar use the remaining viewport space. Clicks and scrolling over the panel stay in the chat.

After clicking in Assistant, keyboard input stays in the chat, including when selecting text in an answer. Copy, Cut and Paste do not also operate on selected CAD geometry. Moving the pointer back over the model does not change this: click outside Assistant to return the keyboard to CAD. Closing or minimising the panel also releases it.

To select more of the conversation than fits in the panel, drag the selection to its top or bottom edge and hold it there: the conversation scrolls, faster the further past the edge you go, and the selection grows with it. It stays when its start scrolls out of view, and Copy takes all of it.

In Assistant, outside the message box, Ctrl+A (Cmd+A on macOS) selects the whole conversation: every prompt and answer, including those scrolled out of view, but not thoughts, tool cards, plans or notices. Ctrl+C then copies them in order as plain text, each under who wrote it. Esc or a click ends the selection; in the message box Ctrl+A selects only the draft.

  • Docking: on the left, Assistant is the first panel, before vertical history and the browser. The drag highlight shows the actual destination bounds. Drag the panel’s header (the dotted grip and the title) to the other half of the window and let go. The side it would land on lights up while you drag. You can also pick Left or Right under Preferences → AI → Panel. SolidCraft remembers the side.
  • Width: drag the panel’s inner edge. SolidCraft remembers it.
  • Its own window: the pop-out button in the header (a window with an arrow) moves the chat into a window of its own, named after the design, which you can move anywhere, including another screen. The window’s own close button closes the chat, which opens there again next time; Dock (the same place in its header) puts it back in the side panel. You can also pick Own window under Preferences → AI → Panel. SolidCraft remembers where the window was and how big.
  • Minimise (the line in the header) folds the panel into a small floating strip (a side strip in the docked layout) that keeps the chat icon, the agent’s state, and a count of anything waiting for your answer. Click the icon to unfold it. Close (×) hides the panel. Neither stops the agent; Stop does.
  • The agent switcher under the title picks the agent for this conversation; switching stops the one running. When an ACP agent reports configuration options (including OpenCode V2), a compact inline strip below it shows the agent’s model, reasoning effort, mode, and any other supported select options, wrapping when space is tight. Hover a selector for its full label and description. Their names and choices come from the agent: not every model offers adjustable reasoning effort. Changing the model can change or remove the effort choices. A selection is shown as applied only after the agent reports it; a rejected change leaves the confirmed selection in place and shows an error.
  • Older agents and API endpoints keep their model and mode drop-downs when they report those choices (an ACP agent’s legacy model/mode lists, or an endpoint’s /models). SolidCraft remembers the last model choice for each agent and puts it back on the next session when still available. An endpoint’s agent also keeps it as its default model. An agent that reports no choices shows no drop-down. When these model lists include more than one provider (Hermes and OpenCode name each model Provider · model, or say Provider: … in its description), the picker groups the models under their providers, each a header with the count of its models that a click folds or opens: the provider of the model in use starts open and the others folded, a search opens every provider with a match, and a provider folded by hand stays folded until the picker closes. Models with no provider named are listed under Other. An endpoint whose listing names its models Vendor: Model under ids vendor/model (OpenRouter) is grouped by vendor the same way; its rows read the model’s name, and the id stays in the tooltip and in the search.
  • Picking a model. Every model picker opens on a search box over the list the agent or endpoint gave at this connection: SolidCraft has no list of its own. The search covers ids, names and descriptions, and Enter takes the exact match or the first one shown. A model that isn’t listed can be typed: Use custom: … takes what is in the box, for an endpoint, an ACP agent with a model option, or an older agent that listed models. An endpoint takes it as it is and keeps it as its default model. An agent may refuse it: its model stays as it was and the chat says why. Only an endpoint keeps a typed model for the next session; an agent gets it back only if it then lists it. An endpoint that can’t list its models (it has no such address, or refuses the key) still shows its configured model, and the popup says why there are no others.
  • Reasoning effort is not permissions. The agent’s thinking setting does not change SolidCraft’s Ask / Edits / All approval policy. Reasoning options belong to the current session; changing them here does not rewrite OpenCode’s configuration or another OpenCode session.

Messages use right-aligned user bubbles and plain assistant text. Consecutive tool calls share an activity group: click a row to inspect its output; failed calls have an error tint. Approval cards retain the agent’s permission choices, and SolidCraft’s own approvals offer Deny, Always allow and Allow once. The composer’s permission menu uses the same Ask / Edits / All policy as Preferences, for the selected agent, and shows the actual changes used against the configured turn limit. Queue, Send now and Stop keep their existing behavior.

Files and pictures

A message can carry up to eight files, to show the agent what you mean: a concept picture, a sketch, a brief, a part to fit around.

  • + next to Send has Choose a file… (a file dialog for the types below) and Paste a picture (the picture on the clipboard).
  • Drop files on the chat. Files dropped on the panel (or its folded strip) attach to the next message, and the app doesn’t open them too. While files are dragged over the chat, it says so. The window gives no position while something is dragged, so the drop is the chat’s when the pointer was last seen over the chat; + always works.
  • The files show as chips above the text box: a picture’s thumbnail, the name, and × to take the file off. A picture is read off the window thread, so a large one never stops the window: its chip shows a spinner meanwhile, and Send waits for it. Send sends files with no text.
  • Pictures (PNG, JPEG, GIF, WebP) go to the agent as they are when they are at most 2048 px on the long side and 3.75 MB (a picture’s base64 then stays under 5 MB, so eight still fit in a prompt). Otherwise they are scaled down to 2048 px and re-encoded: as PNG when that fits, as JPEG when the picture has no transparency, halved until it fits; a GIF goes as its first frame. BMP and TIFF go as PNG. HEIC can’t be read: export it as JPEG or PNG first. An agent that takes no pictures gets none, and the message says which were left out.
  • Text files (UTF-8, up to 256 KB: Markdown, plain text, JSON, CSV, YAML, TOML, XML, SVG…) go in the message’s text, whole as far as the message’s 100,000-character cap allows: past it a file is cut (and says how much is missing) or named only.
  • CAD files (STEP, IGES, FreeCAD, Rhino, STL, 3MF, OBJ, glTF) are imported into the design at once, as components of their own. A file joins the message once its import is in (its chip shows a spinner meanwhile, and Send waits), and the message tells the agent it was added; a failed import says why and the agent is told nothing of it. Taking a part’s chip off the message leaves the part in the design (Undo removes it).
  • Every file is named in the message. In the conversation the attached pictures show under it as thumbnails (hold the pointer over one for a bigger preview, click it to open the viewer, where the file’s name is shown and ‹ › step through the message’s pictures, together with the picture of the view); the other files are listed by name. Pictures in any of the formats above are decoded in the background, with a spinner until they are in. A file that can’t be attached says why.

Files stay with their message: one queued while the agent works keeps its files, and one sent again after a sign-in sends them again. When several queued messages go as one prompt, their files go with it, at most eight; the rest are named in the chat and not sent. New chat clears the files waiting on the chips too.

Typing while the agent works

Each design tab has its own conversation, agent session, draft, attachments and queued messages. A new tab starts with an empty chat and a session of its own; a sign-in the chat confirmed in one tab still counts in the others, so a new tab doesn’t mark the agent as signed out. Switching away stops the current turn and keeps its completed changes in that design’s undo history. Returning restores the conversation; queued messages wait for Send. Closing a design closes its conversation. Conversations are kept only while the tabs remain open.

When the agent itself creates a new design or opens a file, its conversation follows the new design and the same turn continues. Its completed changes stay in each design’s own undo history. The original tab starts a fresh conversation if you return to it. Opening a file that is already in another tab returns an error instead of displacing that tab’s conversation; select that tab yourself to continue there. Your own New, Open and tab switches still stop the current turn. Stop also cancels a file the agent is still opening.

The input stays enabled during a turn. Enter queues the message: it shows in the conversation as a Queued bubble that you can Edit or remove (×) until it is sent. Several queued messages go as one prompt, in order, when the turn ends normally.

  • Send now (on the bubble, or Ctrl+Enter / Cmd+Enter in the input) stops the current step, waits for the agent to say it stopped, then sends the queue as the next prompt, with a short note that the agent’s previous step was interrupted. What the interrupted message changed undoes as one step, like any other message.
  • Stop (the square next to the input, or Esc in the input) stops the agent and keeps the queue: the bubbles wait for Send. If an external ACP agent does not finish its cancelled prompt within five seconds, SolidCraft disconnects it and stops its process in the background. Completed design changes stay undoable, and the queue stays put. Reconnect to start a new agent session.
  • New chat (the header button, Ctrl+Shift+L / Cmd+Shift+L with the chat focused) stops what runs, clears the conversation, the queue and the files on the chips, and starts a fresh session on the same agent, keeping the agent and its mode. It asks first when a message is running or queued, or files are waiting. The design’s undo history is untouched.

When you change the design yourself between two messages (a parameter, a feature, an undo, the History marker), your next message tells the agent so first, in a short note the conversation doesn’t show: which parameters changed and from what to what, which features are new, edited or removed, and where the History marker went. The note asks the agent to read the design again instead of picking edges and faces where they were before. A new chat or a new agent session has seen nothing yet, so its first message carries no note.

Agents

Preferences → AI → Agents lists every agent the chat can use, each with its kind (a program or an endpoint), version or model, whether it looks signed in, Test, Default (the one a new conversation starts with), its own permission setting, and Edit and Remove for the ones you added.

Found by themselves

SolidCraft looks for agents that speak the Agent Client Protocol on your PATH and in the usual install folders a desktop app’s PATH often misses (npm’s global folder, nvm, Volta, bun, ~/.local/bin, ~/.grok/bin, Homebrew, %APPDATA%\npm). For each one it shows its version (from --version, given three seconds) and whether a login was found. Version checks and agent sessions also use those folders on their PATH, so adapters can find runtimes such as Node when SolidCraft is opened from Finder or the Dock. Existing absolute PATH entries keep their order; no shell startup files are run. If a detected Node adapter fails with SyntaxError: Unexpected token 'with' on an older Node, SolidCraft checks these folders for a Node version that supports import attributes. For an adapter with an env node shebang, it retries with that Node and keeps the same interpreter for the chat session, including programs the adapter starts. These extra checks run in the background with a shared three-second limit for finding Node. Working adapters keep their original launch command. If no suitable Node is found, the agent’s error names the old version and asks you to update Node and press Refresh. SolidCraft only checks whether the agent’s own login file (~/.claude/.credentials.json, ~/.codex/auth.json, ~/.gemini/oauth_creds.json, …) or one of its key variables exists. It never reads the file and never starts the agent to find out. On macOS Claude Code keeps its login in the keychain, so SolidCraft doesn’t guess there. A found login file may be expired: Test or a chat connection verifies it. A successful ACP connection shows signed in; an authentication rejection shows not signed in. These results apply to that agent until a newer connection result or Refresh, which checks the installed agents again. An unrelated connection error doesn’t change the last known login state.

AgentWhat to install
Claude Codenpm install -g @agentclientprotocol/claude-agent-acp (the ACP adapter for Claude Code)
Codexnpm install -g @agentclientprotocol/codex-acp
Google AntigravityGoogle’s ACP server, a separate download from Google’s agy command (which has no ACP mode): take the zip for your system from the ACP registry, unzip it and put agy_acp_server.par (agy_acp_server.exe on Windows) on your PATH. The zip also holds localharness_external: leave the two together. It signs in itself (browser).
OpenCodenpm install -g opencode-ai (opencode acp)
Mistral Vibeuv tool install mistral-vibe (vibe-acp)
XAI GrokGrok Build installer, then grok login (grok agent stdio speaks ACP)
GitHub Copilot CLI, Qwen Code, gooselisted with their install command when missing

The panel lists agents that aren’t installed with their install command and a Copy button.

For Mistral Vibe, put vibe-acp on your PATH and refresh the agent list. Follow Vibe’s sign-in prompt, run vibe --setup in a terminal, or set MISTRAL_API_KEY before launching SolidCraft. See Mistral’s ACP setup guide. Refresh looks again after you install one. An agent whose program is there but fails its --version check without printing a version (an install that didn’t finish, such as an npm install whose setup script didn’t run, or a program built for another kind of computer) is still listed, marked does not start with the system’s reason. Connecting to it first says why and gives its install command; reinstall it until --version works in a terminal, then press Refresh. A check that runs out of time is not counted as a failure. Test starts an agent briefly, asks it for a session and ends it again.

Flatpak

The Flatpak sandbox has no home-folder or network access by default. Agents installed through nvm or ~/.local/bin can therefore work in a terminal while SolidCraft reports that no agent was found. For Claude Code and its ACP adapter, run:

flatpak override --user io.tangencylabs.solidcraft --filesystem=~/.nvm:ro --filesystem=~/.local/bin:ro --filesystem=~/.local/share/claude:ro --filesystem=~/.claude --filesystem=~/.claude.json --share=network

These permissions apply to SolidCraft for your user account:

  • ~/.nvm:ro exposes Node and globally installed npm adapters read-only.
  • ~/.local/bin:ro exposes locally installed executables read-only.
  • ~/.local/share/claude:ro exposes Claude Code’s native installation read-only, including the targets of links in ~/.local/bin.
  • ~/.claude and ~/.claude.json allow Claude Code to read and update its sign-in and configuration files.
  • --share=network lets the adapter reach its service. It also gives SolidCraft network access, including making an enabled control server reachable from the host.

Restart SolidCraft after applying the override. Other agents or installation folders may need different filesystem permissions.

XAI Grok

Install Grok Build and run grok login in a terminal to sign in with your Grok account. In SolidCraft, open AI → Refresh, select XAI Grok, then Connect. You can make it the default under Preferences → AI → Agents.

SolidCraft reuses Grok Build’s sign-in in ~/.grok/auth.json; it checks only whether that file exists and does not read or copy its tokens. If sign-in is needed, the panel offers Grok’s own sign-in methods. You can also run grok login again and reconnect. Grok controls model access and usage limits for your account. This connection does not require an API key in SolidCraft. Grok Build also supports XAI_API_KEY for users who choose API authentication.

Add agent…

  • Installed agent: your own entry for an installed agent, for example with other arguments.
  • Custom command: any other program that speaks ACP on stdin and stdout, such as my-agent --acp. SolidCraft starts it directly, not through a shell.
  • OpenAI-compatible API: a preset (Ollama http://localhost:11434/v1, LM Studio http://localhost:1234/v1, OpenClaw http://localhost:18789/v1, OpenAI, Mistral AI https://api.mistral.ai/v1, Google Gemini https://generativelanguage.googleapis.com/v1beta/openai, Mammouth AI https://api.mammouth.ai/v1, OpenCode Go https://opencode.ai/zen/go/v1, DeepSeek https://api.deepseek.com, Qwen https://dashscope-intl.aliyuncs.com/compatible-mode/v1, Cheaper Inference https://api.cheaperinference.com/v1, OpenRouter https://openrouter.ai/api/v1) or a custom URL, the model name, and an optional key. OpenCode Go models are routed to their endpoint by themselves (/responses, /messages or Gemini for those families, /chat/completions for the rest), each conversation request carrying the session header Go asks for, so one preset covers every model. A model the list does not know is retried on the next protocol when Go names the mismatch. For OpenClaw, turn on its chat endpoint (gateway.http.endpoints.chatCompletions.enabled in its config) and use its gateway token as the key; the models it lists are its agents, such as openclaw/default.
  • Anthropic-compatible API: a preset (Anthropic, DeepSeek https://api.deepseek.com/anthropic, Qwen https://dashscope-intl.aliyuncs.com/apps/anthropic) or a custom URL, for an endpoint that speaks the Anthropic Messages API ({base}/v1/messages). This covers Anthropic itself and providers that offer an Anthropic-compatible endpoint, such as DeepSeek and Qwen.
  • The Model picker lists the endpoint’s models, without a key where the endpoint allows that (OpenRouter does), once you leave the address and key fields; opening it asks again after a failure. You can also type a model.
  • An endpoint’s card under Preferences → AI → Agents has the same Model picker. While the agent isn’t running, opening the picker lists the models (just viewing the page asks nothing), and a pick becomes the model for its next connection. While it runs, a pick changes the chat’s model at once.
  • The key goes into the system keychain (Keychain on macOS, Credential Manager on Windows, the Secret Service on Linux). By default it is only sent over HTTPS or to this computer. Test asks an OpenAI-compatible endpoint for its models; an Anthropic-compatible one gets a short message, because not every such endpoint lists models (Qwen’s has no model list). For these agents SolidCraft runs the conversation itself, with the same tools, limits and questions as for a coding agent, and no cap on model requests per message (Stop ends it). Request timeout is how long one model request may take: 300 s by default, from 30 s to an hour. A slow local model (a large model on modest hardware, or a long message with many tool results) may need more; raise it here or in the endpoint’s entry under Agents, where a change applies from the next request. A request that runs out of time ends the message with the limit it hit and where to raise it. Reasoning effort is optional and empty by default: nothing is sent and the provider’s own default applies. When set, the value goes out as reasoning_effort on each /chat/completions request; accepted values differ per provider and model (for example low, high or max). A value you set is never replaced by SolidCraft: if the endpoint refuses it, the refusal is shown instead of being retried with none.

Edit on an agent you added opens the same dialog filled with its name and command, or its protocol, endpoint, model and request timeout, and Save changes it in place: it keeps its id, its permission setting and the tools you always allow for it. A stored key is never shown: leave the key field empty to keep it, or type a new one to replace it. Moving an endpoint with a key to a plain http:// address elsewhere requires the confirmation described below. The running conversation takes a new request timeout at once; a new command, endpoint or model applies the next time the agent connects.

For a private or on-premises OpenAI-compatible endpoint that requires a key over HTTP, enable Allow insecure HTTP in its Add/Edit dialog and confirm the displayed endpoint. This option is off by default and applies only to that endpoint. The API key and conversation travel unencrypted: anyone with access to the network may read or change them. Use it only on a network you trust. The key remains in the system keychain. Test, model discovery and chat all use the same permission; redirects are not followed. Changing the URL or preset clears consent and requires confirmation again. Turning the option off keeps the stored key and disconnects that endpoint’s conversations, including those in other document tabs.

For Ollama, choosing a model checks its reported capabilities in the background. If its capabilities list lacks tools, a warning below the model picker explains that the model can only chat and suggests choosing one with tool support. Results are cached in memory per base URL and model, with a limit of 128 entries. SolidCraft recognises Ollama through /api/version at the endpoint’s origin or the Ollama preset. Failed checks and older servers without a capabilities field show no warning. The check never blocks sending a message.

Local models

Choose a model that supports native tool calling to read or change the design. With Ollama, look for tools in the model’s capabilities; a model that only supports chat cannot operate SolidCraft. Other OpenAI-compatible endpoints also need a model with native tool support.

If tools were available but a completed turn made no tool calls or changes, the panel shows a short notice when the reply was empty, or when an explicit edit request received only text. Ordinary answers to questions do not show it. Edit requests are recognised conservatively from English opening words such as “make”, “rename”, or “can you move”; other phrasing may stay quiet. Cancelled and failed turns keep their existing messages. Text describing a call is never run. A reply that stopped at its length limit, or that the model declined, says that instead of the tool-calling notice: an endpoint reports it as max_tokens or refusal (Anthropic), or as length, content_filter or a refusal (OpenAI-compatible). A tool call cut off at the length limit is not run.

An agent inside WSL (Windows)

An agent installed inside WSL keeps its own settings, skills and sign-in there. Add it as a Custom command that starts it through wsl.exe, for example:

wsl.exe -d Ubuntu -e bash -lc claude-agent-acp

bash -lc runs your login shell’s setup, so the agent is found where your PATH puts it (npm, nvm, Volta). When the command’s program is wsl or wsl.exe, SolidCraft gives the agent its working folder and the solidcraft-app server’s program as WSL shows them (C:\Users\… becomes /mnt/c/Users/…). The server itself still runs on Windows and reaches the app at 127.0.0.1, so this works with WSL’s default networking. If you moved WSL’s drive mounts away from /mnt (automount.root in /etc/wsl.conf), those paths won’t match and the agent can’t start the server.

Signing in

If a coding agent isn’t signed in, it says so and the panel shows Sign in to the agent with the agent’s own ways to sign in:

  • A command for a terminal, e.g. claude-agent-acp --cli auth login --claudeai, with a Copy button. Run it in a terminal, sign in there, then press I’ve signed in: connect again.
  • A button for agents that sign in by themselves (Codex’s ChatGPT login opens your browser).
  • A variable to set for agents that take a key from the environment. The panel has no field for a coding agent’s key, on purpose.

Whatever you typed before signing in is sent once the agent is ready.

What the agent can do

The agent reaches the open design only through SolidCraft’s own tools (the MCP server), which SolidCraft hands it when the chat starts. That server is named solidcraft-app, so an MCP server you set up yourself under the name solidcraft (as in client configuration) doesn’t take its place:

  • Read the design: list commands, inspect features, sketches and parameters, measure, take pictures of the view.
  • Change the design: run any modelling command, alone or in a batch. Changes show in the window as they happen.
  • One undo per message: everything the agent changes while answering one message becomes a single undo step, named after your message. The conversation shows N changes, one undo step with Undo these changes while nothing else has happened since.
  • Tool cards: each tool call shows as a card with its state; click it to see what it returned.
  • Formatted text: the agent’s answers show as Markdown: headings, lists, quotes, tables, code blocks (with Copy), bold, italic, code and links (web links only; each link is a button that opens it in your browser). A wide table scrolls sideways; one without the |---| line under its header, or with more than 200 rows or 2000 cells, shows as plain text. What you type, and the agent’s thoughts, stay plain text.
  • Attach view: tick it to send a picture of the current view with your message, for agents that take pictures. The picture is taken when the agent starts on the message and then shows as a small thumbnail under it. Hold the pointer over the thumbnail for a bigger preview that stays while you do; click it to open the viewer (the message’s pictures one at a time, with ‹ and › or the arrow keys, closed with Esc or a click outside). A long message scrolls inside its box, so Send and the buttons beside it stay in view.
  • Attach selection: tick it to send what is selected in the model with your message, so the agent knows what “this edge” or “that feature” is. The box shows how many things are selected, counted as you clicked them (the two halves of a rim you clicked once are one). Faces and edges go with the names and numbers the agent’s tools take and with their size and place (so they can be found again after an edit), features with their name and kind, bodies with their name. The agent gets them as text after your message; the conversation shows your message as typed (picks as pills) with the things that are not in a pill as chips below it, grouped (Elements, Features, Bodies) with a count; hover a chip to light its target in the model and to see what the agent was told about it. A chip whose target has since left the design (deleted, or changed so its face or edge is gone) is struck through, with a note on hover. At most 64 things go with one message; the rest are counted (“+N more”), not dropped silently. The selection is read when you send, also for a message that waits in the queue.
  • Pick context: press it to choose what the message is about on purpose. A picker opens with three lists, Elements (faces, edges, vertices), Features (steps of the timeline) and Bodies. A click in the model goes into the list that is active (click a list to make it active). A step clicked in the History or the Browser goes into Features, and a body clicked in the Browser into Bodies, whichever list is active. Click a picked thing again to take it out. The arrow beside a list shows what it holds by name, as in every dialog; the × on a name takes it out. OK puts the pick into the message as a pill, «1: Plate · 3 faces», at the text cursor (at the end when the box was never used). Typing @ where a word starts does the same: the @ is taken out and the picker opens, and the pill goes where it was. Pick as often as you like; each pick is a pill of its own, so “make «1: …» match «2: …»” says which is which. A pill is part of the text: it is edited, selected, cut and pasted like text, drawn as a pill, and Backspace or Delete next to it takes it whole (taking the mark out drops the pick). Hold the pointer on a pill to light its target in the model and to list what it holds; a pill whose target has left the design is struck through. The agent gets your text with the marks numbered 1, 2, 3 in reading order, and under it each numbered group with the faces, edges, features and bodies in it; what Attach selection adds is listed apart, as not placed in the message. Cancel, or OK with nothing picked, adds nothing.

Custom instructions

Preferences → AI → Custom instructions holds your own text for the agent: how you like to work, rules to keep to (“don’t start a new design from the chat”, “sketch on XZ with explicit plane vectors”). Every chat starts with it, after SolidCraft’s own note about where the agent is, by the same route that note takes: the system prompt of an endpoint’s requests, or the session an ACP agent opens. An agent that reads neither doesn’t get it. A change applies from the next chat (Connect or New chat), not to the one running. Blank text adds nothing; at most 4000 characters are kept.

They are for this chat only: an MCP client you run yourself keeps its own instruction files, and one text applies to every design.

Permissions

Preferences → AI → Permissions decides what the agent may do without asking, for every agent; each agent can have its own setting in its row.

  • Ask every time: every change to the design and every file action waits for Allow, Always allow or Deny in the panel.
  • Auto-allow model edits (the default): modelling commands run at once; saving, opening, exporting, importing, starting a new design, any command given a file name, and undoing past the start of your message still ask.
  • Allow everything: nothing asks. The page warns you: the agent can then save, overwrite and replace your design.

Always allow remembers that command for that agent. The page lists what you allowed this way, per agent, with Reset.

These settings cover SolidCraft’s tools. A coding agent’s own tools (its shell, its file edits) follow the agent’s own mode, which you pick in the panel’s header. When an agent asks before using one of the chat’s own SolidCraft tools and names it exactly as the Claude adapter does (mcp__solidcraft-app__batch), SolidCraft answers for you, since its own gate checks the call, and notes it in the chat as Auto-allowed. Any other question appears in the panel, including a SolidCraft tool an agent words differently and a SolidCraft MCP server you added to the agent yourself. With Allow everything, or after Always allow, those questions are answered the same way and noted too.

What counts as “a command given a file name”: a parameter that holds a file or folder (file, dir, output, …), or path and source when the value reads like a file name (a separator, ~, a leading dot or a known extension), or any value of those in a command that exports, imports, saves, opens or inserts. A sweep’s or pattern’s path (curve ids) and an occurrence’s path (ids) are geometry and run without a question.

What it can’t do

  • Files wait for you unless you chose Allow everything. Cancel or Stop declines everything that’s waiting, and a question nobody answers is declined after four minutes. The MCP server’s folder limits still apply after you allow: the design’s folder, the recovery folder and the server’s own scratch folder.
  • An optional limit per message. There’s none by default. Untick Unlimited on the AI page to cap the changes the agent may make per message (it starts at 24). Reading the design doesn’t count. At the limit, it’s told to stop and report what’s left; send another message to let it carry on. Stop always works.
  • No crash reports, whatever the permissions. Crash reports can hold other designs and file paths.
  • Not the rest of the app. It can’t click the interface, change settings, quit, or drive the chat itself.
  • Its own tools stay off where we can turn them off. SolidCraft declares no file system and no terminal to the agent, runs it in an empty working folder of its own, and asks the Claude Code adapter to turn off its shell, file and web tools for the session.

When the panel’s agent is stopped, another agent is picked, or SolidCraft quits, the agent and every program it started (the CLI it wraps, its MCP server) are stopped.

Privacy

Nothing goes anywhere except to the agent you run, or the endpoint you added. SolidCraft starts a coding agent as a program on your computer and talks to it over its standard input and output. What the agent sends to its own service, under your account and that service’s terms, is between you and that service, exactly as when you use the agent in a terminal. SolidCraft never sees, stores or passes on a coding agent’s password, token or key. An endpoint’s key is kept in the system keychain and sent only to that endpoint. SolidCraft’s own connection to the agent’s tools uses a random per-launch token in a file only you can read, which never passes through the agent.

Trying it without an account

A scripted test agent is in the source tree. It makes a block, rounds it and measures it, with no model behind it:

cargo build --release -p solidcraft-ui-egui --example acp_test_agent

Then add it under Preferences → AI → Add agent… → Custom command with the path of target/release/examples/acp_test_agent. Put “save” in your message to see an Allow / Deny card, or “ask” to see the agent ask its own permission.

Benchmarking the Assistant

solidcraft-cli bench-assistant runs the eleven tasks of the standard model test (v1, #4851) against a desktop app that is already running with a control port, and checks each result on the design itself. Use it to see how well a local model drives SolidCraft before you post its results.

solidcraft --control 9271
# In another terminal:
solidcraft-cli bench-assistant --connect 127.0.0.1:9271 \
  --base-url http://localhost:11434/v1 --model qwen2.5:7b \
  --json results.json --markdown results.md

Each task replaces the open design with a new one, so save your work first. The command adds the endpoint as an API agent named “bench”, starts a new chat with All permissions for every task, sends the prompt once, and waits for the turn to end. It runs only the setup the test prescribes (the box some tasks start from) and does none of the modelling itself. When it finishes it stops the agent and puts your permission setting back.

  • The control token comes from --token, SOLIDCRAFT_CONTROL_TOKEN, or the app’s token file, in that order.
  • --tasks 1,2,5 runs only those tasks, --repeat N runs the test N times, and --timeout-s (default 300) is each turn’s deadline; a turn that runs past it is stopped and fails as “timeout”.
  • A failed task gets one of the issue’s failure categories (“wrote the call as text”, “wrong or unknown command”, “wrong arguments”, “the endpoint refused tools”, “an empty reply”, “only talked”, “gave up after errors”, or “hit the request limit”), or “wrong result” when the calls worked but the design is not what the task asks for.
  • --markdown writes the issue’s comment template filled in; add the quantization, download size, and runtime before you post it. --json keeps each task’s failed rules, conversation, and design before and after. Both files are rewritten after every task.

The show/hide shortcut defaults to Ctrl+Shift+A (Cmd+Shift+A on macOS). Change, clear or reset Toggle Assistant (ui.assistant) in Preferences → Shortcuts. The binding is saved with the other keyboard shortcuts; the assistant button tooltip follows it. Disabling AI features also disables the toggle.

Local models with the Assistant

You can use a model running on your computer to work on the open design in SolidCraft’s desktop Assistant. See Agent chat for the panel’s controls.

What you need

  • Ollama, LM Studio, or another OpenAI-compatible server running on your computer.
  • A model that supports tool calling: this lets it ask SolidCraft to read or change the design. On Ollama’s model page, look for the tools tag, or run ollama show <model> and look for tools under capabilities. Tool support alone does not guarantee good results; see the table below.
  • Enough graphics memory (VRAM) for the model and its context (the conversation and tool instructions). See the table below for sizes.

Set it up

  1. Start your server and load or download the model you want to use.
  2. In SolidCraft, open Preferences > AI > Add agent… and choose the OpenAI-compatible API. Choose Ollama, LM Studio, or a custom URL.
  3. Enter the server’s base URL, such as http://localhost:11434/v1 for Ollama, and the model name. Use Test, then add the endpoint.
  4. Open the Assistant and select your endpoint. Choose the model in the model list if the server provides one.
  5. Choose permissions in Preferences or the chat’s permission menu: Ask asks before changes and file actions; Edits allows model edits but still asks before file actions and replacing the design; All allows actions without approval, including saving, overwriting, and replacing the design.

Which models work?

These results were measured with the standard test (v1) in #4851. The table in that issue is the up-to-date one.

ModelSize / quantPassed (v1)
Muse Glimmer 30BUnsloth UD-Q3_K_XL, 12.4 GB11/11
Qwen3.8 27BUnsloth UD-Q4_K_XL11/11
gemma4 (latest, 7.5B)Q4_K_M, 6.1 GB11/11
gemma4:12bQ4_K_M10/11
gpt-oss:20bMXFP4, 12.8 GB10/11
qwen3:14bQ4_K_M, 8.6 GB8/11
qwen3:8bQ4_K_M7/11
qwen2.5:7bQ4_K_M, 4.4 GB4/11
qwen2.5-coder:7bQ4_K_M, 4.4 GB0/11 (writes every call as text)
gemma3:4bQ4_K_M0/11 (no tool support: HTTP 400)
deepseek-r1:14bQ4_K_M0/11 (never calls a tool)

For about 8 GB of graphics memory, start with gemma4 (the default tag, 6.1 GB). With 16 GB, Muse Glimmer 30B or gpt-oss:20b also work well.

Troubleshooting

  • The model replies with JSON or code, but nothing happens. It is not calling tools well. Try another model from the results above.
  • “HTTP 400 … does not support tools.” The model lacks tool support. Choose one with the tools capability.
  • Empty replies. The context may be too small for the tool instructions. Increase the server’s context size (num_ctx in Ollama), then try a new chat.
  • Slow answers. The model may be too big for your VRAM and spilling work to the CPU. Try a smaller model or a smaller quantization.

Measure a model yourself

Follow the standard test in #4851 to compare your model with these results. It gives the exact prompts, starting designs, pass rules, and a template for sharing results.

Contributing to SolidCraft

Agents: start from the router in AGENTS.md, which points to a short skill in skills/ for each kind of work. This page stays the full human guide.

SolidCraft is an open-source parametric 3D CAD app written in pure Rust. You sketch on a plane, constrain the sketch, turn profiles into solids, and change a parameter to watch the timeline rebuild. It runs as a desktop app (Windows, macOS, Linux), in the browser (wasm + WebGPU/WebGL2), headless from a CLI, and as an MCP server that AI agents can model with.

Help is welcome, whether that’s a one-line fix or owning a whole area. This page covers how to build it, how the code is laid out, the few rules that aren’t negotiable, and where help is needed most.

Before choosing a task, check the command inventory and roadmap for implemented capabilities and current priorities.

Questions, ideas or want to say what you’re working on? Join the Discord.

Kernel work goes in solidcore/

Kernel and geometry work is welcome: booleans, fillets, chamfers, shell, offsets, sweeps and lofts, threads and holes, STEP/IGES import and export, tessellation. It goes in solidcore/, the vendored solidcore kernel, following solidcore/docs/layers.md and skills/solidcraft-layers/SKILL.md. SolidCraft’s own crates/kernel, crates/smlib and crates/geom only shrink toward the seam (ops, tess, record); please don’t grow them.

A kernel pull request targets main like any other; there is no kernel branch, and the tangency-labs/solidcore repository is a mirror, so don’t open pull requests there. A change that touches the kernel and the app is one pull request. A big feature in several steps goes as stacked pull requests into main, each one landing in order on top of the one before. When a series sets a shape (a type, an error shape, an API, a file format or a schema), its first pull request gets a design review before the later parts are accepted, so open it first and say it is the first of a series. Crashes and data loss are still taken first.

A kernel pull request follows solidcore/docs/layers.md (apps go through solidcore-ops; one implementation per job) and §7 of solidcore/docs/definitions-and-certificates.md:

  • No acceptance is decided by sampled checks.
  • No test expectation is loosened and no test input is edited to make a result pass.
  • A refusal is a typed error that names the cause and, where one exists, the size that fits.
  • Every golden the change moves is explained in the pull request: the cases, the old and new values, and why the new ones are right.

Drawings work targets the drawings branch, not main.

Updating a PR that was moved from solidcore

solidcore’s open PRs were re-opened here on moved/solidcore-N branches in tangency-labs, so contributors without write access can’t push to them. To update one:

  1. git fetch https://github.com/tangency-labs/solidcraft moved/solidcore-N:moved/solidcore-N, then git switch moved/solidcore-N.
  2. Commit your changes on it and push it to your fork: git push <your-fork> moved/solidcore-N.
  3. Open a PR against main that says “Replaces #M” (M is the moved PR’s number). The moved one gets closed.

Get it running (about 10 minutes on a fast machine)

You need rustup. The repository pins Rust 1.99.1 in rust-toolchain.toml, the version CI builds with, and rustup installs it with rustfmt, clippy and the wasm32-unknown-unknown target the first time you run cargo here. The code itself builds on Rust 1.95 or newer (rust-version in Cargo.toml, required by egui/eframe 0.36). For the web build you also need trunk (documentation).

git clone https://github.com/tangency-labs/solidcraft && cd solidcraft
cargo run --release -p solidcraft -- --sample      # desktop app with a sample part
cargo xtask test                                   # unit, engine and UI-scenario tests
cargo xtask ci                                     # everything a PR must pass (see below)

SolidCraft is the product, repository, Cargo package and executable name. Build the desktop app with cargo build --release -p solidcraft, or the CLI with cargo build --release -p solidcraft-cli. Run the CLI with cargo run --release -p solidcraft-cli -- commands (or target/release/solidcraft-cli commands after building). The old name appears only in the compatibility shims listed in docs/compatibility.md; don’t add new uses of it.

Web: cd apps/solidcraft-web && trunk serve --release, then open the printed URL (?sample opens the sample part, ?webgl forces WebGL2). Saving and opening files in the browser needs HTTPS or localhost, because it uses the Origin Private File System.

Windows from Linux: cargo xwin build --release --target x86_64-pc-windows-msvc -p solidcraft.

NixOS: nix develop

NixOS has no /usr/lib, so nothing is on LD_LIBRARY_PATH and a plain cargo run finds none of the libraries the app loads at run time: the Wayland ones winit opens the window through, the EGL loader wgpu renders through, and libdbus-1, which rfd’s file dialogs reach xdg-desktop-portal through (a dialog then never appears and the pick returns no path):

ERROR solidcraft: winit EventLoopError: ... The wayland library could not be loaded
ERROR eframe::native::run: Exiting because of error: WGPU error: Failed to create surface for any enabled backend: {}
ERROR rfd::backend::xdg_desktop_portal::portal::libdbus: Can't connect to a portal: libdbus-1.so not found

nix develop (the flake.nix in the repository root) supplies Wayland, X11, EGL, Vulkan-loader and D-Bus runtime libraries. It also exposes the NixOS driver manifests in /run/opengl-driver/share, so Vulkan can discover the system drivers. Enable hardware.graphics.enable on NixOS; other Linux distributions may need a matching nixGL wrapper. The default development shell retains the Rust/C toolchain already on PATH; nix develop .#rust supplies the pinned toolchain and builds in target/nix.

To launch, use nix run .#solidcraft -- --sample. To install the app’s terminal commands, desktop entry and generated icons, use nix profile install .#solidcraft. nix build alone creates a build result; it does not install a launcher in your desktop menu. The installed solidcraft command runs the wrapped app; solidcraft-cli is included as well. See Nix installation and troubleshooting for remote-flake commands, desktop integration and how to distinguish an old executable on PATH.

How the code is laid out

The architecture page has the layer table. The short version:

  • crates/geom: vectors, planes, meshes, measures.
  • crates/sketch: our own constraint solver, plus profile finding.
  • crates/dynamics: multibody dynamics: rigid bodies, joint constraints, gravity and springs, integrated in time (RFC #9719). It depends only on geom.
  • crates/kernel: the B-rep kernel boundary: solids, extrude/revolve, booleans, fillets, meshes, measures and STEP, behind our own API (implemented with solidcore). crates/smlib is the safe wrapper around solidcore, and solidcore is vendored in this repository at solidcore/; .github/workflows/solidcore-mirror.yml mirrors changes to it back to tangency-labs/solidcore.
  • crates/doc: the document. It holds parameters and expressions, the feature timeline and its incremental rebuild, components and occurrences, joints, appearances, and persistent naming.
  • crates/io: design files and mesh formats (3MF, STL, OBJ).
  • crates/engine: the session and the command registry. Everything the user can do is a command, and the toolbar, dialogs, scripts, CLI, control channel and MCP all run the same commands.
  • crates/ui-egui: the desktop/web front end (egui + wgpu). It’s swappable; nothing below it depends on egui.
  • crates/mcp, apps/solidcraft, apps/solidcraft-cli, apps/solidcraft-web.

cargo xtask layers fails the build if a lower layer depends on a higher one.

Whether a change belongs in SolidCraft or in SolidCore is decided by solidcore/docs/layers.md. Its tests: the kernel (solidcore) computes geometry any client could use; ops (solidcore-ops) is a recipe of kernel calls for a workflow, with no geometric quantity of its own; the app (SolidCraft) holds state and maps input to commands, with no geometry math.

Rules that aren’t negotiable

  1. Independent. SolidCraft is independent. Behaviour is designed from public documentation, general CAD conventions and user feedback. Reference tests use analytic values and open-source tools. We don’t drive, script, automate or extract data from commercial CAD products, and we never copy another product’s code, icons, artwork, help text, tooltips, templates or materials. Command ids are our own (solid.fillet).
  2. No GPL/LGPL/AGPL code, not even reading it. That rules out OpenCASCADE, FreeCAD (including planegcs), SolveSpace, CadQuery/OCP, LibreCAD and QCAD. SolidCraft is MIT OR Apache-2.0, and its solver and blends are written from the maths. (Running an open-source application to get reference values for a test is fine; reading its source is not.) Permissive dependencies are fine. Note which one you’re adding and why in the PR. THIRD-PARTY-LICENSES.txt lists it once the change lands: the Generated files workflow rewrites it on main. Nightly/manual CI also scans advisories, bans and sources with cargo-deny, and a PR that changes Cargo.lock or a Cargo.toml checks the advisories; see the local command and coverage limits.
  3. Every asset is original or openly licensed and has a row in ATTRIBUTION.md. Icons are drawn in code (crates/ui-egui/src/icons.rs). cargo xtask assets enforces this.
  4. Never crash. People trust a CAD app with their work.
    • Non-test code never uses unwrap, expect, panic! or indexing on input-derived data. There’s no unsafe outside apps/solidcraft/src/open_documents.rs (macOS file-open events), apps/solidcraft/src/native_crash.rs (Windows native crash capture) and crates/threedx-client/src/macos.rs (the 3Dconnexion driver’s client API on macOS), and the compiler denies it.
    • An unsupported case returns an error (“not supported yet: …”); it never panics.
    • Every call into the vendored kernel goes through solidcraft_kernel::guard.
    • Every crash fix comes with a regression test.
  5. Everything is a command, with a test. A new feature means a CommandSpec in crates/engine/src/cmd/ with input and output schemas (see Adding a command; a test warns about a command without them, and will fail it once required), engine tests that measure the result (volume, area, face/edge/vertex counts against a value you can derive), and for UI work a scenario (below). A command on the toolbar also ships with its help tooltip (step 6 of Adding a command).
  6. One set of camera controls. Every page or canvas that shows a view (the modelling viewport, the Render preview, and any new page such as a flat pattern or drawing) calls navigation::handle (crates/ui-egui/src/navigation.rs) for its camera input, with NavCaps saying whether it can orbit (off for flat 2D pages) and pick. New canvases must use navigation::handle; never read drags, the wheel or pinch to move the camera yourself. The hand_rolled_camera_input_is_rejected test fails if a page does.

Labels, parameter docs, messages and docs use the terms in the glossary: one term for each idea. A change that adds a new idea adds its term there.

AGENTS.md has the long form of these rules. It’s written for AI agents, but it’s the most complete description of the house style.

Tests

WhatWhereRun
Unit and engine testsnext to the code, or *_tests.rs filescargo xtask test (or cargo test --workspace --profile ci)
UI scenarioscrates/ui-egui/tests/scenarios/*.jsonpart of cargo test
Reference testscargo xtask oracletests/oracle/ (see below)

The reference for what the kernel builds is the kernel-cases ladder (tests/kernel-cases/ladder/, cargo xtask kernel-cases --ladder, docs/kernel-ladder.md).

Kernel cases go to solidcore. A case that checks a kernel operation (a boolean, a blend, a shell, a sweep, an import’s geometry) is a new entry in solidcore’s corpus, solidcore/crates/solidcore/tests/kernel_cases/<area>/ in this repository, with an answer in closed form or exact. Draft it as a rig case here, export it with cargo xtask kernel-cases --area <area> --export DIR, add DIR/<area>/<case>.json and its .bodies.json.gz under solidcore, and delete the draft; or write the entry by hand as that folder’s README describes. The mirror workflow pushes the change to tangency-labs/solidcore. tests/kernel-cases/ takes no new cases: its CASES list only shrinks (cargo xtask lists). App-level cases (what a command does, the UI, product rules) stay in SolidCraft, as tests in their area’s existing test file or UI scenarios.

The naming corpus checks persistent names: every name a corpus design stores still finds a face or an edge, and two builds name its faces and edges alike (cargo test --profile ci -p solidcraft-cli --test naming_corpus). docs/mutation-testing.md shows how to find kernel behaviour these black-box suites miss.

UI scenarios drive the real app headless: start a tool, click a plane or a sketch point, press keys, then assert on the result.

[
  {"start": "sketch.create"},
  {"click": {"plane": "XY"}},
  {"start": "sketch.rectangle.two_point"},
  {"click": {"sketch": [0, 0]}},
  {"click": {"sketch": [40, 30]}},
  {"expect": {"errors": 0}}
]

A new .json file in that folder is picked up automatically. To record a bug before it’s fixed, put {"pending": "why"} in the scenario, and remove it in the PR that fixes the bug.

A failing test without its fix can’t merge as-is: it would turn main’s CI red for everyone. Either include the fix in the same PR, or mark the test #[ignore = "fails until #123 is fixed"] (it can then merge right away), and remove that line in the PR that fixes the bug.

Use the ci profile. CI runs the tests with --profile ci: light optimisation, line tables, no LTO, and debug assertions off. It builds several times faster than --release. If a test fails only in a plain debug build, say so in the PR. cargo xtask test builds the tests with it and then runs every test binary at once (cargo test runs them one after another); arguments after -- go to each binary, e.g. cargo xtask test -- shell. Builds and tests use all your cores by default; nothing in the repo limits them. Only if you want fewer (say, to keep a shared machine usable), set SOLIDCRAFT_TEST_JOBS to a positive integer: it bounds how many top-level test/side commands cargo xtask test and cargo xtask ci run at once, and the sequential wasm/doc worker consumes one of those slots. Every binary and side check still runs after failures. Without the variable the existing concurrency is unchanged. With it set, each test binary inherits RUST_TEST_THREADS and ACTION_MATRIX_JOBS of max(1, cpus / jobs) — the machine’s parallelism shared between the binaries in flight — unless you set either one explicitly; explicit caller overrides always win. Cargo’s build-job setting remains an independent caller override; this limit does not change test filters, timeouts or baselines. Tests run beside each other on a busy machine, so a test must not depend on wall-clock timing.

Ready pull requests test what they touch. CI maps changed files to workspace packages and runs Clippy, the test build and tests for those and their dependents. The package selector and CI workflow define which changes require native checks; documentation, packaging, other-workflow and bot-script-only changes run the repository checks only, on pull requests and on main. Workspace-wide changes, merge-queue groups, nightly runs and Run workflow select the full workspace. A push to main reuses a successful merge-queue run for the same commit, and runs only the repository checks when no code changed since the last push to main that passed; otherwise it runs the full suite. Doc tests, reference tests and the Windows jobs run on main and nightly only.

CI shards the tests. One job builds them into a cargo-nextest archive; two shards run on pull requests and four during full validation. Every test runs in its own process: don’t rely on state left by another test. Pull requests skip a few heavy tests (one to twelve minutes each), which run in full validation (the merge queue, nightly, manual runs and pushes to main not already tested by the queue). Check that validation after merging. They are listed in .config/nextest.toml and the testing page:

  • solidcraft-engine: tests::hostile_params_never_panic, everything in tests::robustness, pattern_suppress_tests::enclosure_arc_extinction_keeps_a_real_sharp_inner_lip_after_resize_and_reopen, pattern_suppress_tests::enclosure_datums_follow_wall_height_and_board_depth_with_material_proofs, cmd::plastic::tests::enclosure_snap_fit_follows_resized_front_wall, the atomic_commands contract tests
  • solidcraft-kernel: tests::boolean_identity_on_tangent_primitives, tspline::tests::hostile_tsplines_never_panic, and the boolean_props property tests (these run only in the nightly “Property tests” job: their inputs are random, so main skips them too)
  • solidcraft-ui-egui scenarios: acceptance_sensor_enclosure

The action matrix tries every ordinary action on every face, edge, vertex and body of a corpus of designs against a checked-in ratchet (docs/action-matrix.md). A pull request that makes an action pass should record it with cargo xtask action-matrix --bless; one that makes a passing action fail fails the sample every CI run checks.

The reference tests live in tests/oracle/, each a recipe.json in our own format with an expected.json beside it. SolidCraft rebuilds the recipe and compares body count, volume, area, centre of mass and bounding box (and, where there is a reference.stl, the surface itself). Expected values are either worked out in closed form (tools/oracle-analytic/) or computed by building the same recipe in FreeCAD, run as a separate program (tools/freecad-oracle/); each file says which. The generated reference test report has the current case count and results. The reference-test CI job runs cargo xtask oracle during full validation, and on pull requests that reach solidcraft-cli. A case that fails because of a known bug names its issue in expected.json ("known_failure": "#123"); never change an expected value to match SolidCraft’s output.

Quarantining a flaky test

A test that fails at random can fail a merge-queue batch, and every batch behind it starts over. Open an issue for it, then add one line to tests/QUARANTINED_TESTS: <test> #<issue> <YYYY-MM-DD> [why], with the test named as in tests/KNOWN_FAILING_TESTS. A quarantined test still runs. If it fails, the job summary says quarantined, failed: <test>; see #<issue> and the job stays green; if it passes, the summary says that too, so you can tell when it is safe to release. A quarantined line may be at most 14 days old: cargo xtask lists fails after that, so fix the test and delete the line. A test that fails every time belongs on KNOWN_FAILING_TESTS instead.

Changing the design file format

A design file says which format it is in ("format": "solidcraft/N"). If your change saves something new in a design (a field, a feature type, a new kind of name), or makes older designs mean something else, it needs a format change. Add one file, and edit nothing shared:

  1. Create crates/doc/src/format/pending/<what-it-is>.rs (lowercase, dashes). Start it with a //! note: what is saved, what older designs get without it, and that the last release asks for an update instead of meeting it.
  2. If older designs must be rewritten as they open, put pub fn step(v: &mut Value) -> crate::Result<()> in the same file. Pending steps run in file-name order, after the older formats’ steps.
  3. Don’t touch FORMAT, JSON_STEPS, the history in crates/doc/src/format.rs, another change’s file, or any v<N>/ folder: those are fixed once released.

That’s all. The build counts the files: while any are pending it writes the next format number, which no release reads, so a release warns about a design that has your change and keeps what it can’t read of it. A file also lists the pending changes it has (format_pending), so a later development build runs only the steps that file is missing. Two pull requests that each add a change merge without a conflict. crates/doc/src/format/pending/README.md has an example.

At release time the maintainer runs scripts/fold-format-pending.sh, which moves the pending files into crates/doc/src/format/v<N>/ and fixes that number for good (see docs/releasing.md). Never add a file to a v<N>/ folder afterwards. Drawings follow the same rules with their own numbers, from 1, in crates/doc/src/drawing/format/ (see its pending/README.md), and the same script folds them.

Lists many changes add to

Some files gain an item in many pull requests at once. They are kept so that two changes that each add an item only meet when their items are neighbours, which spares everyone a conflict and a fresh CI run:

  • Growing Rust lists: use slices (const ITEMS: &[Item] = &[...];) without a written length. cargo xtask lists rejects non-test const/static arrays with more than ten lines of entries, except fixed standard or algorithm tables listed with reasons in xtask/src/lists.rs. Built-in themes need only a JSON file in crates/look/themes/, with a unique nonnegative integer order (the picker order) and its own provenance line; keep existing order values.
  • Message catalogs (assets/locales/*.json): one message per line, sorted by id. Add your message anywhere and run cargo xtask lists --fix; cargo xtask lists (in cargo xtask ci and the Lists workflow) fails on an unsorted catalog or an id that appears twice.
  • mod lines: one per line, in the sorted group they belong to (cargo fmt keeps each group sorted). Add a new module into the existing group, not as a new group at the end.
  • Commands without schemas (crates/engine/src/schema/unconverted.txt): sorted, one id per line, and it only shrinks. Converting a command removes its line; a new command is never added to it.
  • Toolbar commands without help (BACKLOG in crates/ui-egui/src/command_help.rs): sorted, one id per line, and it only shrinks. Writing a command’s help removes its line; a new command is never added to it.
  • ui.prefs settings (docs/control-protocol.md): one line per setting, sorted by name, in the list under the protocol table; add a new setting on its own line rather than editing a shared row.
  • Languages (crates/ui-egui/src/i18n.rs): one per line, English first, then by id (a test checks it).
  • Generated files: THIRD-PARTY-LICENSES.txt and docs/features.md are rewritten on main after each merge by the Generated files workflow (cargo xtask licences, cargo xtask features). Don’t regenerate or edit them in a pull request.
  • Design format: one file per change, never an edit to a shared number (above).
  • New UI strings and attribution rows: one new file per change, assets/locales/pending/<slug>.json and attribution/pending/<slug>.md, instead of editing the catalogs or the ATTRIBUTION.md table; cargo xtask fold-pending folds them on main (see the README in each folder). A new English message also needs every catalog delivered complete (COMPLETE in crates/ui-egui/src/i18n.rs: today ar, he, ja, vi, and zh) in the same pending file; cargo xtask i18n --check reports any gap.

Sending a change

  1. Work is tracked in GitHub issues, labelled by area (area:kernel, area:sketch, area:doc, area:ui, area:infra). For anything bigger than a bug fix that has no issue yet, open one first. Claim the issue with a draft PR before you start, so two people don’t build the same thing:

    • Check nobody has it: gh pr list -R tangency-labs/solidcraft --state open --search "#N".
    • Branch from the latest main, commit git commit --allow-empty -m "claim: #N", push, and open a draft PR titled <summary> (#N), e.g. Fillet: variable radius along a chain (#123).
    • The lowest-numbered open PR for an issue wins. If yours lost, close it (or offer to help on the other one).
    • The bot records the claim when the PR is opened: it assigns you, adds in progress and says so on the issue. It reads #N in the PR title and Fixes #N in the body. If someone already holds the issue, or an older open PR references it, it says so on your PR instead.
    • Want to stake a claim before you have a branch? Comment /claim on the issue (below).
    • Push work in progress at least daily. A draft with no pushes for 72 hours may be taken over: comment on it first, then open your own claim.
    • Mark the PR ready for review when it is done.
  2. Branch from main and keep the PR to one topic.

  3. Run cargo xtask ci before pushing. It runs fmt, the asset, licence and layer checks, clippy (-D warnings), then the tests with the wasm build check alongside. A PR has to pass it. Heavy CI runs after review: a pull request gets the formatting and repository checks and a quick compile (cargo check) when you push. Once a maintainer has reviewed it they add the approved-for-ci label when the PR is ready to merge, which runs the full CI (build, tests, goldens, Clippy); every push after that runs the full set again. Contributors don’t queue or merge their own pull requests. A maintainer or steward adds ready to merge (and approved-for-ci for a pull request from a fork), and the merge shepherd then enqueues it once it is green and passes the review gates: CodeRabbit’s threads are resolved, and a change to CI or workflow files has the owner’s approval. The merge queue tests each group of queued PRs together before it lands on main. You don’t have to wait for the label to see the full CI: run it on your fork, on GitHub’s free runners. On the fork, set two repository variables (Settings, Secrets and variables, Actions, Variables): CI_RUNNER to ubuntu-24.04 and CI_RUNNER_WINDOWS to windows-latest. Then open Actions, CI, Run workflow, pick your branch and tick “Also check WebAssembly and RISC-V” (or gh workflow run ci.yml --ref <branch> -f cross_compile=true -R <you>/solidcraft). That runs the jobs the merge queue runs, plus the RISC-V build, more slowly than the project’s runners, so each job’s time limit is three times the usual one while CI_RUNNER is set.

  4. Reference the issue (Fixes #12). In the PR, say what you tested and how. For geometry work, include the measured numbers (and the expected ones).

  5. Write the PR title and first sentence for someone who hasn’t seen your chat, the issue or the code. Maintainers pick what to review from the title, and it becomes the commit on main. Say which part of the app changes and what a user now sees or can do, and name things rather than leaning on “there”, “it” or “this”.

    • Unclear: Send to Slicer: pick one print face per body there, and show print faces only while it is open
    • Clear: 3D printing: choose the face each body rests on in the Send to Slicer window, not the face menu

    Use plain, literal English in PR text: one idea per sentence, active voice, and one word for one thing. This helps everyone, including readers whose first language isn’t English.

Commenting /claim assigns you, adds the in progress label, and replies with its automatic cleanup deadline. It frees a claim with no linked PR after 72 hours, or a claim with a PR after 14 days without pushes. These automatic cleanup deadlines are separate from the daily progress and 72-hour takeover policy above. A draft PR that references the issue claims it the same way. /unclaim releases the bot claim early (only you or a maintainer can). Issues a maintainer has taken are never freed automatically.

A PR has to explain itself. Start the description with what it is, in plain words someone new to the project would understand: what it does and who it’s for. Don’t assume anyone knows the name of your tool, project or feature. If there’s anything to look at, show it in the description: a picture or video has to be visible there without opening the changed files. “The GIF is the change” doesn’t count. A PR that doesn’t do this waits until it does. For a PR that adds to docs/showcase.md or docs/screenshots/, the Picture check fails until the description shows one.

Help reviewers see what the words mean. For observable CAD, UI or model behavior, please include actual descriptive screenshots or a short recording in the issue or PR. This evidence is required for changes or bugs that can be demonstrated in the app, including kernel, geometry, assembly and backend work. Coding agents should run a small reproduction and capture the evidence themselves; please don’t ask a human for evidence the agent can produce. Show the actual failure or result, preferably before and after, with brief steps and expected/actual behavior. An arrow or annotation can help identify the detail that matters.

A pull request that adds to the UI (a row, an option, an icon, a button or an entry in an existing dialog, toolbar, ribbon or menu) needs screenshots of the whole flow at 1280×720, plus a check at a small window, so a maintainer can see that nothing is clipped or pushed out of view.

Flowcharts and Mermaid diagrams cannot replace evidence of visible behavior. Diagrams are useful for architectural reasoning when actual screenshots cannot illustrate the point. Changes with no useful observable behavior do not need screenshot or video evidence. Use a small example without private designs or sensitive details; attach the media or link to an accessible recording rather than committing generated review artifacts.

To show a picture without committing it, attach the media to the PR (it goes to the PR’s own attachment storage and renders inline in the comment). A gist is the better home for a log or another text file, and you link it; a gist does not render a picture inline. The review artifacts stay out of the diff that way, and nothing has to be pushed to a branch of its own.

Attaching works in the browser for anyone who can comment: drag the file into a comment on the pull request. A command-line agent without a browser session usually cannot attach a file (the upload needs write access to this repository). Say so in the pull request, and the person running the agent or a maintainer can attach it.

Keep an eye on your PR’s status, but don’t push to it only to catch up with main: every push restarts the whole CI run for everyone waiting behind it, and main‘s own run catches what lands in between. When you push a change anyway, or GitHub reports a conflict, bring your branch up to date with the latest main by rebasing or by merging main in; both are fine. Rebasing is preferred for your own branch (push it with --force-with-lease). Never force-push main or a branch someone else works on. When the project’s automation updates your pull request, it merges main into your branch and never force-pushes over your history. Resolve conflicts without losing others’ changes. Don’t re-run CI on an unchanged commit either: a real failure fails again. Re-run only a job that runner trouble cancelled, with “Re-run failed jobs”. While a task remains active, follow an open PR through its status changes; at handoff, clearly identify anything still awaiting merge.

Much of SolidCraft so far was written by AI coding agents (Claude) working in parallel, each owning one area, with a human setting direction and reporting bugs. Their working notes are in AGENTS.md. Human contributors are just as welcome. Use whatever tools you like, as long as the change meets the rules above.

If you run an agent, keep one long-running session rather than restarting it, don’t --resume one whose prompt cache has gone cold (it re-reads the whole history at full price), and act on CodeRabbit comments only when they’re about crashes, wrong results, data loss or security.

Two quick checks run on every pull request (.github/workflows/pr-checks.yml, not required): it fails when a lower-numbered open PR by someone else already closes the same issue (the label not-a-duplicate skips it), or when the description lacks the Changes a default: no / Changes a default: yes — <what>, old behaviour kept behind preference <name> line (docs-only and bot PRs are exempt). A default here is a UX default only: a control, setting, preference or tool behaviour a user sees by default; a changed kernel or geometry result is Changes a default: no.

Labels and triage

The triage bot labels every new issue and PR (.github/workflows/triage.yml). It only adds labels; correct them freely and it won’t put them back.

  • PRs get area:* from the paths they change (crates/kernel and crates/geom → area:kernel, crates/ui-egui and the apps → area:ui, .github/, scripts/, docs/ → area:infra, and so on).
  • Issues get bug (in-app reports and the bug template), enhancement (feature requests), priority:P1 for crashes, and an area:* from the title or the reported command. Until it has a priority, an issue carries needs-triage; setting a priority removes it.
  • Maintainers comment /triage area:kernel P1 (any existing label; P0–P3 for a priority, which replaces the old one; -label removes one) to sort an issue or PR.
  • Short bug reports without steps to reproduce get needs-info and one comment asking for the steps (and the version, when it’s missing). An app report that arrived empty is asked to paste the report the app copied to the clipboard. The reporter’s reply, or an edit that adds the steps, removes the label; an issue that stays in needs-info for 14 days is closed, and can be reopened any time. Feature requests, app reports with their details, and maintainers’ issues are never asked.
  • needs-triage is the queue: open issues with it.

Where help is wanted most

Kernel speed (slow fillets, booleans, STEP): report slow cases with a file under the kernel-perf label, and put kernel effort into the vendored kernel, solidcore (progress). See #1597.

See the help wanted and good first issue labels. In order of how much they’d help users:

  1. Fillet and blend robustness. Fillets are the vendored kernel’s code (solidcore). Constant, variable-radius and chord fillets work, and so do corners where fillets of different radii meet, including three rounds meeting at a vertex. Next are setback corners, fillets along curved edges between curved faces, and fillets that consume neighbouring faces.
  2. Boolean robustness. A random-design fuzzer currently gets about half of its designs through without a kernel error. Each failing seed is a reproducible bug.
  3. Drawings, CAM and mesh tools. These are out of scope for now (see ROADMAP.md), but anyone who wants to own one is welcome.

Updating the generated feature list

After adding or renaming commands, run cargo xtask features. The registry is the source of truth for docs/features.md; do not hand-edit the generated rows. The generator sorts commands by ID and omits global and per-section counters, so additions to unrelated areas no longer rewrite shared summary lines.

If two branches still conflict in this file (for example, adjacent additions in the same area), first resolve the command registry and source conflicts, then run cargo xtask features again and stage the regenerated file. Do not use Git’s union merge driver: it can silently retain stale or duplicate command rows. The generator tests cover stable ordering and additions that leave existing lines unchanged. The initial switch to sorted rows is a one-time larger diff.

Pictures in the user guide

The user guide’s screenshots and short clips (docs/book/src/media/) are captured from the real app by examples/demo/doc_shots.py, one function per figure. Each run starts a fresh app with its own settings, so your preferences never show. When a change makes a figure out of date, run python3 examples/demo/doc_shots.py <page or shot> (it needs Xvfb, ffmpeg, Pillow and a release build) and commit the new file. Stills come in both themes: --light makes <name>.light.png, and the page shows the one that matches the book’s theme (an <img class="dark-ui"> and an <img class="light-ui">). A new figure is a new function there, an ATTRIBUTION row in attribution/pending/, and a <video> (clips) or the image pair on the page. Keep a still under about 250 KB and a clip under about 1 MB.

Licence

By contributing you agree that your contribution is licensed as MIT OR Apache-2.0, like the rest of SolidCraft. A contribution to the brand and interface (crates/ui-kit, crates/look, crates/theme, assets/branding, assets/icons/app) is licensed under LICENSE-BRAND, which lets the maintainers also license it as MIT OR Apache-2.0. SolidCraft isn’t affiliated with any CAD vendor; product names are trademarks of their owners (see NOTICE).

Contributing command guides and resources

The command reference combines the live command registry with reviewed Markdown guides. A guide appears inside its command’s entry, before the collapsed automation syntax. You can improve one entry without rewriting the whole manual. The reference’s Practical guides list automatically links to every available guide; adding a guide does not require editing an index or Rust code.

Add or improve an entry

  1. Find the command in the reference. Its ID is in Automation details.
  2. Copy the guide template to docs/book/command-guides/<command-id>.md, or edit its existing file. For example, Extrude lives in solid.extrude.md.
  3. Explain what the tool does, what must exist or be selected first, the dialog’s choices in a table, a short example, and useful DOs and DON’Ts. Use plain language rather than copying the raw automation parameters.
  4. Add related commands and any checked community resources, then open a pull request. Documentation changes are reviewed like code.

Do not edit the generated src/reference/commands.md: rebuilding replaces it. The generator rejects guide filenames whose command is absent from the registry. Names, groups, shortcuts and automation parameters continue to come from that registry; handwritten explanations remain editable independently.

Use fourth-level headings (####) inside an entry so the command remains the parent section. Write image and page paths relative to the generated reference page, docs/book/src/reference/commands.md, not to the input guide file. For example, ../generated/sheet-metal.png and ../guide/sheet-metal.md.

Write a command link as {{#command sheet.hem}}. The generator inserts its current name and links directly to that command. Unknown or missing command IDs and unfinished directives fail the book build, helping contributors catch stale links. Normal Markdown links can point to guide pages or external resources.

Community resources

You can add a focused tip, a short demo, a model or an illustrative screenshot to the entry’s resource table. Explain what the reader learns from each link; include author and licence information. Links are reviewed, not automatically collected from the web. A resource need not cover the whole command.

Use original SolidCraft examples or openly licensed resources with permission to share. Do not use designs, screenshots, help text or artwork extracted from commercial CAD products, or GPL/LGPL/AGPL code. Prefer small public-safe examples and remove private designs, paths and account information from evidence.

For a new file asset, follow assets/AGENTS.md and add its attribution in a new attribution/pending/<change>.md file. A reproducible recipe in docs/book/scripts/ can be rendered by the book builder; generated images stay out of Git. Existing examples demonstrate both inline pictures and resource links.

Preview and review

Run cargo xtask book with mdBook installed, inspect the command entry and its links, and check the example in SolidCraft. Confirm the required selection, units, defaults and limitations against the implementation. Show reviewers an actual before/after screenshot of the rendered entry. Repository quality gates still apply before a commit.

Copyable template

One sentence explaining what this command does and when someone would use it.

#### Before you start

- Explain the required body, sketch, region, edge, face or point in ordinary words.
- State whether a selection is required and what cannot be used instead.

#### Options

| Choice in the dialog | Meaning | Example or reason to choose it |
| --- | --- | --- |
| Replace with a real option | Explain the effect without copying parameter syntax. | A useful value with units. |

#### Try it

1. Start from a small reproducible design.
2. Name the selection, command, settings and expected visible result.

#### Tips and common mistakes

- **Do** explain a useful habit and its benefit.
- **Don't** describe an unsupported selection as if it worked.

#### Examples and community resources

Add only rows for resources you have checked. Replace placeholders before
submitting; do not publish an empty resource table.

| Kind | Resource and what it teaches | Author and licence |
| --- | --- | --- |
| Tip | Link to a focused explanation. | Credit and permission to reuse. |
| Demo | Link to a short recording or reproducible recipe. | Credit and licence. |
| Model | Link to an original SolidCraft design or an openly licensed model. | Credit and licence. |
| Screenshot | Link to or embed an actual SolidCraft result, with descriptive alternative text. | Credit and licence. |

#### See also

Use `{{#command solid.extrude}}` with a real related command's ID and explain
why it is related. The generator supplies its current label and a direct link.

Architecture

SolidCraft is a Cargo workspace in layers. A crate only depends on crates in lower layers, and cargo xtask layers fails the build otherwise.

LayerCratesRole
L0geomvectors, planes, profiles, meshes and their measures
L1sketch, kernel, render, dynamicsconstraint solver and profiles; the B-rep kernel boundary (the vendored solidcore); product rendering (render jobs and their backends) on the shared viewport renderer; multibody dynamics (rigid bodies, joints, forces, time integration)
L2docparameters, expressions, the feature timeline and its incremental evaluation
L3iodesign files, STL/OBJ/STEP/3MF export, STEP and 3MF/STL/OBJ/glTF import features
L4enginethe session and the command registry (everything is a command)
L5gpu, ui-egui, mcpthe GPU path tracer and SolidCraft’s GPU environment over the shared viewport renderer; the swappable desktop front end; SolidCraft’s MCP tools (headless or bridged to the app) over the shared mcp-server
sharedcolor, ui-registry, ui-text, theme, ui-kit, look, mcp-server, control, control-egui, viewport, viewport-gpu, viewport-eguicrates other front ends use: they depend only on other shared crates, solidcore and external crates (UI toolkits included), any crate may use them, and cargo xtask shared builds and tests each one on its own. The viewport renderer is three: viewport (camera, pictures, CPU rasterizer, shading), viewport-gpu (its wgpu pipelines and plan evaluation) and viewport-egui (the viewport in an egui window). The UI kit (ui-kit) holds the tokenised controls and the components built from them (ribbon, app bar, history and navigation bars, view compass, preferences window, start page, window chrome, theme files), drawn from data the host installs or passes. SolidCraft’s look (look: its icon masters and colour roles, code-drawn glyphs, IBM Plex faces and built-in themes) is a shared crate too, so SolidCraft and other hosts install the one copy. A crate below L5 reaches no UI toolkit or wgpu through them, or any other way: cargo xtask layers follows workspace dependencies to check it
appssolidcraft, solidcraft-clidesktop app, headless CLI

The web app, apps/solidcraft-web, is the desktop front end compiled to wasm with trunk. xtask holds the repository’s own tooling: cargo xtask ci, layers, assets, features, oracle and book.

A few rules follow from the layering:

  • Only crates/kernel touches truck. Everything above it sees SolidCraft’s own types: bodies, faces, edges, meshes and measures. See The kernel boundary.
  • The document knows nothing about commands or the UI. crates/doc holds the parameters, the feature timeline and the component tree, and evaluates them into a model.
  • Everything the user can do is a command in crates/engine. See The command engine.
  • The front end is swappable. Nothing below ui-egui depends on egui or wgpu, directly or through a shared crate (cargo xtask layers checks it). The CLI and the MCP server run the same engine without a window. Headless pictures (solidcraft-cli snapshot, MCP screenshots, ui.render, thumbnails) are drawn on the GPU with the viewport’s own pipelines (solidcraft-viewport-gpu, capture): the engine describes the scene (view::scene) and the host installs the renderer (engine::capture). The tiers, in order: a hardware GPU, a software adapter (Mesa lavapipe or llvmpipe, WARP), and last solidcraft-viewport’s CPU rasterizer, which draws no surface analysis, highlights or legends. SOLIDCRAFT_GPU=auto walks all three, hardware and software stay on their tier (an error without one), cpu forces the rasterizer; every picture’s reply names its renderer (gpu, gpu-software, cpu) and a CPU picture carries a warning. solidcraft-cli gpu lists the adapters and the tier the policy lands on.

SolidCraft UI kit

The opt-in universal panel foundation separates engine layout, retained content controllers and viewport-local services. Its production content adapters are sibling work; existing content, Classic and shipping defaults remain available. WorkspacePanelHost uses the existing Container policy, theme tokens and stable semantic widgets. No second shell or camera path is added.

Shared headers span their complete column width when collapsed. They retain the active identity and declared custom actions, menu, chevron and optional quick close, in that LTR order and mirrored in RTL. hot_closable controls quick close; closable independently controls permission. One click on the same chevron restores the body. There is no rail, hover-peek or pin-open product presentation. Registered actions are bound to retained controllers and validated through the same engine command route as panel.action; unbound/disabled actions cannot run.

Tab groups place text-only tab headings in this same header row, replacing the single active title. One leading six-dot section grip drags the active tab; individual tabs keep their select, drag and reorder responses. Grip, tabs, compact overflow and actual controls use disjoint hitboxes, mirrored in RTL. Collapsed groups retain their headings; inline groups use the allocated title region without displacing editable inline content. Overflow reserves one compact control width, preserving the active label when it fits and keeping every entry reachable through the accessible menu. Single titles retain the heading font without placeholder icons. Icon metadata remains available to other consumers. Neither HostMetrics nor AllocationEnvironment has a second-row tab metric; measurement chrome counts the shared header only once.

After the existing six-point Move threshold, only the dragged instance’s title ink fades to 50% of its current opacity. Group grips have no title ink; the matching tab fades instead. Font selection, other labels, icons, grips, actions, fills and bodies stay unchanged. A paint-only badge shows the full live title next to the pointer on the existing preview tooltip layer, independently of landing validity. Theme typography, padding, radius and opaque backing keep it legible; the offset mirrors in RTL, and the badge clamps to the render-domain clip. The full title remains in its existing display order on one line, with no automatic wrapping, second bidi pass or ellipsis. Oversized text clips from a left-aligned galley in LTR or a right-aligned galley in RTL, preserving its reading start. Explicit metadata newlines retain their existing meaning. There is no added widget, container or input/occlusion/usable-space footprint. Pointer absence hides the badge while the held source remains dim; re-entry uses a fresh position. Release and cancellation clear feedback and repaint the source. Presses, clicks and either resize kind retain their normal appearance.

Expanded content receives bounded Header/Body/Footer slots and owns its scrolling. HostOptions::content_header_height can reserve a content toolbar. Collapsed content has no body and renders none of these slots. Expanded Wide hosts below theme::PANEL_INLINE_MAX_HEIGHT use one total row with the shared title/grip, leading registered actions, a dedicated inline content slot, then menu, chevron and optional quick close. The complete row mirrors in RTL. Only render_inline runs; its safe default delegates to Body and adapters may supply a compact view. There is no extra title band or duplicated footer. The inline specimen contains a working synthetic text input; it does not replace real History playback or timeline content.

Columns have independent widths and stacked tab sections. Sole side Content panels use bounded preferred metrics at their assigned width and leave uncovered space below; declared Fill panels use available height within the same limits. PanelController::measure captures immutable HostContentMetrics snapshots, including a revision that changes whenever any answer changes. The default reports the registration’s preferred outer height honestly. Real adapters supply bounded intrinsic/preferred metrics without rendering hidden bodies or enumerating unbounded lists. Candidate and committed allocation use the same captured metrics.

Tiny windows keep real panels at minimum dimensions in a bounded scroll extent. Scroll tracks own narrow input regions; leftover wheel input over actual panel paint scrolls presentation after content scrolling. The scroll canvas has no full-viewport response. Uncovered gaps stay navigable. Actual clipped paint, input/occlusion and usable contour remain separate from the full render domain below the application bar, including behind the ribbon. Panel changes never move the camera. Fit/Centre and independent navbar/compass placement include persistent legacy forms, palettes and confirmations after all producer passes.

Pointer and app commands use prepare_host_layout_with_snapshot and the same NO_SPACE admission. Pointer release stages the injected layout catalogue, then consumes the exact owned PreparedHostLayout after lifecycle and native preflight; it never reconstructs an action. Only the selected successful candidate’s landing, changed neighbours and tab marker paint. No idle targets or rejected-target grid is shown. Escape, invalid/outside release, focus/visibility/ modal loss, independent commands and changed environment/metrics cancel capture. Missing host/environment refuses structural app commands. A standalone registered HeaderAction follows the owning controller route without placement or layout-store staging. Availability, fallback selection and binding permission still validate. Projection and commit reuse one staged incoming cohort; metric/reservation changes invalidate pending widgets and cached proposals as well as drag and the old frame. The next widget admission requires fresh paint.

The app paints through show_host_deferred, then finalizes after later windows and modals using actual enabled top-layer responses. Geometry alone cannot permission input through a foreign window. Container::bring_to_front and owner-scoped popup layer registration preserve current layer ownership. Root and native windows mount complete separate cohorts. HostFrame::layout retains intended section geometry; its published controls and actual footprint arrays exclude invisible Area sizing passes. reply, Committed and PreparedCommitted events describe committed state, while next_layout describes a pending repaint. Transient menus shield input without becoming Fit targets.

AppPanelRuntime retains the installed native adapter and read-only Session provider. Inspection exposes an immutable runtime; callers cannot replace an adapter while native placements exist. The default adapter truthfully supplies no native Window service. Host cards reuse adaptive Container styling, title fonts, the six-dot grip and header separator. Shared shell gaps are allocation space. Captured frame alpha is reported separately from declarative surface intent; the opt-in surface renderer reports contrast outcomes and raises opacity to retain readable primary and muted text. An invalid authored pair uses an opaque accessible fallback and reports its reason rather than claiming the original pair complies. The shared host publishes expanded disclosure, selected tab/tab-list, toggled action, menu-item and oriented splitter semantics. Keyboard panel commands use the registered keymap and typed host admission; focused text editors retain their automatic key handling. Menus support keyboard navigation and Escape focus return. Focus traversal reveals clipped headers without changing camera geometry. Owner-scoped focus restoration preserves eligible editor state without rendering hidden bodies or taking focus from foreign UI. Native logical direction follows the effective locale. Shared paint transitions use theme timing and cubic-out easing; reduced motion commits immediately. Native windows, saved workspace rollout and real content migrations remain peer work.

The authenticated ui.panel_fixture seam is off by default and rejects replacing an existing runtime. Presets are docks, tabs, collapsed, floating, overflow (24 instances), pinned, columns, stack_tabs, content_fill, inline and surfaces. Only surfaces opts out of the compatibility opaque host policy. Its optional translucency_percent request (finite 0–100) converts to opacity as 1 - percent / 100; readability protection may increase effective opacity. This fixture override changes no saved preference. Inspection reports each section’s declared surface, actual paint_alpha and optional contrast outcome, including fallback reasons. See the surface specimen contract. An enabled specimen accepts translucency_percent alone to update presentation in place, retaining its controllers, drafts, layout and model. A missing/disabled specimen host or a runtime owned by another provider refuses this update. An enabled specimen also accepts the ephemeral reduced_motion boolean, which changes animation without replacing content or writing preferences. Drafts, controller identities and lifecycle/service/action counters are inspectable. The native specimen exposes editor Apply/Finish/Cancel actions. Separate action-binding fixtures retain their declared test actions. Strings use pending ui.panel_fixture.* / ui.panel_host.* catalog entries. Authored source cases and scenarios do not establish visual or automated verification; root owns builds, screenshots, visual acceptance and checks. viewport_status_item and viewport_status_row draw persistent viewport pills on Surface::ViewportStatus. Callers supply translated names, formatted settings, kind, stable identity and key availability, and dispatch returned clear/edit actions. The row keeps supplied order and anchors to the top centre of the free viewport. Its presentation state remembers only open cards, focus, scroll and the selected diagnostic record, scoped to the current document. The ui.status_fixture control method provides named specimens for UI scenarios and live visual review.

details renders a Details description in the shared host: optional caption, live settings, key, findings or readout, then at most two text actions. Tools supply data and handle DetailEvent requests through their normal commands. The caption and actions stay fixed around a body bounded to 60% of the free viewport height. Findings/readouts preview six rows; Show all expands the same scrolling body. patterned_swatch supports an optional second semantic colour, so a grouped key can honestly represent both existing scene colours.

viewport_status_details defaults to live cards. Its false value restores read-only keys and the Section dialog independently of the analysis/Section HUD and Summary preferences. Continuity edits explicitly select one retained body/edge record; its own successful limits and sample count accompany every live command. Stale geometry disables editing and targets. view.status_details reads current effect settings/capabilities and the retained per-seam diagnostics without preparing or evaluating geometry. details_findings and details_readout fixture presets are labelled component specimens, not production results.

ComponentUse
method_sectionSection with a GPU/CPU tag; unsupported contents stay faded with a readable reason.
read_only_textBounded, selectable code box for read-only diagnostic JSON.

crates/ui-kit/src/ (the shared crate solidcraft-ui-kit, which ui-egui uses as ui_kit) contains the tokenised controls used by feature dialogs, preferences, manager cards, prompts, context menus and the sketch palette. Import through crate::ui_kit::{...}. Most controls are crate-visible; the container API is public so platform apps use the same hosts.

Stage bars

StageBar composes two clipped, horizontally scrollable rows inside the shell’s shared floating Container (Surface::Chrome). Its 36 pt first row contains numbered stage chips with the current workspace accent, shared primary/normal buttons and the existing command-search editor. The second row fills the remaining card height. The feature supplies labels, enabled reasons and command dispatch; the kit owns chip geometry, row spacing, typography and colour. Stage selection is navigation; the kit never invents completion badges. The shared compact workspace card is 104 × 88 pt; its icon, label and caption are centred vertically. The stage bar is 88 pt high with an 8 pt gap from that card. Its 36 pt header and 52 pt tools band share a 1 pt divider. Tools use 8 pt side padding, icon-text segments, icon-label actions and an information chip, all with theme::Tokens colours in both themes. Render retains its Inspector controls outside the bar.

Panel::workspace gives paired floating docks the same bounded vertical span and limits each dock to half the available width with an 8 pt gap. Content clips inside the allocated host, so a small window cannot grow a dock into the stage bar or Shots strip. Docks without usable space are omitted. Render’s default outer widths are 266 pt for Browser and 305 pt for Inspector, retaining the shared minimum/maximum resize rules. Its compass is 110 pt; the 48 pt Shots strip clears the docks by 8 pt.

Composition and rules

ComponentContract
ReviewAndApplyShared resource card, wrapping Replaces/Kept rows, variant content, acknowledgement, Cancel and a gated apply action. Physical plans offer Copy plan. Composes Confirm. Gallery: review_and_apply.
SphereTile::badgeOptional availability text on a three-column library tile, alongside the user-count badge. Uses theme tokens.
catalog_footer, catalog_footer_with_tip, catalog_footer_heightOne-line information note with ellipsis and full guidance on hover; optional separate tooltip text.
CatalogLayoutFills the remaining panel height with a scroll body above a measured bottom footer. Optional minimum search-row count keeps lists usable while a long detail card scrolls independently.
  • Every outer host uses ui_kit::Container with a semantic Surface. The shell publishes the layout once per frame; Container::floating() and frame() own adaptive placement policy and chrome. Panel, Window, Confirm and Menu compose it. Raw egui hosts and outer shadows are rejected recursively, including platform apps. The mandatory cargo xtask ui-containers gate runs in fast CI and scans new crates/apps automatically, including macro bodies and alias chains. See the container audit and migration inventory.

  • Colours, typography, dimensions and spacing come from theme::Tokens.

  • Container::selection_card extends the navigation host with a bounded, rounded selection pill. selection_actions composes its name chip and quiet icon actions; callers supply command data and dispatch the chosen action. Container::tooltip shares the library samples’ tooltip frame and padding.

  • Sliders only via ui_kit::slider; Slider options change behaviour, never appearance. Raw sliders and duplicate track/thumb painters are forbidden in the UI and apps.

  • scope(ui, add) supplies form styling. row(ui, label, add) aligns labels and controls at control_height (28 pt). Wide manager rows use a 104 pt label basis with a 10 pt gutter, derived from the compact 70 pt basis and spacing tokens. Compact rows retain their 8 pt gutter. Labels widen from the appropriate basis to at most 1.6 times that width and never exceed half the row.

  • expr_row, hint_row, text_row and choice_row compose row with the existing expression, hinted expression, plain text and dropdown controls. They preserve row order, labels, focus IDs and completion; callers keep Enter/change ownership and explicit dropdown IDs. count_row composes row with count_field for pattern quantities. action_row composes row (empty label column) with button_at across the control column, for a one-shot action in a card (its text from the catalog); it returns clicked and publishes dialog:action:{key}. checked_row is hint_row for a value the form checks before it runs: with a warning, the field is outlined in the warning colour and the message sits under it (warning_caption), so colour is never the only cue. The label of an expr_row or hint_row scrubs: dragging it sideways changes a literal value (20 mm, 45°) by about a hundredth of its size per point, on that step’s grid, Shift ten times finer and Ctrl/Cmd ten times coarser. It keeps the unit and decimal mark as typed, never makes a size negative, and leaves expressions and parameters alone.

  • Panel::workspace owns grip-based left/right placement and bounded width resizing for floating workspace cards, including in Compact ribbon layouts. It reuses the 40 pt Panel/Window header and padded body, with no second top inset below the ribbon. Persistent workspace cards omit the close action. Panel::workspace_with_header lets tree cards compose the shared tree_header (title, compact lens and quiet search action). Its tree policy caps the card at ten 28 pt rows and lets rows own their 8 pt side insets; header navigation retains a separate title grip. Optional header actions share the same title band. stack_first_bounds reserves a bounded Browser above its companion; stack_next_bounds places that companion with the shared gap. Panel::bottom_strip uses Container::bottom_strip_rect, the same placement as Design History. Container::navigation_card supplies the shared Navigation surface for anchored HUDs. Register measured card rectangles and reserved edges with shell::register_panel so fitting and pointer shielding agree. Feature modules provide content, never outer chrome.

  • Panel owns bounded windows, feature/palette composition, scrolling bodies, pinned actions, compute status and keyboard routing. wide() uses three card widths. feature_frame and palette_frame retain docked/floating surfaces. FeatureOptions::apply_label adds Apply; Panel::actions supplies custom labels. With DialogButtons::Top published (Preferences › Interface › Dialog buttons), feature puts the actions in the header row through HeaderActions instead of a footer: the same buttons, enabled states, keyboard and handles, each as wide as its bold label, with Cancel standing in for the close button and the compute status in the title’s place. palette (the Sketch panel) follows the same preference: its footer row moves under the header, above the options. body_rows caps a manager’s scrolling body independently of its position; short viewports still keep the header and actions visible.

  • Window composes the shared Panel frame with a 40 pt icon/title/close header, padded content and optional 48 pt footer. show_with_left pins a left action beside the right-aligned buttons. show_pages accepts declarative WindowPage entries (icon, i18n label key, optional group heading, stable handle, scroll policy and draw callback). Its 188 pt soft page list (150 pt below a 700 pt window width) has 32 pt icon/label rows, selected tint/ink and a divider. Page windows use the navigation board’s border/radius and the shared menu shadow; other windows retain their shared card surface. Preferences has nine grouped pages; legacy control-channel page names remain aliases. page_rows opts into a taller body while preserving viewport bounds. actions, anchor/placed, cards, fields and keep_open_on_escape retain existing window behavior. Empty action labels omit the footer.

  • Confirm is an input-blocking modal using the same card frame and footer controls. It shows a 36 pt icon disc in warning, danger or info tone, a title, caller-supplied message and optional monospace detail, above a 52 pt footer. Glyphs use the construction ramp for warning, the shared shaded danger treatment for danger, and base blue for info; the disc keeps its tint. actions(primary, cancel, left_ghost) places the optional third action at the left. Danger uses form_error with a contrast-selected on-colour; other primaries use blue. Enter invokes the selected answer (primary by default) when valid; Esc cancels. It retains close, answer and message scenario handles. The old Prompt element is removed.

  • Callers supply data and enabled state, then dispatch existing commands. Editors leave their established completion, Enter, Tab and commit ownership unchanged.

  • Every text input uses the kit’s shared text_input policy: symmetric horizontal form_inset, zero vertical margin and centred galley alignment. Font metrics therefore centre both mono and proportional text within the allocated field, including invalid and attached Library fields, without per-caller offsets. Canvas placement editors retain their native line-height plus space_4 hit target so nearby sketch picks are not intercepted; alignment stays shared.

  • Every kit text field has one right-click menu, text_menu::menu (#8875): Cut, Copy, Paste and Select All, disabled when there is nothing selected, the text is read-only or a password, or the clipboard holds no text (always in the web app, where Ctrl+V pastes). A kit helper calls it right after adding its TextEdit; the edit marks the response changed and gives the focus back, so owners that commit on lost focus keep the field. Filters inside menus and drop-downs have no menu of their own. Rows publish menu:text_cut, menu:text_copy, menu:text_paste and menu:text_select_all.

  • Scenario handles and accessible labels are API. Consolidated options preserve the former geometry, wrapping, native metadata and publication keys.

Preference compositions use settings_heading, shared disclosure settings_section headings and settings_pair for responsive controls beside one specimen. option_card supplies the shared illustrated radio control. settings_form and settings_block_hint define aligned label grids; theme_rail keeps the import tile fixed beside a scrollbar-free scrolling row. PreferenceFigure owns theme swatches, orbit pivots, quality, anti-aliasing, contact shading and up-axis figures. layout_preview follows the four workspace choices without opening another viewport. Scale, palette, autosave, mouse and live-axis specimens share tokens and the gallery; live bars show actual calibrated input and introduce no repaint timer.

theme_options_list renders generic ThemeOptionRow definitions without standard-option branches. Its pure preferences_layout::layout packs measured narrow/composite/wide entries in order, with ten non-advanced top-level cells before More. A composite retains its child in one cell; advanced descendants move the entire cell under More. Packing and allocation share measured localized label/readout widths, the form gutter and a compact slider minimum; short composites can share a row with narrow entries. A composite takes a full row when needed and stacks its controls only if they exceed that entire row’s width. theme_options_badge paints inside an existing theme card and is called only for offered options. The small registry in theme_options.rs supplies standard label/help catalog ids, availability and behaviour; only opacity is registered today. Missing providers are absent; unavailable registered providers keep their controls and show their translated reason. Callers submit list edits and Reset through the existing atomic preference merge/control path. The scoped i18n::resolved_labels adapter keeps theme language-map text and fallback labels verbatim through shared controls, while stable catalog messages continue to translate.

Review fixtures are in crates/ui-egui/tests/fixtures/theme-options.json, keyed by light-0/1/3/6/12 and dark-0/1/3/6/12. Each value is a complete importable theme file: pass it as ui.prefs theme_import together with open: "Interface". The 12-entry files include a four-choice wide row, Shadow/Softness composite, six-choice dropdown, an advanced entry and overflow. More contains two top-level entries. Set theme_options: {"options-dark-12": {"opacity": 60}} (or the selected fixture’s id) to compare with 100 on a busy model. Reset uses prefs:theme_options_reset; More uses prefs:theme_options_more; controls and cells publish prefs:theme_option:<id> and prefs:theme_option_cell:<id>. Unavailable-provider specimens are internal to the existing prefs::tests::theme_options_provider_registry_filters_unknown_and_retains_unavailable test; they are never installed in the live registry or exposed as preferences. The manager captures and reviews live-app screenshot evidence separately from the headless harness tests.

Elements

aspect_grid composes three-column AspectTile choices with token-sized aspect glyphs, selection tint and stable targets. frame_guides paints a bounded viewport mask, outline, thirds, a 90% safe area and a centre mark from FrameGuides. These are viewport decorations; callers exclude them from exported images and result canvases.

Module names below describe implementation locations, not caller import paths. Every inline element/variant has one entry in the light/dark gallery::ELEMENTS table. Panel/Window/Confirm compositions also have dedicated geometry and interaction tests.

Module / elementUse and optionsMain tokens
compare: comparison_divider, comparison_divider_chrome, comparison_badgeClamped vertical image wipe handle and bounded corner labels. The caller owns commands, shared framing and navigation suppression while the handle captures input; the paint-only chrome can be raised above source callbacks.control_height/radius/border, space_2/6/8/16/24, font_label, form_ink, panel, active TabAccent
assistant: assistant_header, assistant_controls, assistant_badge, assistant_bubble, assistant_prose, assistant_tool_frame/row/detail/status, AssistantToolState, assistant_approval/actions, assistant_composer/footer, assistant_input, assistant_send, assistant_empty, assistant_banner/banner, AssistantToneShared Assistant presentation: 40 pt header, 22 pt state pill, right-aligned user messages limited to 86% width, unboxed assistant prose, bordered tool groups and command detail, tinted approval heading, rounded focused composer with one unframed growing editor and pinned footer, centred setup copy and semantic notices. Closures preserve existing permission choices, protocol routing, queue actions and scenario handles.form_header_height/close_size, font_label/small/body, dialog_body/footer/rule, control_fill/border/radius, form_ink/dim/primary/success/error, accent_soft/selected_ink, warning, radii and spacing
history: StripHeading, HistoryChip, HistoryStyle, history_drop_bar, history_markerShared strip headings use semibold font_heading, text, a centred baseline and space_12 trailing gap. One painter family for bottom chips, 38 pt vertical icon chips and single-line 26 pt name rows (the browser’s shared TREE_ROW_HEIGHT). Selected items use the active tab tint/ink/border; all variants retain multi-tone feature artwork using one shared kind-to-accent rule (Sketch green, Surface orange, Mesh yellow, Form purple, solids base blue); rolled-back items have 42% opacity; a reference row (a sketch listed under another feature, #6489) has its name in secondary text. Error/warning badges, suppression strikes, horizontal/vertical drop bars and rollback line/hollow circle are shared. No connector lines.control_height, space_2/3/4/6/8/10/12/14/16/24, radii, typography, TabAccent, timeline_item, control_border, text/dim, form_error/fill
tree: tree_row, Row, TreeActionShared Browser rows with disclosure, visibility (an eye column at the row’s end, clear of a floating scroll bar, with a gap between the disclosure and the activation radio; or beside the disclosure with eye_left), activation, selection, appearance swatches, optional subtitle and trailing actions. Compact scene rows are 28 pt, with inline right-aligned summaries, circular swatches, trailing eyes and full-width selection tint. Actions use quiet icons and can appear on hover. Disabled actions retain their reasons and cannot select or fold the row. Browser automation records the same row geometry. The activation radio publishes radio:<handle>.TREE_ROW_HEIGHT, font_label/small, spacing, control radius/border, text/dim, workspace selection tint
browser: Browser, BrowserState, Node, Rows, Event, FoldMemory, order_before, cancel_dragThe Browser panel drawn from the host’s rows, emitted each frame (open branches only; next_visible/skip let a host skip off-screen rows). It draws with tree_row and returns events (Select with Only/Toggle/Range/RangeAdd, Click for rows that are no item, Open, Fold, Eye and the eye drag’s EyeGesture, Activate, Rename, Move with Before/Into, Action, NameAction, Menu, Revealed); it never changes host state. Click selects only that row, Cmd/Ctrl toggles, Shift takes the range of the row’s kind from the anchor. A slower second click, F2 or request_rename renames a row its way: Rename::Inline in the panel’s editor (Enter or leaving keeps the name, Escape cancels, refuse_rename reopens it), Rename::Host by Event::RenameRequested to the host’s own editor (RowOut gives the row’s widget id and name area). A selected row drags the selection of its kind, or the set and typed Payload the host gives with Rows::drag_with on RowOut::drag_started (items no row draws included); a drag carries its Source (scope, presentation) and lands where the host’s rule over source and target accepts it (without rules, within its scope); a row’s own rule (Node::accept) replaces the panel’s for it; a row taking drops before and inside takes Before in its top 30 %, and inside it when the rules refuse before it there (a drop over its middle never lands before it); other payloads pass through. events_per_row hands each row’s events back in its RowOut as it is drawn, with its click, secondary click and the drop zone under a drag; scroll(false) lays the rows out in the host’s layout; active(false) draws and emits nothing, ending an eye drag it holds (EyeGesture::End) and cancelling a row drag it began. end_gestures does the same from the context for a presentation not drawn at all. RowOut also has the row’s widget response and on_control; Rows::pass keeps a row the host lays out itself in the order, so ranges across it are whole. Rows’ menus are the host’s (Event::Menu) or kit Items. BrowserState keeps the anchor, rename timer, editor, drag, reveal, search text and keyboard cursor per presentation; folds stay the host’s (FoldMemory is the shared store). Optional header (title, count, fold chevron, search); search and keyboard arrows are off by default. Publishes browser:search, browser:header, browser:menu:<item>, rename:<handle> and the rows’ handles.tree_row tokens, form_header_height, font_heading/body, field, accent, radius_sm, space_4/6/12/24
search: CommandSearch, MenuItem::search_rowRibbon search composition: one persistent ribbon editor expands from 28 to 40 pt and to 520 pt wide with its right edge anchored, magnifier and S/Esc keycap, 38 pt two-line menu row with 26 pt icon tile, accent bold substring, path, shortcut/kind keycap, counts, JSON detail box and footer. The ordinary 30 pt menu row cannot fit a second line; this variant keeps its enabled/selected states and scenario IDs. Ranking and dispatch stay with the caller.form_card_width × 2 − space_8 (520 pt), control_height, space_2/3/4/6/8/10/12/14/16/24, radius_xl/control_radius, control_fill, dialog_body/footer/rule/border, form_ink/dim/primary/error/error_fill/success/success_fill, selected_ink, accent_soft, typography
icons: IconSet, IconData, IconExtension, install, paint_icon, paint_on, paint_ink (_flipped, _mirrored), paint_app_bar, paint_accented, paint_danger, resolve, primary, renderThe kit ships no artwork. The host compiles its SVG masters, colour roles, accents and aliases into a static IconSet and installs it once, before the first frame and in its test contexts (SolidCraft: solidcraft_look::icons::install). A host that draws another host’s icons plus a few masters of its own installs IconSet::extended(data, extension): the other host’s IconData (the same static tables, not a copy) with an IconExtension of its masters, aliases and default accents, drawn in the data’s colour roles and accents and taking precedence for a name both have. The painters rasterise a master per (master, look, pixel size) in the set’s roles for the theme, accent and colour vision, with theme-file overrides (set_theme_palette); an unknown name draws the set’s placeholder master, and nothing is drawn before a set is installed. The code-drawn glyphs (glyphs: transport, caption, badge, pin, cross, breadcrumb, compass axis tip and radius, agent pointer, brand mark) are host painters too, in a Glyphs the host installs beside the set (SolidCraft: solidcraft_look::glyphs::GLYPHS).app_bar_text, colour vision, the set’s palette
menu: Menu, MenuItemShared popup, 30 pt rows, 28 pt icon column, clipped labels, right-aligned mono shortcuts, submenu chevrons, checked/unchecked and document swatches, inert 40% disabled rows and danger ink; headings and separators; ribbon drag/pin targets retaineddialog_body/border/rule, radius_xl, control_height/radius, form_ink/dim/primary/error, accent_soft, typography and spacing
radial: radial_menuEight rounded annular sectors, parallel 4 pt gaps, 9 pt circular corner joins, tab-accent artwork and hover, centre label/key; existing hold/flick/release dispatchdialog_body/border, form_ink/dim, TabAccent, icon palette, font/spacing tokens
panel: Window, WindowPageShared card frame, header, optional page list, bounded content and footer with a left actionform_header/footer_height, form_card_width, form_label_width, control_height, dialog_footer/rule, selected_bg/ink, spacing
confirm: Confirm, ToneModal warning/danger/info, title/message, mono detail and ghost/cancel/primary actionswarning, form_error/fill, accent_soft, form_primary, on_accent, tab_on_fill_dark, font_subtitle, spacing
status: completion_textCompact strip readout with an optional green completion tick and caller-supplied result identityfont_small, control_height, form_ink/success/dim, space_4/14
metrics: row_height, table_row_height, header_heightShared control/header heightscontrol_height, space_8, form_header_height
layout: scope, row, value_rowForm styling and aligned label/control columns; value_row shows read-only text that wraps in the control column and grows its row to hold every line (one line keeps the 28 pt row)form_label_width, form_gap_y, control_*, font_label, dialog_label, space_4/8
caption, warning_captionDim caption aligned beneath a control; the warning one says what is wrong with its valuefont_small, text_dim, warning, label-column tokens
status_line, StatusToneFull-width, wrapping status or refusal text in dim, warning or error ink. Translates source labels; already translated messages also work. Place directly in the form, outside row, so its height grows with the whole message. Rect: dialog:status:TEXT.font_label, text_dim, warning, error
figure_rows, FigureSlotA caller-painted figure in the label column beside a stack of captioned expression fields (the Hole dialog’s section); the caller gets each field’s rect and hover/focus state to draw leaders and light the part it sizes. Fields keep their dialog:<label> handles; the block publishes dialog:figure:<id> and its picture dialog:figure_picture:<id>control_height, form_label_width, font_small, form_dim, space_2/4, label gap
linear_color_fieldLinear RGB swatch with precise channel fields; clips only the preview to display gamut and preserves HDR values until editedcontrol_height, space_4, shared colour popover
section, section_count, section_method, search_fold, dividerPersisted small-caps disclosure with chevron, optional count or method tag and trailing hairline; search_fold is that row for shortcut matches during Preferences search, closed until opened; full-width rulecontrol_height, dialog_rule, font_caption, tracking, spacing
icon_actionIcon-only action with an accessible name, tooltip, enabled state and danger variantShared icon-label action geometry and ink tokens
list_rowPicked item with a remove targetcontrol_height, font_label, icon/spacing tokens
text, TextHint, note, report, inline or cell text; see variants belowfont_small/label, text/dim/error, control_height, space_4
text_linesVirtualized read-only licence/report lines, horizontal overflowfont_small, text, control_height
fields: field, hint_field, focused_field, checked_fieldExpression editors, optional empty hint or retained focus, or outlined in the warning colour when flagged; returns accepted Entercontrol_height/fill, form_inset, font_label, warning
count_field, CountExpression editor for a whole-number count (a pattern’s quantity) with up/down step arrows at its right edge. Count gives the limits and the evaluator (the design’s parameters); an arrow steps the evaluated count by one within them, replacing a plain number or adjusting an expression’s trailing + n, and is dimmed when it can’t step. Handles dialog:<row> up / down; returns accepted Enterfield tokens, control_border, control_radius, selected_bg, form_ink/dim, space_4
text_field, focused_text_fieldPlain name editor, optionally retaining focusfield tokens
multiline_text_fieldThree-line plain-text row with stable focus IDfield/row tokens
password_fieldStable secret editor; excludes the secret from scenario/accessibility metadatafield tokens
message_fieldGrowing message editor; owner retains scroll and Enter routingfield tokens
cell_editor, bounded_cell_editor, search_field, library_cellStable-ID inline/search editors; bounded variant caps Unicode characters; library_cell attaches a Library dropdown inside the editor borderfield tokens, text/error
wide_cell_editorA table cell editor that, while focused and too narrow for its text, opens over the cells to its right and wraps, with the live value or error under it (#9358)field tokens, popup frame, text/error
menu_filterThe filter at the top of a long menu: search glyph, frameless editor and a rule under them, so it does not read as an entry; the caller filters and owns focusspace_8/16, font_label, form_dim/ink, dialog_rule
text_areaBounded multiline JSON/data editorfield tokens
dropdown, dropdown_menu, compact_dropdown_menuIndexed choices or caller-supplied menu controlscontrol_height, text_dim, form tokens
searchable_dropdown, SearchOptionFiltered choices, custom-value action and pending messagedropdown tokens
search_picker, SearchPicker, SearchChoice, PickedDropdown whose popup opens on a focused search box over rows filtered by value, label and tip, with group headings (two or more groups fold under headers with a count, the current value’s open), per-row tips, an optional “use what was typed” row and a dim note; Enter picks the exact or first match, Esc closes the popup only; only the rows in view are laid out, and the popup keeps the height it opened with, the list taking what the rest leaves. Handles {handles}, {handles}:search, {handles}:custom, {handles}:{value}; returns the pick and the control’s responsedropdown and field tokens, space_16/24, font_small, text_dim, form_card_width
selection_field, SelectionSelection chip with explicit active and filled state, in three looks: the active input (where the next pick goes) has a form_primary border and selected_bg tint; a filled inactive one a quiet neutral tint, a stronger neutral border and its count in form_ink; an empty inactive one the plain control with a dim promptselected_bg/ink, form_primary/ink/dim, control_*, form_inset, space_2/8/24
value_pairPaired captions/expressions; optional independent field namesrow/field tokens, space_8
fraction_field, scalar_field, mixed_scalar_fieldLocale-aware bounded dimensionless values; scalar fields fill their control column; the mixed variant accepts a translated placeholderform control/font tokens
number_fieldInteger/decimal field with caller range, drag speed and suffixform control/font tokens
number_fieldsN numeric cells sharing a row’s remaining width equally, including gaps; returns each field response in ordercontrol_height, inherited item spacing, number-field tokens
length_field, angle_fieldLength in design units, angle in degreesform control/font tokens
slider + Slider optionsOne token-painted track/thumb over native slider/value interaction; optional step, caller formatting, whole-step manipulation and hidden value editorcontrol_height, space_24/8/4/2, form control/font tokens
placement_fieldStable focus ID; drawing tool owns Enter/Tab validation and completioncontrol_fill, font_body, text, control_height, spacing
color_field, color_field_with_opacity, mixed_color_field, color_popoverDocument RGB swatch and kit popover; global U8/F/Hex segments, active-format copy, gamma-byte or linear RGB fields or one Hex editor, painter-drawn HSV square/hue and current strip; outside click dismisses. The opacity-aware field edits the caller’s separate f64 opacity, never premultiplied RGB. Six Hex digits preserve exact opacity; eight set alpha to aa / 255. RGB-only callers reject non-FF alpha without applying RGB. Incomplete text remains editable without changing the last valid colour. The mixed-aware wrapper adds a non-editable label and reports accepting the first colour as an edit, so the caller can set all selected targets.control_*, form_*, border, on_accent, space_*
choice: segmented, SegmentsEqual text choices; states supplies mixed selection, enabled keeps unsupported choices faded and inert, disabled_reasons also disables choices and supplies hover explanations, compact clips instead of falling back to a dropdown, primary uses accent/on-accent for the selected segment, glyphs draws a symbol per segment in place of its label (the label stays the hover text and accessible name)control_*, selected_bg/ink, font_label, space_4/8
segmented_icons, IconSegmentEqual icon choices and mixed statessegment tokens, icon palette
checkbox, checkbox_indeterminate, checkbox_icon, checkbox_row, checkbox_grid, optionsOne box/tick painter for inline, mixed, full-row, grid and menu choices; checkbox_icon adds a command icon between box and label (Customize Toolbar)space_14/3/2, space_16, radius_sm, control_fill/border, form_primary/ink, hover, on_accent
text_emphasisBold/italic with translated accessible namesform control/font tokens
tool_choices, ToolChoice, toggle_iconWrapped command modes or independent icon togglessketch mode/icon tokens, accent
list_pickerSelectable row with caller identity and native checked statecontrol_height, form selection tokens
thumbnail_choice, ThumbnailChoice, add_choiceShared selection-chip variant with 52 × 30 preview, selected tab accent, modified dot, progress ring and dashed add action for bottom strips; Panel::strip_contents reserves right actions and scrolls choicescontrol_height, spacing, radii, TabAccent, control/semantic tokens
popup_list, PopupOptionFocus-neutral suggestions; owner retains keyboard routingspace_2, form_label_width, control_height, form tokens
sphere: sphere_thumbnail, SphereTile, sphere_grid, sphere_preview, sphere_preview_with_sourceDeterministic lit material samples; matte/satin/gloss/metal/glass finishes. Catalog tiles centre a token-sized sphere above two full lines of text with an optional source badge, active-tab selection ring, user-count badge and shared Container tooltip. The grid owns three columns and token gaps. The Inspector preview composes a sphere, name, informational chips and replacement action in the shared list card; the source variant supports an interactive inheritance menu.control_height, radius_xl, space_2/3/4/6/8/16, font_small/badge, control_fill, dialog_rule, active-tab accent, on_accent
color_swatch, swatch_grid, color_thumbnail30 pt draggable samples, 6 pt grid step gap, selected outer ring and checker pattern under transparent colours; caller payloads retained; local-library thumbnails use 16 ptcontrol_height/radius, border, form_primary, control_fill, space_2/6/8
assistant_entry, AssistantEntryStateApplication-bar chat icon with quiet, ready, busy, auth, stopped and approval count marks; caller owns state and targetapp_bar, agent_ok/busy, warning/error, font_badge
actions: button, ButtonSecondary action: Normal, Small, or Enabled(bool)native sizing, form font/control tokens
primary_buttonAccent action outside a footeraccent, on_accent, form tokens
navigation: NavigationMetrics, navigation_surface, navigation_button, navigation_separatorShared live/editor navigation painter, button states, upward menu chevron and separator; approved board dimensions belowdialog_body/border, form_ink/dim/primary, control_border, selected_bg, nav_hover/press
ReorderPayload<T>, insertion_marker, drop_indexTyped drag origin, insertion chrome and horizontal gap selection shared by the ribbon and navigation editoraccent
navigation_button_editable, navigation_edit_outlineThe live navigation button with drag interaction, selection outline and dashed hidden-slot chromeNavigation control tokens, border/accent
navigation_menu: navigation_menu, NavigationMenuRow, navigation_context, navigation_tooltipUpward menus, checked rows, headings, separators and the delayed shortcut tooltip; context menus retain kit popup hosts and Container’s shared elevationdialog_body/border/rule, form_ink/dim/primary, selected_bg, nav_hover/tip/tip_ink/tip_key_border
navigation_settings: navigation_stage, navigation_tile, navigation_drag_ghostCustom preview stage, draggable slot tiles and drag feedback. Ordinary preferences reuse settings_section, settings_form, row, default segmented, checkbox, button, divider and text hintsdialog_border, control_border, form_ink/dim, nav_field/hover/viewport
settings_cardFull-width settings detail group; uses Container chrome and preserves its width with empty or changing contentdialog_footer, border, radius_md, space_12/10
settings_plugin_card, checkbox_atInstalled plugin card with 36 pt content, 12 pt padding, 36 pt tile, 13 pt semibold title, 11 pt version, 12 pt description and 2 pt text gap. The unframed 24 pt folder action and 16 pt checkbox are centred; actions wrap on narrow cards.control_fill, dialog_border, radius_lg, space_12/16/24
mirrored_icon_button_atDirectional counterpart of an existing icon master, sharing icon-button presentation and accessible labelShared icon-button tokens
icon_button_atIcon action inside a supplied rectangleform_icon_inset, text/dim, icon palette
button_pair, PairButtonEqual secondary actions, optional icons and selected stateform_button_height, control/selection/icon tokens
footer, Footer, Action, HeaderActionsOK/Cancel with optional Apply, compute status or selected action; HeaderActions puts the same buttons in a header rowform_footer/button_height, dialog_footer/rule, control_*, accent, spacing
keyboardEsc cancels, unowned Enter submits when validnone
action_menu, command_menu_buttonNested dense action menu or ribbon overflow action retaining its icon, shortcut and enabled stateform control/font tokens, space_16, icon palette
table: table_custom, Column, Table::headers, Table::row, TableRow::cell, Table::add_promptLeft-aligned headers/cells, horizontal overflow, selected/hover rows and empty state. table_custom supplies header actions through headers. Column::flex shares remaining width by weight, freezing columns at their control minimums before redistributing the remaining space; Column::icon_only hides the heading while retaining its automation identity. add_prompt is the final slim dashed line and requests a regular draft row from its owner; Column::new(label, width) uses label-column multiples, min_content_width(points) reserves measured control content; ContentWidth::name measures strong configuration nameslabel-column tokens, control_height, dialog_rule/footer, selected_bg, spacing
name_list, NameList::row, NameRow::name, NameRow::cellA list of named rows on the table grid (the web app’s stored files): the name column takes the width the other columns leave; the other columns are as wide as their widest cell, measured as drawn (a changed measurement discards the frame). A name is cut with an ellipsis, and given its full text on hover, only when it does not fit; dialog:name:NAME:cut reports itform_label_width, font_label, form_ink, space_8, table_row_height
removable_header, plus_header, bin_button, active_pillHover-only column removal, anchored Add Column menu with insertion brackets, icon-only immediate deletion and active-row status. Bins preserve dialog:button:Actions:Delete and :×; header removal preserves dialog:button:LABEL:×.control_*, form_*, selected_bg, spacing
compact_button, summary_rowBorderless stars and chevrons with enabled/selected states; named two-line rule summary beside an enable checkbox and compact actions.form_close_size, control_*, form_*, typography and spacing
label_actionsClipped row name beside compact actionscontrol_height, form font/spacing tokens
status: status_header, StatusHeaderSupplied icon/name/status and constraint meterfont_label, text/dim, accent, sketch/header tokens
progressClamped determinate progressspace_3/6, dialog_rule, accent
stepsA running operation’s steps: ticked when done, a text-high spinner on the current one with how far it is, a ring for those to comefont_label, control_height, form_ink/dim/primary, space_2/8

History uses the shared panel header, sizing, docking and collapse controls. The bottom position is a fixed-height row with transport and features. Side and floating positions use the vertical content with names and folder controls as available space allows. Feature and folder selection, context menus and rollback stay with History. The view.history_layout compatibility command updates the shared layout without altering the design or opening dialogs.

text(ui, text, options) supports the existing text behaviors and the new error band:

OptionLayout and scenario publication
Text::Hint(key)Wrapping 12 pt hint; rect and handle dialog:hint:KEY
Text::Note(key), Text::MonoNote(key)Wrapping small dim text (optional monospace) with vertical padding; rect dialog:note:KEY
Text::Error(key)Wrapping full-width error band with tinted background and padding; retains dialog:report:KEY
Text::Report(key, error)Native wrapping label, optional error colour; rect/handle dialog:report:KEY
Text::Heading(key)Semibold body heading; retains dialog:report:KEY
Text::NameCell(strong)Medium/semibold clipped clickable name; retains dialog:cell:TEXT
Text::MonoCell, Text::Value(key, error)Monospace unit/value cells; clipped to one row with full tooltips. Value/error cells retain dialog:report:KEY.
Text::InlineNative truncated label; rect/handle dialog:inline:TEXT
Text::CellFixed-height clipped clickable cell with tooltip; rect/handle dialog:cell:TEXT

name_cell_with_hint(ui, name, strong, hint) keeps the name cell’s allocation and scenario identity, but owns its single tooltip: the action hint alone when the name fits, or the full name followed by the hint when it is clipped. Use it instead of adding a second hover tooltip to Text::NameCell.

Examples:

#![allow(unused)]
fn main() {
segmented(ui, &mut mode, &["One", "Two"], Segments::default());
selection_field(ui, "Select", Selection { active: true, filled: false });
value_pair(ui, "Position", [&mut x, &mut y], ["X", "Y"], Some(["Distance X", "Distance Y"]));
button(ui, "Add", Button::Enabled(valid));
footer(ui, "OK", "Cancel", valid, Footer { apply: Some("Apply"), ..Default::default() });
text(ui, report, Text::Report("validation", invalid));
}

Icon surfaces that explicitly choose a palette ramp use icons::paint_accented. None selects base blue, while unknown names retain the icon’s semantic accent. Only active roles change: ink/line outlines, passive faces, guides and knockouts keep normal artwork colours. Existing icon painters retain their semantic-accent behaviour. Danger menu items and danger confirmation glyphs both use icons::paint_danger. It additionally maps passive-1/2/3 faces to constraint active-1/2/3, retaining the shading steps, ink outlines, handle and transparent slat details. The cache distinguishes this whole-glyph treatment from ordinary accent selection and monochrome ink masks. Radial icons follow the active tab (Sketch, Surface, Mesh, Form; other tabs use base blue) in both normal and hovered sectors; the hovered background retains its tab tint.

Navbar_Bar / Navbar_Settings and their dark boards own this composition. navigation_bar_view::paint_row paints both callers; the editor supplies selection, canvas availability and drag interactions. Its stage publishes prefs:navbar:stage; prefs:navbar:bar exposes the positioned bar inside it. Existing nav:* and editor slot/tile/add/appearance targets retain their identities.

Board specimenLogical points / stateAuthority
Bar 1, 6Regular: 32 button, 18 icon, 7 button radius, 40 outer height, 10 bar radius. Compact: 26 / 16 / 6 / 34 / 9NavigationMetrics::for_size, height
Bar 1Border 1, padding 3, gap 2, bottom offset 7; separator width 1 + two 4 margins, height 18NAV_BORDER/PADDING/GAP/BOTTOM/SEPARATOR/SEPARATOR_HEIGHT
Bar 2Menu padding left 7 / right 6, gap 5, chevron 12; medium text 12.5NAV_MENU_LEFT/RIGHT/GAP, NAV_CHEVRON/TEXT
Bar 3Hover / press and open / latched; focus outline 2 with offset 1navigation_visual, navigation_fill, NAV_FOCUS_WIDTH/OFFSET; nav_hover, nav_press, selected_bg, form_primary
Bar 5Width 244, padding 5, radius 10; shared menu shadow (0,14), blur 36; row 30, radius 6, inset 8, icon 17, gap 9; heading 22, text 11, tracking .07emNAV_POPUP_*, NAV_MENU_*, NAV_ROW_GAP, NAV_HEADING_*, navigation_popup_frame, Container::menu_frame
Bar 6Left / Centre / Right, 12 edge insetPosition, NAV_EDGE; legacy bottom / bottom_left / bottom_right deserialize as centre / left / right
Bar 7More button follows the selected size; popup width 200; value text drops before labels and overflowNavigationMetrics, NAV_MORE_WIDTH, nav_bar_layout::measure
Bar 8Idle fade 45%, hidden 0%, restored 100%; hide-mode fade in 120msnavigation_opacity, NAV_IDLE_OPACITY/FADE_SECONDS
Bar 9Tooltip delay 800ms, gap 6, padding (8,5), radius 6; 12 medium text, 11 mono key, 15 key line heightNAV_TOOLTIP_DELAY/POPUP_GAP, navigation_tooltip; nav_tip/tip_ink/tip_key_border
Bar 10Render hides Look at and Visual style; flat canvases hide Orbit, Spin, Look at, Projection and Visual style, retaining Home and DisplaySlot::available, canvas_caps
Bar 11Context width 232; bar has Customise / Hide / Reset, slot has RemoveNAV_CONTEXT_WIDTH, navigation_bar_view::context_menu
Settings 1, 8Stage height 96, radius 8, padding 12 inside its 1 border; a miniature of the 3D view (the view’s gradient and grid colours); the bar follows the chosen anchor (side and top or bottom edge) live, and the slot count sits beside it on the side facing the view’s middleNAV_STAGE_HEIGHT/RADIUS/INSET, navigation_stage, editor_layout
Settings 5Position is one picker of six anchors on a miniature of the view (prefs:navbar:anchor:0..5, top row then bottom row); the separator source is a dashed chip at the end of the Available heading, apart from the button tilesnavigation_anchor_picker, navigation_separator_source
Settings 1–3Standard settings section and full-width detail card; actions stay on one line, with name truncation before icon-only actionssettings_section, settings_card, inspector_header_layout
Settings 4, 5Marker 2×24 radius 1; ghost 70%, dragged tile 55%, hidden slot 40% and dashed borderinsertion_marker_with_ink, NAV_MARKER_HEIGHT/GHOST_OPACITY/TILE_DRAG_OPACITY/HIDDEN_OPACITY
Settings 6Enabled Add buttons answer with “Remove a slot to add another.” at the 16-slot capPure list operations in navigation_bar.rs; warning
Settings 7Standard form label column, control height, segment typography and fills; kit dropdown fallback at narrow widthssettings_form, row, segmented, default Segments
Settings controlsStandard checkbox, Normal buttons, icon buttons, divider and hints. Custom tile content height 30 + two 1 borders = 32 outer; Add 22checkbox, button, icon_button_at, divider, Text::Hint, NAV_TILE_HEIGHT/ADD

The custom tiles use CSS content-box sizing. Their rendered outer height is 32 points, including the two border pixels around their declared height of 30. Editor menu slots also include a transparent one-point border, unlike live menu buttons. Their geometry and icon padding include it while reusing the same painter. Ordinary choice rows use the app’s standard segmented control and its narrow-width dropdown fallback.

Colours shared with the boards use dialog_body (window/popup), dialog_footer (inspector), dialog_border, dialog_rule, control_border, form_ink, form_dim and selected_bg. Additional nav_* roles above, plus nav_field, nav_viewport and nav_dim, have light/dark defaults in crates/theme/src/palettes.rs. They are ordinary solidcraft-theme/1 colour keys: import, export and palette cache identity all include them through theme_files.rs. Files omitting them retain their base palette, including older exported themes. Semantic selection/action ink reuses measured form_primary, and the slot-cap notice reuses measured warning; neither has a navigation-specific duplicate. The colour-vision groups measure them together against the dialog ground. Remaining navigation colours are neutral surfaces/ink, with explicit reasons in NO_MEANING. Popup elevation comes from Container::menu_frame, not a navigation-owned shadow: its (0,14) offset, 36 blur and app-bar colour at 18% replace the board’s (0,4), 16 blur and 10%/30% shadow.

The default is Regular/Centre; saved Compact values stay Compact. slots remains a plain list of IDs; optional slot_options: {"visual_style":{"shows":"icon_and_value"}} stores menu presentation independently of global button labels. Missing Visual style options mean Icon only; Display has no current value and remains Icon only.

Board regression checks live in the existing test modules:

  • navigation_bar_tests.rs: navigation_board_metrics_and_state_colors_are_shared_in_both_themes, navigation_board_popup_width_includes_its_border_and_padding, navigation_board_position_aliases_migrate_and_editor_alignment_follows_position, navigation_board_drops_menu_value_text_before_folding_slots.
  • theme_files.rs: navigation_board_colours_are_themeable_and_roundtrip_on_both_bases.
  • prefs.rs: navigation_bar_preview_stage_follows_position_live; the existing narrow-width regression also checks unelided text bounds at 1600, 880, 640, 600 and 480 points. The existing drag regression covers removal into empty stage space as well as outside the stage.
  • The existing pointer/state, shared-painter, context-menu and canvas-availability tests follow the board’s changed states, editing borders, menu split and hidden slots. The tool-cursor regression uses its Navigation preference because Display no longer lists it. tool_cursor.rs::navigation_preference_restores_tool_badge_on_cancel_and_defaults covers the relocated setting’s snapshot and page defaults. Runtime icon discovery visits More at narrow width, retaining Preferences page coverage.

Type

The kit sets type through ui_kit::type_face: the weights (medium, semibold, bold, RichTextExt), the family and fallback names a host installs its faces under, the script checks and the on-demand CJK font state. It holds no font data. Host contract: before the kit draws, the context’s fonts bind the weight families. A host with faces of its own binds them in its definitions (SolidCraft’s look, solidcraft_look::fonts, installs IBM Plex at four weights; with_weight_families binds any it leaves out); a host with none calls type_face::install_weight_families(ctx) once, which replaces the definitions with egui’s defaults, and the kit draws every weight with those faces. Without either, egui finds no face for the weight families and draws no glyphs for weighted text.

Extending and checking the kit

Add one implementation, document its tokens/options here, re-export it through mod.rs, and add one table entry in gallery::ELEMENTS. Keep implementation-only helpers private. Add interaction tests when the element introduces an action.

The gallery checks geometry, label alignment, handles and effective paint bounds in both themes. Field/footer/panel tests retain focus, completion and action coverage. ui_kit_guard_tests scans every UI feature module recursively, rejects raw control constructors and methods (including aliases), and rejects removed legacy helpers. The raw-control allowance table is empty and guarded against additions. Standard colour/font guards, Clippy, wasm, i18n, assets, icons and layers still apply.

See remaining coverage boundaries for specialised surfaces that remain outside the kit and why.

Panel and Window use 28 pt controls, a 40 pt title band (14 pt semibold title, 19 pt command icon and 26 pt close/fold targets), and a 48 pt footer with 28 pt actions. Table rows are control_height + space_8 (36 pt), independent of control height. Bodies use 12 pt insets. Width limits and scroll boundaries remain owned by Panel/Window/Confirm. Titles retain the supplied case.

The form_ink, form_dim, form_primary, form_error and form_error_fill colour keys match the approved light cards. Dark values follow existing text, accent and error palette roles; every role is exported by theme files and measured by the colour-vision checks. Invalid fields include both error text and a tinted surface. Long variable-height lists can use Window::placed with a top-left pivot to keep their painted controls and scenario targets aligned as they grow. Completion lists use the approved 230 pt minimum width (derived from card and spacing tokens), show kind labels on the right, selected details beneath the list, and the existing keyboard hint.

Parameters and Configuration Table use this one table grid, including Favorites and each model group. The grid has space_8 horizontal insets within the row highlight, shared by headers and cells, plus space_8 column gaps. Flexible widths are measured inside the scroll viewport and rounded by cumulative column edges; fitting content does not acquire a horizontal scrollbar from independent rounding or inherited scroll margins. Explicit minimum widths still allow genuine overflow. The viewport and measured content width publish dialog:table:TABLE:viewport and dialog:table:TABLE:content_width for geometry checks. Hover uses the clipped row rectangle, including interactive children, while preserving child click ownership. Their add prompts publish dialog:table:TABLE:add, use control_height - space_4 height, a dashed control_radius outline and a pointing-hand cursor. Clicking creates a regular unsaved row, focuses its name and exposes the existing field controls and bin. Enter or valid focus loss commits through the existing commands; abandoning a nameless row drops it. Configuration Enter with an empty name retains ConfigurationN naming and copies the active row. Parameter validity is published as param:new:valid; invalid expressions cannot commit on focus loss. The natural scrolling body height is published as dialog:body:content_height, so geometry tests distinguish content size from the viewport cap. After a successful parameter add, a fresh empty draft retains Name focus for continuous keyboard entry; leaving that empty row drops it. Parameter draft fields retain param:new:name/unit/expression/comment, and the draft’s bin button publishes param:new:delete. Configuration drafts publish config:new:name; existing config:cell:ROW:COLUMN, activation, rule and preview handles remain. The header plus retains dialog:menu:Add Column, including the existing menu nesting. The old separate Add forms and column-removal band have been removed.

Crash Reports uses report_list (bordered bounded scroll), report_header (kind, count pill and mono UTC time), path_chip (clipped mono path and adjacent action), empty_box (dashed empty state), and icon_label_button (secondary icon/text, optional danger ink). These use dialog_rule/footer, control_*, form_*, font_label/small and spacing tokens. The report header retains the combined-title cell alias; bins retain the previous Delete handle. Report actions remain immediate. list_frame supplies the same border to virtualised library rows. The list frame owns its border and horizontal padding; the report viewport adds no inherited scroll margin. Report list measurements publish dialog:list:KEY:viewport and dialog:list:KEY:content_width. Reports use vertical scrolling; the horizontal rule beneath each report remains a divider. Appearance uses a 340 pt card derived from card/label/spacing tokens, with the 104 pt label basis where space permits, 10 pt body gaps, and a count on In This Design.

Colour popovers retain dialog:color:ROW and add color:format:U8/F, color:copy, color:channel:R/G/B, color:sv, color:hue, color:current and the color:popover rectangle. Format changes update egui’s global numeric colour presentation without changing RGB. Linear fields edit linear RGB, not normalised gamma bytes. HSV state preserves hue at zero saturation/value and sub-byte precision during drags. Gradient colours are document colour data; all interface chrome uses existing paired light/dark tokens.

Sliders only via ui_kit::slider, everywhere in crates/ui-egui/src and apps. The element owns its form styling even when called from a viewport menu. Slider options select behaviour, never appearance: range and values remain in caller units, step preserves stepping, format preserves suffixes/display mapping, integer rounds manipulation and show_value: false hides the editor. Disabled sliders use ui.add_enabled_ui; tooltips attach to the returned response. Preferences UI scale still applies only after the pointer is released. There are no alternative slider helpers, raw egui::Slider callers, or custom track/thumb painters outside the kit. The source guard scans both trees without an allowance table; the gallery covers continuous, percent/stepped, integer/no-editor and disabled states in both themes. Numeric fields use the existing kit scalar_field, length_field, angle_field or number_field rather than constructing a raw egui::DragValue at a caller. For a numeric tuple, call number_fields(ui, &mut values, range, speed, suffix) inside row. It divides the remaining control width equally after reserving the inter-cell gaps, and keeps each cell’s editor width and native input minimum local to that cell. An empty slice draws nothing; responses follow the input order.

Parameters retains its bounded header drag offset while the manager is open, including frames without a regular feature dialog. The shared Panel bound reserves header and footer space at the bottom edge. Import and both export actions use icon_label_button with the existing import/export arrow icons and unchanged command routing and scenario identities. The gallery includes all three actions. The Add Column target is form_close_size - space_4 (22 pt) square: idle paint is only form_dim ink; hover adds the selected accent fill and insertion guides. HSV gradients use a rounded mesh silhouette sampled along control_radius arcs; the current-colour strip uses the same rounded radius. Render tests sample all four corner pixels and opaque interiors of all three surfaces at 1× and 2× in both themes.

Checkboxes only via ui_kit::checkbox, everywhere in crates/ui-egui/src and apps. checkbox_row, checkbox_grid and menu checkbox rows share its single box painter and paint_check_glyph path. The box is space_14 square with radius_sm corners, a control_border outline and control_fill background. Checked/mixed boxes use form_primary and on_accent; hover uses hover and an accent border, focus adds a space_2 outset ring, and disabled controls inherit egui’s disabled opacity. Geometry never depends on caller font or native icon size. checkbox_indeterminate represents a mixed selection with a bar and activates to checked. Space/Enter, accessible checked state, tooltips and scenario handles are retained. Exclusive choices use existing kit segments/dropdowns/list pickers; there are no radio controls. The recursive guard rejects native checkbox/radio constructors (including aliases), raw toggle methods and duplicate check glyph painters across both trees without allowances. Offscreen checks compare actual box and tick geometry/pixels across form, grid, palette and menu call sites at 1×/2× in both themes.

Disclosure chevrons point down when open (content visible) and right when closed (content hidden). Only ui_kit::disclosure draws them, using the original chevron / chevron_right SVGs at form_section_icon_size, without animation (as in kit section headers). Sections, nested groups, Browser trees, card headers, window folds and the Assistant strip all follow this rule. Keep existing scenario handles, including dialog:collapse, and translated Collapse/Expand tooltips. Dropdown arrows always point down; submenu arrows point right. Bottom-strip hide/restore actions use strip_toggle with the same down chevron and its vertical flip for up. Playback, reorder, window management and overflow controls are not disclosures. The source guard scans UI, apps and compiled asset glyphs without caller allowances; render tests inspect the actual icon textures used by sections, card folds and window folds.

Per-tab accent tokens

TabAccent::for_tab supplies three independent theme colours: color (fill) for pills, active-tool borders and the history marker; ink for small text and selected rows; soft for tinted selection/tool backgrounds. These are palette entries, not blends. Browser rows and selected ribbon labels use ink directly.

Tab idFill tokenInk tokenSoft token
MODEL / defaultaccentselected_inkaccent_soft
SKETCHsketch_accentsketch_inksketch_soft
SURFACEsurface_fillsurface_inksurface_soft
MESHmesh_fillmesh_inkmesh_soft
FORMform_fillform_tab_inkform_soft
SHEET METALsheet_fillsheet_inksheet_soft
RENDERrender_fillrender_inkrender_soft

form_tab_ink is separate from the existing form_ink used for form controls. Sketch reuses its existing fill in both themes. Surface and Form fills match their existing accents in light mode but need independent dark values; Mesh and Render fills also differ from their existing mesh_accent and render_accent values. All existing tokens, including Model’s fill, ink and tint, retain their values. The Model/default mapping preserves its existing appearance rather than adopting the proposed Model colours from the board.

The new palette entries (fill / ink / soft) are:

TabLightDark
Sketch#057F45 / #057F45 / #EDF7F1#128D45 / #46BE78 / #1F3B2B
Surface#A45000 / #A45000 / #FBEBD8#B26A14 / #E0914A / #45331E
Mesh#E0B200 / #8A6D00 / #FFF8DC#C6A800 / #D4B726 / #41391A
Form#7349CD / #7349CD / #EEE8FA#8A6FCC / #B39BEE / #372F4F
Sheet Metal#1E6F96 / #1E6F96 / #E1F0F7#2A7FA8 / #4FB3D9 / #1F3A47
Render#A8326E / #A8326E / #F8E6EF#C8588F / #E074AE / #402336

Each key is supported by solidcraft-theme/1 import/export, including tab_on_fill_dark (#000000 in both themes). TabAccent::on_fill uses on_accent (white in the built-in palettes) when it reaches 4.5:1, otherwise that dark ink. The fallback is black because the board’s dark ink does not reach 4.5:1 on several of the approved fills. It only styles tab pills; global primary actions still use on_accent. Built-in palette tests require WCAG contrast of at least 4.5:1 for ink on the card and panel, ink on soft, and pill text on fill in both themes. Only Mesh separates ink from fill in the light theme.

Context menu adapters retain item IDs, enabled logic and command parameters. Menu owns popup elevation (10 pt corners, 1 pt border), headings, separators, adjacent submenus and bounded arrow navigation. Enter opens a submenu or chooses its row; Right opens, Left returns, and Esc dismisses the menu. Appearance includes document swatches and Remove override through appearance.assign; its original action still opens the full editor. Workspace retains its descriptive cards and scenario handles on the shared surface. Ribbon rows reuse MenuItem::paint while owning drag, focus and pin interactions. History context menus and ribbon overflow use kit popup chrome. Rename uses the shared caption, field policy and Cancel/OK actions with its stable editor ID. No additional translation keys or colour roles are needed.

The menu gallery includes normal, hovered, open, disabled, checked/unchecked, swatch and danger rows, a submenu and rename. Radial geometry tests measure the actual side endpoints at both radii and sample all four rounded corners. Optional CPU-rendered menu evidence can be produced without a window with SOLIDCRAFT_MENU_EVIDENCE=/tmp/menus cargo test -p solidcraft-ui-egui --lib menu_board_in_both_themes. Desktop interaction and visual acceptance still require a reviewer capture.

Command search uses the ribbon field itself (sc_sbox_text); ribbon:search, shortcut:search and palette:search all identify that same field. Opening gives it the accent focus ring, cursor and Esc keycap. The field and the attached results sheet unfold in 120 ms with egui cubic ease-out, capped by egui’s animation time so instant settings remain instant. Esc, click away and command dispatch reverse the animation. Only the visible sheet height participates in layout and hit testing; result content is clipped while unfolding. The sheet’s ribbon:search_popup / palette:outer handles exclude the field. Its footer (ribbon:search_footer) fills the bottom strip edge to edge, with rounded bottom corners and a 1 pt top rule. Ranking, command mode and keyboard routing remain with the caller.

Generate CPU-rendered evidence of all five search states and the actual ribbon’s closed/opening/open/closing transition in both themes with SOLIDCRAFT_SEARCH_EVIDENCE=/tmp/search cargo test -p solidcraft-ui-egui --lib command_search.

Compact workspace composition

Panel::workspace accepts WorkspaceOptions::compact to display the minimum dock width while retaining the remembered width. Compact widths do not resize the saved preference; returning to a wider floating layout restores it.

Container::gap and Container::frame supply the layout policy for every adaptive surface. Compact has no gaps or shadows between edge-bound panels; floating uses the shared card spacing and frame. Panel bounds draggable compact sheets to the workspace. The shared frame rounds all corners away from the right dock and only the left corners while right-docked; the footer inherits that frame’s bottom corners. Both layouts retain drag offsets. Browser uses the same content renderer and width in both modes, with a square, flush ribbon join in Compact. Non-workspace surfaces keep their roles. DockEdges declares touching edges. The container measures content-hugging cards before rounding their exposed corners, and aligns touching outlines onto a single border strip without changing logical panel allocations. The application bar has an independent surface role. The saved Prefs::layout is changed through view.layout; ui.set may still make a transient automation override. With the universal host enabled, the model render domain fills the area below the application bar in both layout modes, including behind ribbon, docked and floating chrome. Paint/input footprints remain separate: transparent styling does not expose controls to picking. The usable region follows actual occupied edges, retains short-panel gaps and floating-panel holes, and supplies explicit Fit/Centre and measured navbar/compass placement. This contour also includes actually painted persistent surfaces outside the host, such as the active sketch palette. Closing one releases its footprint; transient menus shield input without changing fitting or control placement. Ordinary panel operations preserve the camera and its framing. Fit may change zoom; explicit Look At/view.normal_to centring preserves zoom, distance, orientation and selected focus. If the selection cannot fit at the current zoom, only its focus moves to stable usable space; it does not trigger a zoom-out.

Universal title/tab drags offer explicit dock, split and tab targets. Their landing preview uses the same prepared geometry as the final transaction, including affected neighbours and insertion index. Releasing outside a valid target cancels; floating placement is an explicit action or command. Previewing never changes the committed usable region or the render projection.

File/start presentation

FileView owns File surfaces and controls. It accepts translated strings, existing icon ids and cached thumbnail textures; callers retain commands, document state, persistence, thumbnail jobs and recovery decisions.

  • page, content and rail own the page background, responsive insets and full-height rail fill/clip. navigation also supports the narrow navigation menu. Callers supply layout rectangles, scrolling and entry order.
  • nav_row accepts a stable id, label, shortcut, selection and enabled state. back_to accepts a translated {name} template, paints the document name semibold and reuses the mirrored chevron and outlined keycap. separator supplies grouped rules. Disabled rows cannot activate.
  • title, subtitle, detail and section own typography. section accepts a token-based heading/body gap: space_12 for New, space_10 for Recent/Samples. Callers retain semantic section order and the larger file_section_gap.
  • grid provides responsive columns with 14 pt gutters and no trailing row gap. action_tile supplies an 84 pt card with 44 pt original artwork and vertically centered label/subtitle. tile_min_width informs responsive columns.
  • recent_list owns the rounded border and zero row gap. recent_row accepts a RecentRow, paints internal separators, medium names and workspace dots, and retains title/path at narrow widths. Callers handle unavailable-file actions.
  • sample_grid(ui, id, &[SampleCard]) measures each row’s tallest content and stretches cards to equal bottoms without clipping descriptions. Responses stay in sample order, with stable {id}:{index} ids. Artwork inputs remain unchanged.
  • recovery_banner accepts RecoveryMessage segments for a bold title, normal message, monospace filename and normal suffix. Actions align right or wrap below at narrow widths. Discard is optional for summaries; the return value is (recover_response, optional_discard_response). recovery_review uses the same geometry with per-design Discard. Callers provide truthful metadata.

The exported file_* colour tokens provide readable light/dark foreground and background pairs, including File-specific secondary text. Thumbnails retain the neutral file_image_tint; icon and text colours do not change their pixels. Spacing, dimensions, fonts and radii come from theme::Tokens.

Gallery and geometry tests cover these components. To capture the actual File view using public temporary fixtures, set SOLIDCRAFT_FILE_EVIDENCE to an output directory and run file_view_application_evidence_in_both_themes_and_widths with --ignored. It produces desktop and 420 pt narrow PNGs in both themes.

button_at(ui, id, rect, label, Button::Primary/Normal) places the same shared native button used by button inside a bounded, centred layout. It preserves stable identity, enabled state and exact caller-owned geometry. Recovery actions use this component rather than custom button painters. The recovery well renders the existing restore artwork through its construction accent ramp.

Theme corner radii

Theme files may override radius_sm, radius_md, radius_lg, radius_xl, control_radius and sketch_mode_radius in an optional radii object (integer UI points, 0–32). Missing entries retain the base palette’s shape. Light and dark variants of a family should use identical values. Import/export and preferences retain these overrides; colour vision modes leave them alone.

Use Tokens for decorative UI corners, including hover, selection, focus, image thumbnails, popups and nested surfaces. Small legacy accents derive their radius from the small token so the default appearance stays unchanged. Expanded outlines use Tokens::outset_radius so square corners stay square. Container continues to own outer surface chrome and square dock contacts. Functional geometry (circles, model selection boxes, plot clipping, manipulator handles and the artwork inside icons) retains its own shape.

Theme border hierarchy

Three structural levels distinguish outer chrome, major section rules and minor dividers. Separate solid container and control roles keep enclosing boxes independent of divider patterns.

RoleUI audit / assignment
PrimaryRibbon/workspace cards, Browser/History, feature and palette sheets, system windows, menus/popups, navigation/status surfaces
SecondaryRibbon tab/content rule, Browser/History heading rule, feature/window header and footer, Preferences page/navigation division, disclosure section rules, Assistant header/composer division, File navigation sections
TertiaryRibbon category dividers, menu groups, report/table rows, neutral History items, File cards/thumbnails, ordinary inherited egui separators
ContainerSettings previews, information cards, list/detail wells, Assistant content cards, shortcut keycaps
ControlNeutral fields, buttons, checkboxes, segmented choices and search inputs

Focus, selection, active commands, errors, recovery warnings and status keep their existing semantic strokes. Icon artwork, example mouse diagrams, plots, model edges, manipulator geometry and the compass are content; structural border roles do not recolour them.

solidcraft-theme/1 optionally accepts borders. Each role has a width (finite 0–4 logical UI points; zero hides the stroke) and a color (#rrggbb or #rrggbbaa). primary_inset also requires gap (finite 0–12 points of clear distance inside the primary border). This companion is painted inside panel/ribbon/window/menu chrome, without extra input targets or layout space; tiny surfaces omit it. Compact contacts remain square and collapse adjoining primary strokes. Other ornament, such as the circular compass and native widget-owned tooltip content, does not acquire an inset.

"borders": {
  "primary": {"width": 1, "color": "#ffffff"},
  "secondary": {"width": 1, "color": "#e5efff", "pattern": "dotted"},
  "tertiary": {"width": 1, "color": "#102748"},
  "container": {"width": 1, "color": "#a8c8ed"},
  "control": {"width": 1, "color": "#a8c8ed"}
}

Those are Blueprint’s settings: a single primary outline and dotted secondary rules. Secondary roles optionally accept pattern: solid (default) or dotted; this pattern follows horizontal/vertical section rules. Container outlines remain solid, as do the other roles. Themes without border settings retain their previous appearance. SolidCraft 95 uses silver surfaces and directional two-band bevels: light top/left edges and dark bottom/right edges for raised panels and buttons, reversed for recessed fields and pressed buttons. Inputs use the field colour independently of the neutral button face. Disabled controls fade their bevel; focus, selection and validation keep their semantic indicators. Solid grey rules divide sections.

Optional borders.bevel enables this shared container/control treatment. It accepts width (finite, greater than 0 and at most 2 points per band), highlight, light, shadow and dark_shadow (hex colours). Missing bevel settings preserve flat rendering exactly. The bands paint inside existing rounded or square geometry without changing hit targets or allocation. Ghost controls remain borderless. Missing roles preserve each surface’s existing width and colour, including overrides in colors; exporting keeps omitted roles absent so reimport cannot flatten those legacy surface differences. Dark/Light and the Gruvbox families share border geometry. Imports and direct preference updates reject unknown roles/settings, invalid colours, non-finite/out-of-range numbers and missing required settings. Invalid hand-edited saved border settings safely fall back to legacy styling. The optional field requires no format version change. There is no general border editor in Preferences; theme file import/export and the existing preference/control workflow own this extension.

Theme icon and tab colours

Theme files optionally supply icon_palette SVG roles and semantic ramps. The shared renderer snapshots that palette into the icon look and cache identity; callers continue to request semantic icon IDs and surface-specific monochrome ink. See the shipped-theme colour audit for the schema, workspace assignments, accessibility rules and intentionally monochrome themes.

Preference page scrollers keep their content width separate from the scrollbar host’s gutter extension. Child layout overflow contributes height only, so it cannot widen the window and leave earlier sections clipped at their previous right edge. The Navigation editor follows the board’s composition and uses standard kit controls for ordinary settings, rather than a second control skin.

Material previews and look chains

sphere_thumbnail(ui, id, Sphere { color, kind, opacity }, size) paints a lit sphere using a fixed CPU mesh and the egui painter, without a texture or GPU. FinishKind covers Matte, Satin, Gloss, Metal and Glass. Colour is document data; highlights, shadows, checker and UI chrome use Tokens. Glass shows a four-by-four checker and defaults to 35% opacity; other finishes default to opaque. Explicit opacity is clamped to 0–1; non-finite opacity uses the finish default. Sizes are rounded and bounded to 1–96 logical points (non-finite sizes become 1).

A shared per-egui-context FIFO cache retains at most 64 meshes, keyed by RGBA, finish, byte opacity and bounded size. Hits reuse shading without changing eviction order. Every mesh has 637 vertices and 1,152 triangles, independently of input. Theme highlight/shadow changes clear the cache. No file assets are added.

look_chain(ui, id, labels, looks, active) draws Face → Body → Component → Material as four connected steps. Pass translated labels, [Option<Sphere>; 4] and one LookStep; empty slots use a dashed circle and plus, while filled slots reuse the sphere cache. Exactly one step has a Tokens::render_accent ring. It returns Option<LookStep> on activation; callers own commands and selection. Disabled controls do not activate. Handles/rects are {id}:face, :body, :component, :material; {id} is the full allocation. Long labels clip within their step. These are content widgets; their enclosing surface still belongs to Container.

Gallery entries show all five sphere finishes, a mixed filled/empty look chain with Body active, and an empty chain with Face active in both themes.

For CPU-rendered review images, run SOLIDCRAFT_MATERIAL_WIDGET_EVIDENCE=/tmp/material-widgets cargo test -p solidcraft-ui-egui ui_kit::gallery::material_widgets_board_in_both_themes.

catalog_scope supplies compact catalog metrics to the shared search field, segments, icon segments and category chips. catalog_footer draws the quiet information hint. CatalogLayout::new(ui, id, min_rows) reserves the measured footer height and gives body all remaining space without shrinking to its items. Call footer after body; changes to the footer’s natural height request another layout pass. Use min_rows = 3 for a search list with selected-item details below it; the detail card scrolls when necessary. The kit publishes {id}:content, :body and :footer rectangles for layout checks. SphereTile reserves two full font rows below the sphere. Names wrap at word boundaries with an ellipsis after the second line; {id}:label and :galley expose the caption allocation and laid-out text bounds. Catalog title marks use the active workspace accent; tile captions, usage badges and selection outlines share the sphere widgets. Feature code supplies data and commands, while the kit owns every dimension, colour and inset.

The Appearance Inspector uses the shared inspector_header (selection icon/name, workspace chip, Menu and close action) and Panel::editor_footer (48 pt pinned band). mixed_color_field uses the one shared colour popover with a non-editable mixed-value label, not an outer Hex editor. Non-PBR Appearance passes exact opacity; PBR passes no opacity and remains opaque. All Hex parsing and draft state stay in the kit. mixed_slider pairs the standard slider with the mixed-aware scalar editor. editor_section uses a 24 pt heading and 6 pt field gaps. workspace_chip, text_button, section_heading, pending_section, searchable_icon_dropdown and Menu::text retain shared token paint and hit targets. All are exercised in the kit gallery.

Shared editor componentBehaviour
method_sectionCompact, initially collapsed editor section with a method tag; unsupported contents remain inspectable and disabled, with a translated reason visible even when collapsed. Uses editor_section typography and spacing.
vector3_fieldThree bounded number fields divide the row’s control column evenly, retaining the shared parser, focus and bevels. Used for RGB absorption and world-space origin/repeat vectors.
sphere_preview_with_sourceSample card with an interactive source tag, retaining the sphere, name, usage badge and trailing action. Appearance uses the tag to inspect its inheritance chain.

The compact settings_choice_row variant reuses the shared settings radio-card painter for preset lists: 40 pt rows, right-aligned summaries and the active workspace accent. progress_action keeps a name, a status, a bounded progress track and a Cancel action on one row inside a Navigation card. Cancel and the status always show whole; a long name is cut first (ellipsis, full name on hover), then the track narrows. Container::below_navigation_row reserves that card and its shared gap above an inline result toolbar.

number_pair_preserving extends the shared value-pair row to bounded numeric fields (such as image width and height), preserving label alignment and clipping. Authored out-of-range values remain unchanged on paint; explicit typing and dragging still respect the selected bounds.

Preferences cards, figures and help (redesign)

The redesigned Preferences pages (tracker #9863, reference in docs/design/preferences/) compose these shared pieces; the pages themselves arrive in later steps. Sizes follow docs/design/preferences/measurements.md.

ElementUseMain tokens
picture_card(ui, &Card, width)A radio card: a figure, a label, an optional sub-label, an optional “has options” disc (its text is the hover) and an optional accent check. The figure keeps its aspect ratio and fills the card’s inside width; the card sizes to the width its container gives it (card_width(available, columns, gap) divides a row). Selected: 2 pt form_primary border, selected_bg fill, selected_ink semibold label. Disabled: dimmed, no hover, no click, no focus ring. Keyboard focus draws a ring outside the border. Reports RadioButton to accessibility.form_primary, selected_bg, selected_ink, hover, control_fill, dialog_border, form_ink, form_dim, on_accent, radius_lg, space_*, font_label/small
toggle_tile(ui, &Card, width)The same picture with a 15 pt checkbox in the top-right corner (Card::selected is “on”). Reports Checkbox.as above, control_border
settings_picture_choices, settings_picture_choices_sized, PictureChoice, PictureChoiceSizeA labelled, wrapping radio group with stable option handles and one search result for the row or matching choice. Returns the picked index. The sized variant accepts the card width, figure aspect and caption visibility used by independent Preferences pages.card tokens, space_10/12/16/24
preset_card(ui, name, word, selected, enabled, &icon, width)A 70 pt card with a 38 pt icon box (icon paints into it in the ink it is given), a name, one word and an accent check. Used for Performance, Quality and Custom.as above, pref_well
help_icon(ui, text)The (i) after a label. Shows text as a tooltip on hover and in a popover after a click, Enter or Space; a click outside or Escape closes it. Exposes text as the button’s label.form_dim, form_ink
shortcut_binding(ui, chord, editable)Complete mono keycaps using the command-search painter; one focusable recorder target per binding, or a read-only label. Empty chords show dim Not assigned. shortcut_binding_width measures the same font and padding, including literal plus keys. Gallery: shortcut_binding.font_small/label, control_height, form_dim, form_primary, space_4/8/10, radius_sm
theme_browser(ui, id, count, grid, reveal, item)The Appearance theme list: a carousel (one clipped row, an edge fade in the container fill, round arrows on a side with more cards, an arrow moves by the cards that fit, drag and horizontal wheel scroll while vertical wheel input goes to the page) or a 5-across grid with its own vertical scroll. reveal scrolls an index into view. The box and fades use the settings_card_frame fill and stroke.dialog_border, dialog_border, control_fill, hover, form_ink, radius_lg

card_look(selected, enabled, hovered, focused) holds the state rules (border width, fill, ink, weight, dimming, focus ring) apart from the painting so they can be tested.

Standalone picture cards, toggle tiles, preset cards and settings chips participate in Preferences search and reveal their own bounds. Labelled rows retain ownership of their search result. section_heading is available to production callers for groups that stay expanded.

Figures

crates/ui-kit/src/preferences_figures.rs paints the illustrations of the Preferences boards. The geometry of each comes from its SVG in docs/design/preferences/figures/: tools/preferences-figures/generate.py compiles those files into crates/ui-kit/src/preferences_figure_data.rs, one constant and one painter per figure (paint_tess_normal, paint_orbit_origin, …; FIGURES lists them all), and preferences_figure_ops.rs paints the operations with egui shapes. A figure is a list of rectangles, circles, polygons, soft shadows and tiny captions in the figure’s own coordinates. Nothing is rasterised and no image is loaded; a figure costs a few dozen shapes and scales to any rectangle, keeping its aspect ratio. A zero, tiny, huge or non-finite rectangle paints nothing or something clipped, never panics.

To change a figure, edit its SVG (light and dark) and run python3 tools/preferences-figures/generate.py; never edit preferences_figure_data.rs. The colour of each element is a role read from Tokens: figure_ground, figure_line, figure_face_top, figure_face_left, figure_face_right, figure_card, figure_accent, figure_reference, figure_axis_x/y/z, figure_ok, figure_warn, plus form_ink, form_dim, dialog_border, control_border and control_fill (docs/design/preferences/tokens.md). The new tokens, with light and dark values, are in crates/theme/src/palettes.rs; theme files cannot override them yet and keep the built-in values. The colour vision figures (paint_colour_vision) read axis_z, sketch_projected, axis_x, bend_up and bend_down, so they follow the colour vision mode; the host composes them into its own sample with its own names.

Theme thumbnails (paint_theme_thumbnail, paint_theme_system) take a ThemeSample (12 colours, ThemeSample::from_tokens(&tokens)), not the dialog’s tokens, because a card shows its own theme. The Orientation figures and the visual-style icon are not repeated here: the app already has them.

New pieces and figures each have an entry in gallery::ELEMENTS (picture_card, toggle_tile, preset_card, help_icon, theme_browser, preference_theme_thumbnails, preference_figures), checked in both themes. SOLIDCRAFT_PREFERENCE_WIDGET_EVIDENCE=<dir> keeps the CPU-rendered boards (preferences-widgets-*.png, preferences-figures-*.png) from preference_widgets_and_figures_board_in_both_themes.

Assistant permission cards accept an optional SettingsChoice.icon; Do everything uses the existing warning artwork, including its exclamation mark.

Extensible libraries: materials, appearances and components

The first local scalar-library consumer implements only copied colour/opacity and density/look definitions. Its separate v1 catalog is not the complete resource-pack/provenance/update system proposed below. Texture/environment descriptors remain coordinated with the maintainer’s renderer work.

Proposal for #785, #786 and #787. This page describes planned contracts and acceptance gates, not an implemented pack format or new commands. It does not bring the deferred Simulation or Render workspaces into scope.

Reuse the existing paths

Physical materials already have built-in densities and design-local definitions (material.create, material.edit, material.delete, material.list, material.assign). They drive component.properties, survive reopening and participate in configurations. Appearances have a built-in color/opacity library and body, face and component overrides. parts.library, parts.insert and parts.resize already generate seven families of fasteners, pins and bearing envelopes as editable components.

Extend those routes. A shared catalog supplies identity, discovery and provenance; each entry kind retains its own payload and application rules. A physical material is not a texture, and a reusable component is not executable plugin code.

Portable, independently extensible packs

Use one versioned, offline pack envelope for three kinds: physical material, appearance and component family. Both project contributors and users can author packs without rebuilding SolidCraft. Start with a directory and JSON manifest, not a new archive format or an online marketplace. The exact schema belongs in the bounded-loader PR, accompanied by machine-checked examples; the following are its design requirements:

RecordRequired meaning
PackSchema version, stable namespaced ID, immutable revision, display name, author, source and license metadata
EntryID scoped to its pack, immutable revision, kind, label, category/tags, typed payload and per-entry provenance
ResourceRelative path, content digest, byte count, media role, author, source, license and included license text where required
DependencyExplicit entry/revision reference; bounded acyclic resolution within explicitly selected packs
SelectionPack ID + entry ID + exact revision; labels and filesystem paths are not identities

Pack revisions and schema versions are different: the former identifies content, the latter describes how to read it. Changing content under an existing revision is an error. Unknown schema versions, entry kinds and required capabilities produce a clear refusal; they are never silently interpreted as an older format. Optional extensions need an explicit namespace and preservation rules before being accepted.

Provide three independent scopes:

  • Bundled: reviewed with SolidCraft and read-only in the library editor.
  • Project: packs explicitly opened for the current project/team; never discovered by silently executing or trusting files beside a design.
  • User: local packs the user creates, imports, edits or exports. Native file access belongs in the app; browser users import/export through the web storage/file workflow.

Discovery order is deterministic. Duplicate labels can coexist, with their source shown. Duplicate qualified identities/revisions with different contents are errors, not overrides chosen by scan order. Editing a bundled entry uses Save a copy into a user namespace. Updating a pack is explicit and leaves existing design instances unchanged.

Designs own their selected definitions

On assignment/insertion, copy the definition, chosen revision, parameters, provenance and required resource bytes into the design’s existing container/resource mechanism. Keep the source identity for inspection and an eventual explicit Update action. A saved design must open, rebuild and render the supported appearance without the original pack, network or another user’s home directory. Installing a pack never changes a design or undo history.

Assignment/insertion/update is a normal engine command, atomic and undoable. Preview is non-mutating. A failed import, unresolved dependency, name collision or unsupported recipe leaves the document and undo/redo stacks intact. Existing material names and appearance precedence retain their legacy meaning; introduce stable IDs through a documented migration rather than reinterpret old name-based assignments. Design-format changes need their own version/migration and reopen tests, separate from the pack schema version.

Physical properties (#785)

Keep physical material and appearance selection independent, with an optional default look. Convert imported density units explicitly to the existing g/cm³ representation; geometry continues to use millimetres and radians. Never infer units from magnitudes. Properties need source, edition/date, grade, processing condition and applicable temperature/moisture when relevant. Unknown properties stay unknown, not zero or guessed values.

Start with density and mass integration through the existing commands. Reserve typed extensions for elastic constants, yield/ultimate strength, thermal properties and orthotropic axes, but do not expose these as FEA-ready until their model, units and validation are implemented. A generic wood density is not an engineering material grade. User-measured data can be stored with its own provenance and edited without changing the bundled library.

Wood and sheet appearances (#786)

Begin with body-local mapping, an explicit physical repeat size and a user-editable grain frame. Suggest the longest local body-bound axis once, with X/Y/Z order for equal extents; store the chosen frame rather than recompute it on every resize. Manual rotation and scale survive parameter rebuilds, instance placement, undo and reopening. Shared component instances transform the same definition-local mapping with their placement. Non-finite or degenerate frames refuse; automatic choice remains a suggestion.

Visual grain does not rotate the body, its geometric bounding box or a manufacturing stock frame. Structural grain direction and grain-aware nesting/cut lists are distinct features, coordinated with #137. An anisotropic shading effect also does not imply mechanical anisotropy.

After basic wood mapping, add sheet-specific top/bottom/edge looks: MDF, chipboard, plywood and compact laminate, then melamine, laminate, linoleum and veneer finishes. Appearance layers initially describe presentation only. Physical finish thickness or mass must be explicit modelling/material inputs, never an invisible side effect of paint. Face/edge assignments require durable identities and must refuse a lost association rather than paint a nearby replacement face.

Keep texture role, color space, normal-map convention and physical scale explicit. Coordinate with the render capability contract: the existing color/opacity viewport is not proof of PBR or texture support. A renderer must report what it supports; unavailable maps use a documented preview fallback with a visible notice. Export either preserves supported mapping/resources or reports loss.

Standard and user component families (#787)

Keep the existing fastener insertion/resize path. Add original parameterized BAR, SHS, RHS and CHS recipes first, with explicit length units, parameter bounds, section frames and insertion/connection frames. Validate wall thickness against the outer size and state whether corners are sharp or rounded. Do not label approximate geometry as a certified standard section. UB/UC need actual flange/web radii and tapers; identifying a standard does not grant redistribution rights to its published size tables.

A family revision defines a bounded parameter schema, permitted recipe and optional reviewed size table. A size designation identifies a row, not an arbitrary filename. Changing size rebuilds the same component identity and preserves supported connections. Initial recipes must use a small allowlist of ordinary modelling operations. No embedded native code, shell commands, scripts, arbitrary command IDs or network hooks. Reject cycles, excessive expansion and unsupported operations before mutating the design.

Custom assemblies follow single components: bounded dependencies, named connection frames, explicit shared/independent instance semantics and one-step insertion undo. Store selected parameters and definition snapshots so missing or changed catalogs cannot silently substitute another part. Geometric envelope models, including existing bearing envelopes, must remain distinguishable from detailed/manufacturing-ready models.

Sources and licensing

SolidCraft is MIT OR Apache-2.0. Code and generated recipes must follow the clean-room rules; never copy commercial CAD materials, templates, geometry, icons or catalogs. Third-party assets retain their own licenses. Every bundled resource needs its author, source, license and ATTRIBUTION.md row, even if attribution is optional under CC0. Record hashes and retrieval dates when admitting actual files. A manifest’s claimed license is provenance to review, not automatic evidence of permission.

Sources checked on 2026-10-13; no external assets or datasets are bundled by this proposal:

SourceSuitable use / admission condition
ambientCG licenseDownloadable assets and material preview renders are CC0. Candidate wood/sheet texture source; choose and attribute individual files.
Poly Haven licenseAssets are CC0. Site text, logos and other site content have separate rights; API access has separate terms. Do not copy provider implementation code.
Wikidata structured-data licenseStructured entity/property data are CC0; other page text has different terms. Candidate for individual referenced quantities and identifiers, not unverified engineering-grade defaults. Preserve the underlying measurement references, qualifiers and units.
USDA Wood Handbook, revised 2021Reference for moisture, physical and mechanical wood properties. Verify the specific table’s rights, species, units and conditions before importing data; no wholesale dataset approval is implied.
NIST data/publication rights and repository termsCandidate property references only. Uploaded data use provider-selected terms; public availability is not a blanket redistribution license. Review each dataset.
Khronos PBR overviewPublic rendering conventions for texture roles and shading properties; a design reference, not a source of CAD material data.
Original SolidCraft recipes / user measurementsPreferred initial component recipes and illustrative procedural previews; preserve authorship and label illustrative versus measured values.

Prefer CC0 resources for the starter texture pack and original recipes for the starter component pack. Defer uncertain or restrictive data rather than scrape supplier sites, copy standards publications or assume a downloadable CAD file can be redistributed.

Phases and acceptance gates

Each implementation PR references these issues without closing them prematurely.

PhaseDeliverableRequired evidence before calling it complete
0This proposal, source review and roadmapMaintainer-reviewable contract; distinguish existing functions from planned ones; no runtime or format changes.
1Shared bounded manifest parser, resolver and pack validation/import/exportProject and user manifests use the same schema; deterministic conflicts, invalid versions/IDs, missing resources/dependencies, cycles, path traversal and budget exhaustion fail atomically; native + wasm pass.
2Material and appearance catalog integration and browser UICreate/copy/edit/import/export from the UI; existing commands used; analytic box mass before/after assignment; configurations, undo and offline cold reopening; actual screenshots.
3Wood mapping and a small reviewed starter packRotate/scale grain on a public-safe block and placed instance; no flip on resize; reopen without pack; rendering capability/fallback tests and screenshots; attribution for each shipped asset.
4Parameterized section families and reviewed standard size dataBAR: area = width × height; sharp RHS/SHS: outer area minus inner area; CHS: π(outer_diameter² − inner_diameter²)/4. Check volume = area × length, hollow membership, insertion frame, resize, connections, undo and offline reopening; screenshots.
5Custom assemblies, sheet face/edge layers and expanded catalogsBounded nested insertion, shared/independent semantics, durable face associations, explicit updates and loss reporting; measured tests and screenshots for each workflow.

After phase 1, material/appearance integration and section-family work can advance independently. Wood mapping depends on actual renderer support, not on section families. Phase 5 is several separate PRs, each following its relevant earlier workflow, rather than one change coupling assembly insertion to face painting.

Keep parsing/resource I/O in io, durable selected definitions in doc, application and validation commands in engine, renderer-specific interpretation in render/its adapters, and discovery/editing UI in ui-egui. Lower layers do not depend on the UI or platform file dialogs. Reuse container resource deduplication; avoid a second blob store.

Before allocating/decoding, enforce manifest byte/entry/string/dependency limits, total resource bytes, image dimensions/decoded bytes and recipe expansion bounds. Paths must be relative, normalized and confined to the selected pack, including symlink handling. Phase 1 must publish exact limits and test their boundaries. Do not auto-fetch missing files or install executable extensions. Explicit import/export is enough for the first delivery.

Run cargo xtask ci for each implementation arc and check remote PR results. Geometry tests use analytic values; UI and observable CAD changes include actual app evidence. The three issues remain open until their individual workflows pass these gates.

Local material libraries

The local library transports design-owned appearances and physical-material definitions. Portable catalog v2 preserves opaque PBR parameters and validated engineering cards, including SI values, material-local axes, source records and per-property conditions. It reuses the existing commands, file pickers and library contract. The catalog has its own version; no additional design-format change is needed.

Desktop workflow

  1. Apply a named colour/opacity look using Appearance, or define a physical material using Physical Material → New / Edit or the material commands. An appearance does not change physical density.
  2. In Appearance → Local library, choose Export design library…. The snapshot contains explicit body/face/component appearance overrides and design-local physical materials, including their default looks. It does not export imported kernel paint, unassigned built-in catalog entries or an unapplied colour-picker draft.
  3. Open that library in another design, search its appearance presets, select one and choose Apply library appearance. The normal body/face/component targets are reused. OK after this immediate application does not add a second undo step.
  4. To import a physical definition in the same Local library section, enable Physical material definitions, select an entry and review its density/card. Name on import makes a collision explicit: choose another name instead of overwriting existing data. Import material into design copies the definition only. Cancel or OK after copying keeps the import and does not assign either a material or an appearance to any body. Assign the copied material later in Physical Material. Each import is one undo step.
  5. Save the design. Its selected definitions are ordinary copied design data; the source library can be moved or removed without changing the saved result.

Export does not edit the design, its dirty state or history. An existing file is refused unless Replace existing library file is explicitly selected. Export is a snapshot of the design definitions, not an automatic merge with entries in an existing catalog. Editing a copied look/material uses the normal commands and does not edit its source.

Copied look labels are inert data even when equal to a starter.* recipe ID. Reopening Appearance or applying the copied look to another target preserves its color, opacity and PBR response. Library body and face targets use the same world selections as ordinary assignment, including displaced component instances; the outer command maps them into the source component exactly once.

The combined render integration uses main’s merged #1127 asynchronous picker. Model edits and tab changes wait for its answer while drawing/resizing continues. Overwrite consent is captured before opening; export snapshots the design when library.export runs after choosing the destination. Document identity is checked, and an old library answer cannot replace another Appearance editor. Cancellation does not read or write a library. The earlier #1114 candidate is no longer a runtime dependency of the refreshed consumer.

The desktop panel applies appearances and imports physical definitions. library.apply also supports explicit body assignment in the same atomic command; imported definitions appear in the existing Physical Material list and can be edited there. The browser can export a library through the existing download service. Interactive local library opening is a desktop feature in this first slice. Browser engine commands can consume a bounded catalog already supplied through VFS; wasm compilation does not prove an interactive browser importer or download workflow.

Commands

All programmatic calls use JSON and never open a file picker:

{"command":"library.export","params":{"path":"my-library.solidcraft-library.json"}}
{"command":"library.inspect","params":{"path":"my-library.solidcraft-library.json"}}

Inspection/export returns a catalog with qualified entry IDs and exact revisions. Use the returned values when applying; do not replace an unknown revision with a guessed one:

{"command":"library.apply","params":{
  "path":"my-library.solidcraft-library.json",
  "entry":"user.local/appearance-<returned-id>",
  "revision":"<returned-64-character-revision>",
  "targets":{"bodies":["Body1"]}
}}

Appearance targets can be bodies, components or faces: [{body, point: [x,y,z]}]. Points are world-space millimetres. At most 1,024 total targets are accepted. Missing objects and invalid targets fail; the importer does not invent a nearby replacement.

A material entry can be copied without targets or assigned to body targets. The optional name explicitly selects a new physical-material name on import. An identical existing definition is reused; a conflicting definition or built-in name is refused. No density, look or configuration cell is silently overwritten. Creation and assignment share one outer undo transaction through the existing material commands. Mass remains the existing volume × density calculation; a generic illustrative density is not an engineering grade.

library.export also accepts id (default user.local), name (default My library), provenance, overwrite (default false) and format (default solidcraft-library/2). Explicit solidcraft-library/1 refuses PBR or engineering-card loss before publishing, including with overwrite enabled. Only explicit assignments/design-local definitions are inspected, without evaluating geometry. Duplicate exact definitions are deduplicated; different values with the same display name remain distinct entries.

Portable catalog v2

solidcraft-library/2 extends the same envelope and definition kinds with optional pbr: {metallic, roughness, dielectric_ior} on appearance values and optional physical_card on material values. PBR uses the bounded opaque contract and requires opacity exactly one. Cards use the existing SI admission and validation rules. Sources and conditions are copied as supplied data, not certification. Card density never replaces assigned mass density. An independent default PBR look and a physical card may coexist in a material.

Applying an appearance is an exact replacement: v1 scalar values and v2 values without PBR clear the target’s prior PBR. V2 values with PBR retain their parameters. Display labels are inert, including labels equal to built-in preset IDs: importing an owned value never selects a different recipe. Undo/redo and native save/reopen preserve the copy after removing the source file.

Absent richer fields are omitted from serialization. Existing v1 scalar bytes, content revisions and identities remain exact; pinned appearance bytes and a material revision cover the writer contract. Changing only the envelope version leaves entry revisions unchanged. PBR and complete card data participate in revisions when present. V1 wire input rejects even null v2-only fields. Older readers refuse v2.

Scalar catalog v1 compatibility

The independent JSON format marker is solidcraft-library/1. It has id, name and an entries array. Each entry has id, revision, provenance and a typed definition:

  • appearance: value: {name, color: [r,g,b], opacity}; sRGB bytes and finite opacity 0–1.
  • material: value: {name, density_g_cm3, appearance?}; finite density above 0 and at most 1,000 g/cm³, with an optional appearance value of the same scalar shape.

Every object rejects unknown fields. Unsupported kinds (including texture/environment payloads) and versions fail explicitly, rather than dropping fields or pretending that a colour preview is a textured material. Catalog IDs are 1–64 ASCII bytes, beginning with a lowercase letter and continuing with lowercase letters, digits, ., _ or -. An entry ID is <catalog-id>/<local-id> with the same local-ID rules; paths and labels are not IDs.

An entry revision is lowercase SHA-256 of compact v1 serde JSON [provenance, definition]. Derived struct field order, omission of absent optional fields, UTF-8 escaping and f64 round-trip number formatting are part of this writer contract; this is not arbitrary JSON canonicalization. Prefer library.export or the Rust Entry::new/content_revision API when authoring revisions. A future incompatible writer changes the catalog version. The default export IDs contain a kind and 16 hex revision characters; collisions are rejected, never used to overwrite a different definition. Explicitly authored stable local IDs can retain their identity across revisions. Application requires the selected exact revision. Changing another entry does not change the selected definition’s identity.

Provenance contains a required license, optional author and optional source_url. It is supplied metadata, not verified permission or a publisher signature. URLs are never fetched. The default LicenseRef-User-Provided does not claim that user data is openly licensed. No external art/data is bundled in this slice. Catalog provenance/revisions stay in the catalog: values are copied into existing Look/UserMaterial records without a persistent catalog-origin/update receipt. Card source records and property conditions belong to the card and are copied exactly. Resource embedding and explicit update links remain separate work.

Bounds and failure behavior

The reader accepts at most 4 MiB encoded JSON and 1,024 entries. A bounded raw preflight preserves strict v1 wire fields before typed decoding. Entry decoding stops at the count cap with fallible vector growth. Names are trimmed, nonempty and at most 128 UTF-8 bytes; provenance license/author/source URL limits are 128/256/2,048 bytes. Control characters, invalid identifiers, duplicate identities, bad revisions and nonfinite scalar values fail. Export inspects at most 100,000 source references, deduplicating into at most 1,024 entries. Export bounds retained unique definitions before whole-catalog encoding, reserving 4 KiB for the envelope and counting each entry’s pretty bytes plus nested indentation. This conservative admission may refuse a near-limit compact catalog. Physical imports also obey the design’s 1,000-material cap. These are encoded/work limits, not a process-memory or latency guarantee.

Native reads are bounded regular-file reads; browser reads check VFS size before cloning. Native no-clobber writes reuse atomic new-file publication; explicit replacement is atomic without reading/backing up an arbitrarily large old target. All library command paths obey the active MCP/control file scope before reading or publishing, including overwrite. Browser VFS publication is synchronous and reuses the host’s download service. No automatic discovery, asset fetch, relative-resource resolution, executable recipe or plugin is introduced.

Failed application preserves the document and undo/redo history. The source file is read and checked before copying; a stale selected revision needs an explicit reload/reselection. The copied design remains independent after successful assignment, undo/redo and cold reopening. This library has no dependency on the pending render stack and does not change the document format. User-visible evidence and actual native/wasm results are recorded in the PR; geometry/robustness checks remain separate and no reference baseline is weakened.

Scope

The catalog carries the document’s scalar/opaque Look and engineering-card values. It does not transport GPU2 runtime overrides, refractive glass, clearcoat, texture resources, mappings, image files or environment maps. The six starter glass recipes remain recipes; v2 does not turn an IOR or alpha preview into refraction. Unknown resource kinds and fields fail explicitly. A runtime GPU-only look must not be presented as captured by this design-definition export.

The command engine

Every user-visible action is a command: a CommandSpec registered in crates/engine/src/cmd/, with a dotted, lower-case id (solid.extrude, sketch.create, timeline.rollback). The toolbar, dialogs, marking menu, S box, scripts, the CLI, the control channel and MCP all run commands through Session::execute(id, params). Commands take JSON parameters and never open dialogs; a dialog is a front-end form that builds the parameters.

#![allow(unused)]
fn main() {
CommandSpec::new("solid.fillet", "Fillet", fillet)
    .at("SOLID", "MODIFY")      // toolbar tab and panel ("" = not on the toolbar)
    .icon("fillet")             // an SVG master in crates/look/icons/design/svg
    .key("F")                   // default shortcut
    .params("edges: [[x,y,z] | {body, index}]; radius: expr; …"),
}

run is fn(&mut Session, &Value) -> Result<Value>, and enabled is fn(&Session) -> Result<(), String>; the error string is the disabled-button tooltip. The params string is the one-line documentation shown by solidcraft-cli commands, MCP’s list_commands and the command reference.

A command that changes the design is undoable by default: the session snapshots the document before it runs, and a failing command leaves the document unchanged. Feature commands add a feature to the timeline and let the document evaluate it; they validate their inputs first (units, ranges, references) and return BadParams instead of building a broken feature.

Only the registry’s own dotted ids name commands (matched ignoring case); any other id fails with unknown command, which names up to three close ids: a typo’s (solid.extrud), or one ending in the same word (file.open: did you mean doc.open?).

Adding a command

  1. Add a CommandSpec to the right file in crates/engine/src/cmd/ and implement run.
  2. Give it input and output schemas: a parameters type (its contract) and a reply type in the module for its command file under crates/engine/src/schema/ (schema/features.rs for cmd/features.rs, and so on; create it for the file’s first contract), built from the shared field types in schema/types.rs. Register them with checked!(…), .contract(…) and .output(…), as solid.extrude does, and add the command to that module’s schemas_and_the_key_check_agree test, with error-message cases and a reply test. The parameters type documents each parameter (keep the .params(…) prose too, for now), and describe_command serves both schemas to agents and scripts. A test (every_command_has_input_and_output_schemas) warns for a command without them, and will fail it once the rule becomes required; the commands that predate the rule are listed in schema/unconverted.txt, which only shrinks. Command schemas walks through it, with the shared types and a checklist.
  3. Write engine tests that measure the result: volume, area, and face/edge/vertex counts against values you can derive by hand.
  4. The hostile-input test (hostile_params_never_panic in crates/engine/src/tests.rs) runs your command automatically with every documented parameter set to junk. It must return errors, never panic.
  5. For UI work, add a scenario (see Test harnesses).
  6. A command the ribbon shows (through .at(…) or a section in crates/ui-egui/src/workspace.rs) ships with its help tooltip: two short sentences, what the tool does and then when to use it, in your own words. Add the message help.<id> to a new file in assets/locales/pending/ (English, every catalog in COMPLETE in crates/ui-egui/src/i18n.rs, which a test requires, and any other language you can write) and the id to DOCUMENTED in crates/ui-egui/src/command_help.rs. The test every_toolbar_command_has_help fails for a ribbon command without help (#2758).

Command schemas

Every command has an input schema and an output schema (JSON Schema 2020-12), generated by schemars from two Rust types:

  • a contract: the parameters type, checked in front of the handler (see crates/engine/src/schema/mod.rs); with the session’s check level at warn or strict, a misspelt key or a wrong-typed value is reported or refused instead of silently ignored;
  • a reply type: what a successful call returns.

engine.describe and the MCP tool describe_command serve both, so agents and scripts read a command’s exact parameters instead of guessing from prose.

When the check fails

The tests in crates/engine/src/schema/coverage_tests.rs say what to do. A command without schemas is only a warning for now (the test prints the list); it will become required once the PR backlog is down, so give new commands both schemas now. Set SOLIDCRAFT_SCHEMA_RULE_STRICT=1 to make the missing-schema check fail today.

  • “x.y has no input or output schema” (or “no input schema”, “no output schema”): a new command. Give it what it lacks, as below. Don’t add it to schema/unconverted.txt: that list holds only the commands that predate the rule.
  • “x.y has its schemas now: remove its line from unconverted.txt”: you converted an old command. Delete its line.
  • “x.y is a planned! placeholder, which needs no line in unconverted.txt”: delete its line.
  • “unconverted.txt names commands that don’t exist”: a command was removed or renamed. Delete its line.
  • “unconverted.txt must be sorted, each id once”: keep the list in byte order (LC_ALL=C sort).
  • “x.y ran with an unknown key in strict” or “x.y should refuse with …”: it has a contract, but its run isn’t crate::checked!("x.y", <its handler>) (step 5): the wrapper is missing or names another command’s id.

Writing a contract

1. List the keys the handler reads

The contract’s keys are exactly the keys the handler reads, no more and no fewer. Read the handler and everything it passes p to:

  • direct reads: p.get("k"), params::expr(p, "k"), req_expr, str_, num, bool_, vec3, string_list and the like;
  • features::add_feature (every feature command) reads name, body_name, body_names and participants: take them with FeatureCommon. What participants changes depends on the feature, and for some it changes nothing in the result: check where the feature is evaluated (crates/doc/src/eval.rs). participants takes its description from FeatureCommon, shared by every feature, so say what it does for this one elsewhere: where it and another key override each other (a primitive’s targets gives way to it; an extrude’s targets overrides it), in that key’s description; where it has no effect, in the summary or in the description of the key it might seem to change (bounding solid’s bodies). A feature that makes no body (scale) declares name and participants itself instead, as body_name and body_names would do nothing; a construction axis, point or plane flattens ConstructionCommon or PlaneCommon, which hold just those two. It keeps participants because timeline.redefine passes a feature’s own back, though for a scale it changes nothing either; its own description says so. For the primitives (box, cylinder, sphere, torus, cone) add_feature also reads targets; a new primitive must join the primitive match in add_feature_in, and its contract declares targets. The commands that combine bodies read operation (and usually targets) in their own handlers;
  • keys read around the command: frames::to_local reads body (and face_body) for commands that take picks;
  • live, for a command with .live(…) (today sketch.drag and sketch.edit_text): a call with live: true goes to the live runner, which checked! doesn’t wrap, so live calls aren’t checked yet. sketch.drag and sketch.edit_text have their contracts (with live declared as an optional bool) and their live calls aren’t checked. A new command that needs a live runner gets its contract the same way; say in its pull request that its live calls aren’t checked.

A key the handler reads only in some situations (thin-wall keys with thin: true) is still a field; its description says when it applies. Contracts don’t check combinations of keys (sketch with face); the handler does.

2. Put it in the module for its command file

Contracts for cmd/features.rs live in schema/features.rs, for cmd/inspect.rs in schema/inspect.rs, and so on, each with its tests in a file next to it (schema/features_tests.rs). For a command file’s first contract, create the module and add its pub mod line, sorted, under “The contracts, by command file” in schema/mod.rs. A new module starts like this (keep the imports it uses):

#![allow(unused)]
fn main() {
//! Contracts and result types for the commands in `cmd/wedge.rs`.

use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use super::types::{FeatureCommon, LengthExpr, OperationParam, PointExpr3, StringList, feature_common, present};

// …the parameters and reply types…

#[cfg(test)]
#[path = "wedge_tests.rs"]
mod tests;
}

3. Write the parameters type

#![allow(unused)]
fn main() {
/// Add a wedge primitive.
#[derive(Debug, Deserialize, JsonSchema)]
#[schemars(deny_unknown_fields)]
pub struct WedgeParams {
    /// Size along X.
    pub length: LengthExpr,
    /// Size along Y.
    pub width: LengthExpr,
    /// Height at the thick end, along Z.
    pub height: LengthExpr,
    /// The wedge's minimum corner. Default: the origin.
    #[serde(default, deserialize_with = "present", skip_serializing_if = "Option::is_none")]
    #[schemars(with = "PointExpr3")]
    pub corner: Option<PointExpr3>,
    /// How the wedge combines with existing bodies. Default: new.
    #[serde(default)]
    pub operation: Option<OperationParam>,
    /// The bodies a join, cut or intersect works on. Default: every body in the feature's
    /// component, not its sub-components (an intersect removes those the wedge doesn't meet).
    /// Ignored when `participants` lists bodies.
    #[serde(default)]
    pub targets: Option<StringList>,
    #[serde(flatten, deserialize_with = "feature_common")]
    pub common: FeatureCommon,
}
}
  • Descriptions are the documentation. The struct’s doc comment is the command’s summary and each field’s doc comment is its description in the schema, so put nothing else on those lines. Say the unit or form, the default (Default: …) and when the key is ignored. Write for someone who has only the schema. Check what each value does where the feature is evaluated (crates/doc/src/eval.rs and the kernel), not only how the handler reads it: a limit the evaluation enforces (a torus’s minor must be less than its major) or a key it ignores belongs in the description. A scale’s factor, for one, has no effect when factors is given, though it must still evaluate.
  • Required or optional follows the handler: a key it can’t do without is a plain field; any other is #[serde(default)] Option<T>.
  • null: the common readers (str_, num, bool_, expr, string_list) read null as absent, which a plain #[serde(default)] Option<T> matches. Readers that parse p.get(k) themselves (features::feature_sketch, features::profiles, the pick lists) refuse null; for those keys use #[serde(default, deserialize_with = "present", skip_serializing_if = "Option::is_none")] with #[schemars(with = "T")]. The key may then be absent but not null, the schema has no null branch, and skip_serializing_if stops schemars publishing default: null, which the schema itself would refuse.
  • Use a shared type for every value it fits (below) rather than String or f64: shared types accept exactly what the handlers’ readers accept, give the handlers’ messages, and publish the matching schema.

4. Write the reply type

A feature command whose handler returns add_feature’s reply unchanged returns FeatureResult (in schema/features.rs): use it, don’t copy it. A command that returns anything else, including a feature command that reshapes the reply (solid.thread returns {feature, name, thread}), gets its own reply type:

#![allow(unused)]
fn main() {
/// What `wedge.measure` returns.
#[derive(Debug, Serialize, JsonSchema)]
pub struct WedgeMeasureResult {
    /// The wedge's volume, in mm³.
    pub volume: f64,
    /// In the warn parameter-check mode: what the call's parameters got wrong.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub param_warnings: Vec<String>,
}
}
  • Build the handler’s reply from the type (serde_json::to_value(reply)), as add_feature_in builds FeatureResult, rather than a json! literal next to it. Then the schema can’t drift from what the command returns.
  • Warn mode adds param_warnings to a successful reply that is a JSON object, so an object reply type keeps that field. A reply that isn’t an object (a list) is returned unchanged and has no such field.

5. Register them

In the command file, next to its COMMANDS:

#![allow(unused)]
fn main() {
use crate::schema::features::FeatureResult;
use crate::schema::wedge::WedgeParams;
use crate::schema::{TypedContract, output_schema_for};

const WEDGE_CONTRACT: TypedContract<WedgeParams> = TypedContract::new();

// in COMMANDS:
CommandSpec::new("solid.wedge", "Wedge", crate::checked!("solid.wedge", prim_wedge))
    .at("SOLID", "CREATE")
    .icon("wedge")
    .contract(&WEDGE_CONTRACT)
    .output(output_schema_for::<FeatureResult>)
    .params("length, width, height: expr; corner?: [x,y,z] …"),
}

.contract and .output publish the schemas, and checked! checks each call against the contract that its id publishes, in front of the handler, so what is checked is always what is published. The handler itself doesn’t change: it still reads the raw parameters. Keep the .params(…) prose: the command list, the palette and describe_command’s params still show it, until it is generated from the schemas.

6. Test it

In the module’s tests file:

  • schemas_and_the_key_check_agree: add assert_keys(input_schema_for::<WedgeParams>(), &[…]) with the keys, sorted, FeatureCommon’s included. The keys then change only on purpose.
  • strict_keeps_todays_errors: for calls the handler already refuses (a malformed value, an unknown spelling), add cases to assert_strict_keeps_todays_errors: strict must give the same message as the handler. Leave out the cases its documentation lists as different on purpose, such as a missing required key (strict says `length` is required, the handler something else).
  • A reply test: run the command and check the reply matches its output schema with conforms (replies_match_the_output_schema in schema/features_tests.rs shows how; its misspelt-key call checks that a reply carrying warnings still matches). Run every form the command takes (each alternative key, such as corner or center, each operation, one name and a list of names) in Warn, and check those replies have no param_warnings: Warn runs a call the contract refuses anyway, so a matching reply doesn’t show the contract accepts the call. A reply that isn’t an object never carries param_warnings; run those calls in Strict, where a refused call is an error. In the same file, converted_primitives_and_body_commands_reply_with_feature_result shows the Warn and no-param_warnings checks across several commands.

schema_defaults_are_valid checks the default of each top-level parameter on its own; a nested type’s defaults are yours to check.

7. Converting a command that already exists

Do steps 1 to 6, remove its line from schema/unconverted.txt, then check the contract against every caller the tests have: run the whole suite with the audit recorder and summarise it.

mkdir -p audit
SOLIDCRAFT_PARAM_AUDIT=$PWD/audit cargo test --workspace --no-fail-fast --features solidcraft-engine/param-audit
python3 scripts/param_audit.py audit

The recorder doesn’t create the directory, and it writes nothing if it can’t, so check the summary’s first line says at least one file. Each line under “SUCCEEDS TODAY, STRICT WOULD REFUSE” is a call that works today and that strict would refuse: a form the contract is missing (fix the contract) or a caller’s mistake, such as a misspelt key (fix the caller). Put the summary in the pull request.

Shared types

In crates/engine/src/schema/types.rs. Each accepts exactly what its reader accepts, most by calling that reader.

TypeForMatches
LengthExpr, AngleExpra number or an expression ("w / 2", "30 deg")params::expr
NumberExpra number or an expression with no unit (a ratio, a factor)params::expr
Point3, Point2[x, y, z] / [x, y] or {x, y, z} of numbers, in mmparams::vec3, params::vec2
PointExpr3a placement point whose coordinates may be expressionsfeatures::point_param
IdOrNamea feature by id or name (extrude’s sketch)features::feature_sketch
SketchRefa sketch by id or name, with features::sketch_id’s messagefeatures::sketch_id
StringListone name or a list of namesparams::string_list
OperationParamnew, join, cut, intersect and their spellingsOperation::parse
FaceRefa face by a point on it or its nameface::face_ref
FaceRefsthe faces list of shell, draft, offset face, split face and face.delete (not solid.replace_face, whose messages differ)features::face_points
EdgeRef, EdgeRefsan edge by a point on it or {body, index}; fillet’s and chamfer’s edgesfeatures::edge_points
ProfilesParamwhich sketch profilesfeatures::profiles
ScaleFactorsscale’s factors: three NumberExprs (strict words a bad item differently; see its doc)features::scale
AxisDirX, Y or Z, or a non-zero [x, y, z] / {x, y, z} (cylinder’s, cone’s and coil’s axis)features::axis_dir_form
FeatureCommonname, body_name, body_names, participants, flattenedfeatures::add_feature
ConstructionCommon, PlaneCommonname and participants of a construction axis or point, or of a construction plane (whose name is a PlaneName), flattenedfeatures::add_feature
PlaneNamea construction plane’s name, which may not be an origin plane’s (strict names the command called; see its doc)features::check_plane_name
PlaneParama plane: an origin or construction plane’s name, a construction plane’s id, or {origin?, x_dir, y_dir} / {origin?, normal}features::plane_param
PlaneOrFacea plane, or a planar face of a body {face, body?, index?, path?, fixed?} (offset and at-angle planes’ base)features::plane_param, sketch::face::face_pick_form
GeoRefParamgeometry a construction feature is built from: a picked face, edge or vertex, a sketch point or curve, a point, a plane, an axis, a construction pointconstruct::geo_form
AxisLinean at-angle plane’s axis: X, Y, Z, a construction axis, {origin?, dir} or a model edge or facefeatures::axis_line_object, construct::geo_form

When no shared type fits

Add one to types.rs, next to the reader it mirrors:

  • Its Deserialize calls the handler’s own reader, so “the contract accepts it” means “the handler accepts it”. Don’t re-implement the parsing. Many readers are private to their command file or need the design (features::point_param takes the Session). Split out the part that only looks at the JSON as a pub(crate) function, have the handler call it, and re-export it from cmd/mod.rs, as face_ref_form and edge_ref_form are; whether the value finds something in the design stays in the handler.
  • On failure it gives the handler’s message. Wrap a message that is already a whole sentence, such as the handler’s unknown operation `x` (…), in whole(…), and the check passes it on as it is. Otherwise start it with “must”: the check names the key, so “must be a point [x, y, z] of numbers” becomes "`corner` must be a point [x, y, z] of numbers". (Any other message comes out as "`corner`: …".) Where the handler puts serde’s own message after the key ("`point`: missing field `p`"), wrap it in keyed(…): the check puts the key and a colon in front and leaves the rest as it is, instead of rewording “missing field” and “invalid type” as it does for the type’s own fields.
  • Its JsonSchema describes the same values, with a description. Where JSON Schema can’t say something the reader checks (ignoring case, a byte length), say it in the description. A pattern that allows surrounding whitespace must allow exactly what Rust’s trim removes: build it from WHITESPACE, as not_blank_pattern and AxisDir do, rather than writing a regex’s whitespace class by hand (it differs, and the escapes are easy to get wrong). conforms doesn’t read patterns, so pin the pattern’s text in a test.
  • Test both: values the reader accepts and refuses are accepted and refused by the type, and they match the schema (the conforms helper in schema/tests.rs; see face_and_edge_refs_match_the_pick_readers).
  • A handler that reads a key more leniently than any type can (a wrong-typed value read as if the key were absent) gets the stricter type: that difference is what warn and strict report.
  • A reader that tries forms in order, where some forms, once chosen, refuse the value rather than letting a later form take it (construct::geo: {sketch, point: [1, 2, 3]} is a sketch reference without a point, never a fixed point), needs a schema that keeps that order: give each later form a not naming the keys of the earlier forms that refuse, as GeoRefParam does, and test values that two forms would read differently.

Checklist

  1. The contract’s keys are exactly the handler’s reads, including add_feature’s (less body_name and body_names for a feature that makes no body), the frame mapping’s and live.
  2. The struct and every field have descriptions, with defaults and when a key is ignored.
  3. Shared types wherever they fit; a new type calls the handler’s reader and gives its message.
  4. FeatureResult for a command that returns add_feature’s reply unchanged, else a reply type the handler builds its reply from.
  5. checked!, .contract and .output on the CommandSpec, and the .params prose kept.
  6. assert_keys, error-message cases, a reply test.
  7. For an existing command: its unconverted.txt line removed and the audit summary in the PR.

The kernel boundary

crates/kernel is SolidCraft’s B-rep kernel boundary. The modelling kernel behind it is solidcore, the vendored Rust port of NVIDIA’s SMLib (solidcore/): crates/smlib wraps it in a safe API, and crates/kernel turns that into the operations the app uses. Callers get SolidCraft types (Body, face and edge indices, meshes, measures) and a KernelError instead of kernel types and panics.

guard

The vendored kernel can panic on geometry it does not handle. Every call into it goes through solidcraft_kernel::guard, which catches the panic and turns it into KernelError::Internal, leaving the model unchanged:

#![allow(unused)]
fn main() {
pub fn guard<T>(what: &str, f: impl FnOnce() -> Result<T>) -> Result<T>
}

A failing feature shows an error on the timeline; it never takes the app down.

What is our own

The B-rep operations are solidcore’s. SolidCraft’s own code in crates/kernel is the boundary around it: the public API, display meshes, measures, persistent names and STEP exchange. A failing kernel operation stays failing until the kernel is fixed; there is no workaround on SolidCraft’s side (tests/kernel-cases/README.md).

The vendored kernel

solidcore is vendored in the solidcore/ directory with git subtree and is built from there as the path dependencies solidcore and solidcore-record. Develop it there, in a normal commit; .github/workflows/solidcore-mirror.yml mirrors changes under it back to tangency-labs/solidcore. solidcore/ and vendor/ are excluded from our fmt and clippy rules because they are not our code.

Persistent naming

A fillet stores the edges it rounds, a sketch the face it sits on, a shell the faces it opens. If those were face and edge indices, an upstream edit that adds a face would move the fillet to another edge. SolidCraft stores names, and finds them again every time the timeline evaluates.

Why names follow the kernel’s history

Names used to be worked out from geometry: sample points of each new face were looked up on the faces of the feature’s input, and the majority won; pieces of a split face were numbered by where their centres lay. That guess is right most of the time, and wrong exactly when it matters: after an upstream edit that splits, merges, mirrors or shrinks a face, points can land on a neighbour, centres can swap order, and a fillet or a sketch quietly re-attaches to a different face. Nothing tells the user, which is the worst thing a CAD tool can do.

The kernel now reports, for every operation, what each face and edge of its result came from (crates/kernel/src/history.rs: a face’s origins and how it changed, an edge’s origin edge or the faces whose meeting made it). Names follow that history, so they are exact and deterministic, and when a reference can no longer be found exactly the feature says so instead of guessing.

How a body’s faces are named

crates/doc/src/lineage.rs composes the histories of the kernel operations a feature runs (a tool built from a sketch, cut from each target, the cut body split into lumps) into one lineage over operands that can be named:

OperandIts faces are named
a body of the model before the featureas they were
a moved copy of one (pattern, mirror)<name>@F<feature>.<instance>
the cavity side of a shell grown outwardF<feature>:inner(<name>)
a body the feature builtby role: F<feature>:side:<sketch curve>, start/top/bottom/end caps, +x… box sides, face:<k> imported faces, link:<digest> faces of a linked design (see below)
a copied tool of a patterned featureF<feature>:<role>@F<pattern>.<instance>

crates/doc/src/history_names.rs names each face of the result from its origins: a kept or trimmed face keeps its origin’s name, a fillet or chamfer face is F<feature>:blend(<edge>) of the edge it replaces, a shell’s inner wall F<feature>:inner(<face>). A side’s sketch curve is the curve the profile segment lies on exactly; nothing is sampled.

  • Pieces. Faces that end up with one name are pieces of one face, and are told apart by the faces around them that the others lack: F2:top#[F2:side:l4,F4:side:l4] is the piece of the block’s top bounded by its left wall and the slot’s left wall. That survives the slot moving, growing or shrinking. Pieces nothing around them tells apart are numbered (#1, #2): by what they came from when that differs (the two halves of a circle’s wall), else in kernel order and then unverified.
  • Merged faces. A face made of several (a join flush with the body) takes the name of the face they are all pieces of, else the target’s; it answers to the others’ names too (aliases).
  • Edges are named by the faces they separate, <face A>|<face B>, ~k when several edges run between the same two faces (numbered by where they lie; a reference by such a number is reported when only geometry confirms it).
  • Lumps. When a cut leaves a body in pieces, the whole is named first, so pieces of one face in different bodies are told apart, and the bodies (Bar, Bar (1)) are assigned in the order of their least face name, not the kernel’s shell order (which an upstream edit can swap; see Older designs).

Names are worked out lazily, on first request, and shared by a body’s clones.

Linked designs

A linked design (Insert Design) comes in as STEP text, which keeps no names: its faces would be named by their place in the file (face:<k>), and adding a hole to the part can give that place to another face. So when the link is made or updated, each face is matched to the face of the part it was written from (the same geometry, so a point well inside one lies on the other) and the Import feature keeps that face’s name in the part (Feature::link_faces). The face is named F<import>:link:<digest>, a digest of that name (which may hold anything: the part’s feature numbers and @, | mean something else in the assembly), and a piece keeps its qualifier with the faces around it named the same way. A joint or other reference on a linked face therefore stays on it across Update Links exactly as it would inside the part, and says so when the part no longer has the face. Every linked face still answers to face:<k>, so references made before keep finding their face by place; Update Links warns about joints that do.

Where there is no history

A body made by an operation that does not report history yet (some kernel paths return none; see the kernel’s handoff lists, and the doc features that do not compose it yet: sheet metal, plastic and surface features, emboss, rib, replace face, the faceted thread cut), or a face whose role is not known exactly (a loft’s sides, a coil’s or pipe’s generated profile, a hole’s tool), is named the old way, from geometry, and its names are marked unverified (model.faces reports exact: false). A reference that resolves through an unverified name while its stored point no longer lies on the face says so: face ... was found by geometry, not exact history; check it.

How a reference finds its face again

crates/doc/src/name_lookup.rs, in order:

  1. the face with that name;
  2. the one face answering to it: merged into it (the feature says it was merged), or the named piece, which the faces listed in its name still bound;
  3. the pieces of it (it was split, or it is on several bodies): the one nearest the reference’s point, and the feature says it was split;
  4. nothing: the feature falls back to the geometry it recorded and says the face no longer exists.

An edge name is found as the edge between the two faces it names, each found as above: when exactly one edge runs between them it is that edge, even after a split of one of its faces.

A sketch’s link to a projected edge (sketch.project {edge: [x, y, z]}, and Intersect and Include 3D Geometry of an edge) keeps the edge’s name beside its point. The link is that edge wherever a change takes it; of an edge that was split it is the piece nearest the link’s point, and the sketch says so. When no edge has the name any more the link is lost and keeps its last geometry, as it does for a face: it does not take whichever edge is nearest now. A link saved without a name, by a build that kept only the point, is found by the edge nearest its point, as it always was, and takes that edge’s name when its sketch is next edited.

A command that takes a face as a point can be given a name model.faces lists instead (face.delete, solid.shell, solid.draft, solid.offset_face, solid.split_face, the faces of solid.replace_face, the face of sheet.convert). It is found with the same lookup (naming::locate_one: the face with that name, or the one face answering to it, on the given body or any body; a name that finds several faces is refused). The command takes a point on that face (naming::named_face_point: the incentre of the face’s roomiest triangle, moved onto the surface of a plane, cylinder, cone, sphere or torus), which the feature stores like a picked one, with the name it then gets as before. Nothing new is stored in the design. model.faces lists that point for every face.

Profiles picked by their curves

A profile picked by a point or an index is stored by the curve ids of its outer loop (ProfileSel::Curves), so it follows dimension and parameter edits exactly. Editing the outline itself can leave no profile bounded by exactly those curves; the feature then falls back to the best match and warns, instead of failing and taking its body and everything after it along (profile_refs::fallback_loop):

  1. all the stored curves are still there and still close a loop on their own, but another curve now cuts the region: the pieces inside that loop that touch it, which together are the region that was picked (islands inside stay holes);
  2. otherwise the profile whose outer loop shares the most of the stored curves, at least half of them (a side deleted and drawn again, a notch trimmed in); a tie goes to the one with the fewest curves the loop did not have, and a tie left after that is an error, as is a loop that matches nothing.

A line, arc or circle listed twice (the pieces a circle was cut into when the pick was saved) is one curve to a pick, so a line that ends on a circle and gives it more arcs changes nothing; the outputs of an analytic offset take step 1 only, never step 2 (#9368). Loops of free-form offset curves, free curves and legacy graph-span multisets keep their exact meaning. Commands still check a pick with the exact rule, so a curve list that names no profile is refused when given. A sketch on such an extrude’s cap falls back the same way: its certificate names the loop, and the cap of the same solid extrude at the same end, from the same sketch and on the same body, that shares the most (at least half) of its curves stands for it, unless the exact cap is still on another body (Document::anchored_face_indices).

A hole placed by a position on a face is the exception to “the point nearest the reference’s point”: its position is a place, not a pick. The hole follows its face along its own axis, so it moves with the face when the face moves, and when the face no longer reaches the position it stays where it was placed and the feature says so, instead of sliding to the face’s edge and drilling half a hole there. (Holes at sketch points follow their sketch.)

Older designs

Designs saved before format 14 stored names worked out from geometry, and a few of them name a face that history names differently (a tube’s outer wall was named after the sketch curve nearest its centre, its inner circle). Migrating such a design marks those names (?F2:top#2), and they are found among the names geometry gives, as the build that saved them found them: an older design evaluates as it was saved. Picking the face again stores its exact name.

Their features also keep naming the pieces they split a body into in the kernel’s shell order (Feature::shell_piece_order, set by the migration): the body names stored after them (fillet and chamfer bodies, targets, participants, materials, appearances, joints) mean the pieces in that order. Features added later name their pieces after their faces.

Not done yet

  • Certificates (FaceSource, the strict attachment identities behind sketches on faces and configured face styles) still travel only where the kernel’s planar-source mapping keeps them; carrying them through History::face_sources would let a certified attachment follow a face a later join reshapes, which today is refused on purpose.
  • Operations without history, listed above, keep the unverified geometric names.

Test harnesses

cargo xtask ci runs everything a pull request must pass: fmt, xtask assets, xtask licences --check, xtask layers, clippy with -D warnings, the tests with the ci profile and the wasm build check.

HarnessWhereWhat it catches
Unit and engine testsnext to the code, *_tests.rsmeasured results: volume, area, topology counts against derived values
Hostile inputhostile_params_never_panic, crates/engine/src/tests.rsevery command × every documented parameter × junk values: errors, never panics
Sketch fuzzcrates/engine/src/cmd/sketch_fuzz_tests.rsrandom sketches through the solver and profile finder
UI scenarioscrates/ui-egui/tests/scenarios/*.jsonthe real app, headless: clicks, keys, drags, assertions
Layout testscrates/ui-egui/tests/ui_layout.rs, window_widths.rspanels and dialogs at every window width
Design-file corpusapps/solidcraft-cli/tests/corpusevery saved file version still opens and evaluates the same
Kill mid-saveapps/solidcraft-cli/tests/kill_mid_save.rsa save killed at any moment leaves a readable file
Reference testscargo xtask oracle → docs/oracle.mdparts rebuilt from tests/oracle/*/recipe.json, compared with analytic or FreeCAD expected values

Dependency advisories

Scheduled and manually dispatched CI runs have a separate Dependency advisories job that checks advisories, bans and sources. A pull request or merge group that changes Cargo.lock or any Cargo.toml runs the same job for advisories only, and ci requires it then; other pull requests skip it, so a new advisory does not fail an unrelated change. To run the nightly scan locally:

cargo install cargo-deny --version 0.20.2 --locked
cargo deny --locked check --warn unmaintained advisories bans sources

The checked-in deny.toml rejects vulnerabilities and unknown registries/git sources. Unmaintained crates, yanked versions and duplicate versions warn; licences remain covered by cargo xtask licences --check. Review advisory failures and update dependencies rather than adding broad ignores. The first scan prompted an update to quick-xml 0.41.0 for RUSTSEC-2026-0194 and RUSTSEC-2026-0195.

Coverage excludes the version matching of local path patches: egui-wgpu and winit in vendor/, and the vendored kernel in solidcore/. Their checked-in code and patches still need comparison with their permissively licensed upstreams; a green scan does not establish their security or incorporate an upstream fix automatically.

UI scenarios

A scenario is a JSON list of steps run against the real app with a headless egui context:

[
  {"start": "sketch.create"},
  {"click": {"plane": "XY"}},
  {"start": "sketch.rectangle.two_point"},
  {"click": {"sketch": [0, 0]}},
  {"click": {"sketch": [40, 30]}},
  {"expect": {"errors": 0}}
]

A new file in crates/ui-egui/tests/scenarios/ is picked up automatically. To record a bug before it’s fixed, add a {"pending": "why"} step; its test is then ignored with that reason, and the fix removes the step.

CI profile

CI runs the tests with --profile ci: light optimisation, line tables, no LTO and debug assertions off, which builds much faster than --release. If a test fails only in a plain debug build, say so in the PR.

Run cargo xtask test to build with this profile and run the test binaries concurrently, with doc tests alongside. Arguments after -- go to each test binary, for example cargo xtask test -- shell. To run the test binaries sequentially instead, use cargo test --workspace --profile ci. On a loaded machine set SOLIDCRAFT_TEST_JOBS=N to bound how many test binaries and side commands run at once; each binary then gets RUST_TEST_THREADS and ACTION_MATRIX_JOBS of max(1, cpus / N) so the binaries in flight share the machine instead of each defaulting to one worker per CPU. Setting RUST_TEST_THREADS or ACTION_MATRIX_JOBS yourself overrides that split, and leaving SOLIDCRAFT_TEST_JOBS unset keeps the original all-at-once behaviour.

Test-level scheduling for full local CI

Install cargo-nextest, then set SOLIDCRAFT_TEST_RUNNER=nextest when running cargo xtask ci. For example, on PowerShell:

$env:SOLIDCRAFT_TEST_RUNNER = 'nextest'
cargo xtask ci

This optional backend builds the workspace tests once, then schedules individual tests using the local-ci nextest profile, which inherits ci. The action matrix runs first, then the longest geometry cases start before the other costly tests, so the ordinary queue does not stall behind an all-slot reservation. It ignores the default filter, so the heavy tests below still run, does not stop after a test failure, and does not retry failures. Tests marked #[ignore] remain ignored, as with the ordinary full local CI. The default concurrency is at most four tests; NEXTEST_TEST_THREADS overrides it. The action matrix’s child workers default to the smaller of that budget and the host CPU count; ACTION_MATRIX_JOBS explicitly overrides this worker count. The local profile reserves CPU slots for the matrix and keeps its scheduling changes separate from GitHub CI. On Windows, the solidcraft-gpu and app packages, plus UI offscreen/GPU tests, use a serial group. The Wasm check and doc tests still run alongside, and the web-offline gate still follows a successful native/Wasm/doc run.

The backend keeps Cargo and binary metadata under target/xtask-nextest/ and reuses it for the native run, freeing Cargo to run the side gates. Nextest prints each test’s result and duration as it finishes. SOLIDCRAFT_TEST_RUNNER=libtest (or leaving it unset) retains the original runner; cargo xtask test and its Cargo/libtest argument forwarding are unchanged. Unknown runner names fail explicitly.

CI sharding and heavy tests

On GitHub, one job builds the tests into a cargo-nextest archive and four jobs run a quarter of the tests each (two halves on a pull request; --partition slice:N/4, settings in .config/nextest.toml), the slowest tests first; doc tests run in their own job. Pull requests skip a few heavy tests (each takes one to twelve minutes); they run on every push to main, nightly, and in cargo xtask test:

  • solidcraft-engine: tests::hostile_params_never_panic, everything in tests::robustness, pattern_suppress_tests::enclosure_arc_extinction_keeps_a_real_sharp_inner_lip_after_resize_and_reopen, pattern_suppress_tests::enclosure_datums_follow_wall_height_and_board_depth_with_material_proofs, cmd::plastic::tests::enclosure_snap_fit_follows_resized_front_wall, the atomic_commands contract tests
  • solidcraft-kernel: tests::boolean_identity_on_tangent_primitives, tspline::tests::hostile_tsplines_never_panic, and the boolean_props property tests (these run only in the nightly “Property tests” job: their inputs are random, so main skips them too)
  • solidcraft-ui-egui scenarios: acceptance_sensor_enclosure

The action matrix (apps/solidcraft-cli/tests/action_matrix.rs) tries every ordinary action (sketch, offset plane, press pull, hole, draft, shell, fillet, chamfer, construction geometry, move, mirror, pattern) on every face, edge, vertex and body of a corpus of designs and compares with a checked-in baseline: an action that passed there and fails now fails the test. Every run checks a fixed sample; the nightly “Action matrix” job runs it all. After a fix, record what passes now with cargo xtask action-matrix --bless. The results and the causes of the failures are in the action matrix page.

To run exactly what a pull request runs: cargo nextest run --cargo-profile ci --profile ci; add --ignore-default-filter for the full set. A new test that takes over a minute on CI can join the list (the default-filter in .config/nextest.toml) if it guards robustness rather than everyday behaviour.

Control protocol

The universal panel foundation supplies typed layout actions and retained runtime guards. The application-layout commands below are registered engine commands reached through engine.execute, engine.batch, engine.script and MCP’s existing execute/batch tools. They require an injected application backend; enabling the internal panel runtime remains an explicit integration step. Current product defaults and the production shell are unchanged.

Application panel and layout commands

Every command has input and output schemas through ordinary command discovery. Inputs are strict at every parameter-check level: unknown fields, including nested placement fields, are errors. As with other engine calls, omitted/null parameters mean an empty object. IDs use the foundation’s bounded lowercase ID domains; dimensions are finite positive outer logical units, at most 32768. Requests are bounded to 16384 encoded bytes before cloning or deserialising. No command opens a dialog, edits the document, changes dirty/undo/revision, evaluates or tessellates a model, fits a body, follows a camera request or selects a ribbon/tool/document tab. Layout revision is independent of document revision.

Canonical existing targets are typed: {"target":{"type":"instance","id":"log"}}. section, workspace and native_host are the other foundation target types; each command accepts only the domains listed below. A legacy id may replace target, but both together are rejected. It first resolves an exact instance ID. A kind ID resolves only when its registration is single-instance and exactly one canonical instance exists, including retained closed state. Multi-instance kinds are rejected even when only one instance currently exists. No first-match guessing or implicit instance creation occurs. panel.open instead requires separate kind and instance fields.

All mutations accept optional expected_revision:u64. A stale revision produces the foundation revision rule and leaves the command’s layout and catalogue unchanged. Successful mutations return the committed LayoutReply (layout, revision, interaction, effects, capabilities and availability), never an echoed placement request. layout includes all retained instances with requested_open, ordered sections and their active tabs. Missing availability entries mean available. Query replies contain no new host effects.

CommandParameters and behaviour
panel.list{} → {state:LayoutReply,kinds:[PanelSpec]} from the live sink and registry.
panel.open{kind,instance,destination?,expected_revision?}. New instances require a destination; closed instances can reuse remembered placement.
panel.close{target:instance,expected_revision?}. Explicit guarded close retains placement and can refuse cancellation.
panel.move{target:instance,destination,activate?:false,expected_revision?}. Existing open instances only; explicit activation joins atomically.
panel.resize{target:column|section|instance,size:{width,height},thickness_only?:bool,expected_revision?}. Omitted/false keeps full-size behavior. True changes shared Bottom row height or side column width while preserving stack extent preference/rank/weight; Floating/Window refuse. Both size components must remain finite, positive and bounded. All tab and column bounds intersect.
panel.collapse{target:section|instance,collapsed:bool,expected_revision?}. Keeps requested dimensions.
panel.floatSame as move, requiring a new_section with floating location.
panel.popoutSame as move, requiring a new_section with window location and return destination. Desktop/headless without native capability refuse; web explicitly lowers to floating.
panel.focus{target:instance,expected_revision?}. Reopens retained closed state, activates, expands and requests focus.
panel.focus.next, panel.focus.previous{expected_revision?}. Resolve the next or previous eligible panel from the live host’s keyboard context.
panel.toggle{expected_revision?}. Collapse or expand the focused panel’s section.
panel.move.next_zone{expected_revision?}. Move the focused panel to its next permitted zone.
panel.sash.left, panel.sash.right, panel.sash.up, panel.sash.down, panel.sash.reset{expected_revision?}. Resize or reset the focused sash through the same constrained candidate path as pointer resizing.
panel.activate{target:instance,expected_revision?}. Activates an open tab without expanding or requesting focus.
panel.action{target:instance,action:ControlId,expected_revision?}. Validates registration, active/open state, availability and owning controller binding, then invokes the real bound action. Returns LayoutReply; missing/disabled bindings refuse.
panel.peek{target:instance,expected_revision?}. Temporary presentation of a collapsed tab, without persistence changes to size/collapse.
panel.dismiss{expected_revision?}. Dismisses transient peek.
panel.resize_pair{first:SectionId,second:SectionId,first_size:{width,height},second_size:{width,height},expected_revision?}. Adjacent sections of one dock; conserves split-axis extent and cross-axis dimensions.
panel.reset_pair{first,second,expected_revision?}. Resets adjacent sections to the registered default ratio within intersected bounds.
panel.native_close{target:{type:"native_host",id:HostId},expected_revision?}. Native window close redocks its whole section; it does not explicitly close/cancel content.
layout.save{name,overwrite?:false,expected_revision?} → {name,overwritten,state}. Saves the complete current envelope in the injected catalogue.
layout.apply{name,expected_revision?}. Applies a named preset to the current workspace, rebinding its known layout workspace ID.
layout.reset{expected_revision?}. Applies the selected workspace’s registered immutable default. User presets and other workspace histories remain. Missing defaults return a capability error.
layout.get{} → {state,workspaces:[{id,title_key,default_preset,has_remembered_layout}]}. Selection is only state.layout.workspace.
layout.workspace.select{workspace:WorkspaceId,expected_revision?} → {previous_workspace,source:"remembered"|"default_preset"|"unchanged",state}. Registered descriptors only; no implicit creation.

The nine contextual commands above return LayoutReply. Their strict input accepts only the optional revision, checked before resolving the host context. A headless sink without a live context refuses rather than guessing a focused panel or sash. Command metadata reports the live availability and a localized disabled reason. An atomic batch may contain one contextual command alone; a mixed atomic batch refuses before applying any action.

Shortcuts use the existing rebindable keymap. The four sash arrow bindings retain their existing defaults; the other contextual commands start unbound. Automatic host shortcuts defer to a focused text editor. Explicit contextual command calls may target its actually owned panel, but cannot infer a sash from an editor. Modal, document, viewport and stale-presentation guards still apply.

Destinations are distinct foundation operations, not aliases:

{"type":"new_section","section":"logs","index":0,"location":{"type":"dock","zone":"left"},"size":{"width":280,"height":240}}
{"type":"join_tab","section":"logs","index":1}
{"type":"relative","section":"logs","placement":"after_column","new_section":"new-logs","new_column":"extra-column","size":{"width":280,"height":240}}

New-section indices address the global section order after detachment; join indices address the destination’s tab list after detachment. Joining keeps the destination’s activation, collapse and dimensions. Locations are dock (left/right/bottom), floating (origin:{x,y}), or window (host, origin, return_to). Native return_to contains {zone,index,size,collapsed,join?:{section,index}}; it never recursively contains another native host. Pins and placement/size intersections retain the foundation’s structured refusal rules.

The host’s explicit title/tab drag is a separate UX intent: a tab landing sends Move and Activate in the same guarded transaction so the dragged tab is visible. Generic background joins keep the activation semantics above.

Side sections reference stable columns, ordered outer to inner, and retain saved stack weights. Relative Before/After column placement is logical; Above/Below stacks within a target column. Pointer code maps physical edges through dock side and RTL. App commands and gestures share host allocation and typed no_space admission. Missing host/environment refuses structural app commands. Overflowing neutral/worsening proposals reject except Close, Collapse (including expansion), Focus, Activate, or strict extent improvement without growth on the other axis. Independent commands and refused batches cancel a pending gesture before parsing/preflight. Standalone panel.action validates registration, the available fallback selection, owner availability, enabled binding and controller preflight. It can execute without a host environment, during overflow, or when layout storage refuses writes; it neither allocates placement nor stages layout persistence. It cannot be combined with structural mutations in one runtime transaction. Published peek/dismiss requests remain compatibility commands; the amended host exposes no rail/peek/pin-open UI.

Sole side Content height uses bounded immutable controller metrics at assigned width, including chrome/body/footer/padding. Fill is declarative registration or responsive snapshot data. An incoming owner cohort captures its own availability and immutable metric providers before candidate allocation. Projection and commit retain those same controllers/providers. Changing metrics cancels previews, pending widget intents, cached proposals and the old frame, requiring fresh paint for widget admission. No hidden render pass is used.

Preset names trim surrounding whitespace, require 1..64 Unicode scalar values and reject controls anywhere in the supplied name. Raw input is capped at 1024 UTF-8 bytes before trimming; the trimmed name is at most 256 bytes. Names are case-sensitive. There are at most 64 user presets. Collisions reject unless overwrite:true is explicit; built-ins are immutable. No production kinds, preset contents or workspace descriptors are invented. Headless starts with an empty registry/catalogue and an internal current-layout identity, not a registered product workspace. Fixtures and peer integrations can inject synthetic or real registrations. Missing capabilities return errors rather than guessed defaults or disk/native success.

Workspace selection stages the outgoing full envelope, destination and selected workspace together, then uses one guarded Replace on the retained runtime. Same-workspace selection is a true no-op, preserving revision, focus, peek and effects even if storage staging is unavailable. Applying/resetting a preset never selects a workspace. Replacements hide omitted content without implicit Close or Cancel; retained editors, conversations and hidden services survive A→B→A.

Catalogue preparation uses encoded full LayoutEnvelope records and explicit aggregate caps: 64 registered workspaces, 64 built-ins, 64 remembered envelopes, 64 user presets, 16384 metadata bytes, and 4194304 aggregate envelope/recovery bytes. Each envelope/rejected recovery buffer remains at most 262144 bytes and obeys the foundation node/depth/collection caps. Quarantine, retained data, shared section snapshots including projected sections, complete section order and bounded rejected recovery intent survive save/apply/reset. Conflicting opaque identities, snapshots, retained data or recovery intent reject atomically. Restoration uses the foundation decoder and reappearance logic. Storage preparation finishes before live dispatch; catalogue publication is infallible after successful native/controller guards. Revision changes during staging reject the staged catalogue. No fallible storage write after live commit is treated as an atomic success. The application preference adapter stores the bounded workspace catalogue under panel_layout in SolveApp::prefs. Native and web use the same representation. A saved shared layout takes precedence over older placement settings, which are read only for migration when no shared record exists.

Atomic layout batches contain 1..64 commands, using the same parser/executor and one final guarded Apply or Replace plus one catalogue publication. Queries and saves may accompany mutations; save/apply/reset/workspace sequences can stage together. Replies describe the final committed state, not hypothetical intermediate live states. Every expected_revision in an atomic batch refers to the initial live revision. Explicit Close/Open conflicts remain lifecycle errors. Atomic batches cannot combine a snapshot replacement with explicit panel mutations, or mix document commands with application layout; they return capability before publishing anything. This preserves the foundation’s explicit lifecycle intents. Use rollback:false for sequential per-command transactions across these domains; failures report rolled_back:false, undone:0 and completed results. Scripts use the same injected backend sequentially and stop at the first failure. A Session-only executor (including ordinary CLI design scripts without an injected backend) returns a structured missing-capability error for layout commands.

The GUI route uses the enabled runtime’s actual content-service provider and an injected NativeHostAdapter, never a reconstructed LayoutService. Headless MCP owns one persistent application backend distinct from Session; Remote forwards to the app and keeps no mirrored layout. Auto mode does not discard headless layout work when an app appears. Foundation errors retain {rule,detail} with bounded details: control replies add panel_error alongside the existing error string; MCP tool errors add structuredContent.panel_error. The error string also encodes that object, preserving identity through older string-only backend boundaries. Rust integrations implement ActionSink::project(LayoutAction) as a pure projection on the authoritative service. Its default returns capability so an older inspect/dispatch-only sink cannot accidentally publish unstaged commands. LayoutService and RuntimeActionSink provide it using the foundation’s own preparation path. with_live_layout refreshes the envelope’s shared section memory and projections without discarding opaque fields, then encodes against the same foundation limits before publication.

Storage consumers can enumerate CatalogueState::user_presets() read-only and round-trip a complete validated catalogue with export() and CatalogueState::hydrate(bytes,registry) or InMemoryCatalogue::from_snapshot. Hydration is independent of live commands; restored users remain explicitly overwritable users, while built-ins remain immutable. Workspace descriptors, remembered full envelopes, and rejected recovery bytes are included. The versioned snapshot uses SCPCAT01, then length-prefixed little-endian u32 counts/UTF-8 names and raw byte records, in built-ins/users/workspaces/remembered order. Each envelope record contains length-prefixed envelope JSON, a recovery-presence byte (0/1), and, when present, length-prefixed exact recovery bytes. Workspace descriptors contain ID/title-key/default-name strings. The snapshot has an additional 8192-byte framing allowance above the existing aggregate payload/metadata caps (4218880 bytes total). Every count/string/record is bounded before allocation, and hydration rejects duplicate names across users/built-ins, duplicate workspace keys, corrupt framing, trailing data, invalid envelope identities and all existing aggregate/node/depth limits before a candidate can be published. This seam performs no disk storage.

With application panel preferences enabled, the existing Layout preferences page uses these commands for workspace selection, named layout save/apply and reset. Reset changes only the current workspace and keeps the named layout library. Preferences Cancel restores the layout and catalogue through runtime admission; if restoration is refused, the dialog and its saved baseline stay open. Periodic preference writes retain that baseline until Apply. Settings profiles carry the selected layout workspace and each workspace’s current arrangement, excluding the named library and machine-specific native window geometry. Page defaults preserve all arrangements and the library. None of these operations changes the design or camera. Names compare case-insensitively; layout.save {name,replace:true} explicitly replaces a user layout. The legacy overwrite flag remains accepted.

Reading a retained envelope does not consume quarantine merely because the registry has grown. decode_retained_record validates an already-retained record exactly; decode_retained additionally checks its workspace. Neither reader generates section order/projections or performs quarantine migration/reappearance. Hydration preserves the original validated record bytes and rejected binary data, including records at the per-envelope or aggregate cap when optional panel kinds disappear or return. Missing registrations do not invalidate unrelated users, built-ins or workspace memories. Retained validation checks structural ownership, placement bounds and all applicable known-kind constraints, without invented descriptors. An exact retained layout can still contain absent kinds and is not live admission. StoredEnvelope::project_envelope (or foundation project_retained_record) stages missing-kind quarantine before a guarded live use, preserving full section order, activation and opaque records. This staging does not consume existing quarantine; ordinary save/live overlays therefore keep reappearance retryable after registry growth. Foundation decode also stages migration and attempts restoration, with its existing fallback/rejected-input result contract. Materialization may legitimately exceed a cap and reject its staged candidate; reading/hydrating the accepted original record does not materialize it. Apply/reset/workspace replacement explicitly stage restoration, and consume it only with a successful authoritative Replace and catalogue publication. A failed action leaves the restore obligation retryable. panel.popout requires the panel’s features.popout on both web and native policies before any enabled web fallback; panel.float remains separate.

Application command, atomic-batch and GUI action boundaries refresh the current read-only provider/document environment and availability. A document or contextual model change earlier in a sequential batch/script is visible to the next panel step without a tick. Queries and failed actions cannot borrow the previous document’s context. No content tick, document edit, geometry work or camera tail is added to this boundary. All application GUI and command routes use one stored native adapter. Command-adapter installation is once-only and precedes native placements. Disabling the runtime preflights retained controllers and native release through that same adapter; refusal leaves it installed. Unsupported atomic intent mixtures are rejected by pure structural preflight before any context/controller publication as well as before layout/native effects.

At MCP tool level, an atomic batch containing any panel/layout command must reach the authoritative batch transaction. If the endpoint is unavailable or the encoded request exceeds its transport margin, a structured zero-effect capability/size refusal replaces the old sequential fallback. No document checkpoint or individual layout command is sent. Explicit rollback:false keeps the sequential fallback and reports completed layout changes honestly. Document-only batch fallback keeps its existing checkpoint/rollback behaviour. Explicit null parameters normalize to an empty object in single, atomic, sequential and script routes for GUI/headless.

Compatibility: view.layout still means Floating/Compact surface styling. History-in-Browser, History Bodies/Origin and Render search/options remain content preferences. view.history_layout and view.render_dock use the shared host for placement. Integrations with explicit identities can issue typed panel.move, panel.resize and panel.collapse requests directly. No command implicitly selects a layout workspace on a document, ribbon, tool or context change.

At the Rust app boundary, AppPanelRuntime::new(runtime, services) is fallible and rejects incoming native placements. SolveApp::dispatch_panel_layout(action) uses the stored native adapter. It accepts no borrowed adapter. Install a future native adapter with set_panel_native_adapter before native creation/restore, or install it with the catalogue through AppPanelRuntime::set_command_adapters. Once command adapters are installed, neither injection API can replace them. App command/action/render/mount boundaries reconcile the actual current Session without another service tick. Runtime inspection remains read-only. The shared host adds no production command registration beyond the application-layout commands above.

For native host evidence, ui.panel_fixture {enabled?, preset?, remember?, translucency_percent?, reduced_motion?} installs bounded synthetic content in the real SolveApp workspace host. The normal connection authentication applies. It is off by default and ephemeral unless explicitly enabled with remember:true. It adds no design change, production content registration or native panel window.

RequestEffect
{}Read current host/probe/Session observations without ticking services or dispatching actions.
{"enabled":true}Enable the docks specimen if no runtime exists.
{"preset":"tabs"}Enable the named specimen if no runtime exists.
{"preset":"tabs","remember":true}Enable the specimen and its application preference catalogue. Restore a saved record after valid host dimensions are available.
{"preset":"surfaces","translucency_percent":50}Enable declared surface-policy specimens with a runtime-only translucency request (finite 0–100 percent). Readability may raise effective opacity.
{"translucency_percent":100}Change the enabled specimen’s request without replacing controllers, drafts or layout. Refuse if no specimen owns the host.
{"reduced_motion":true}Disable specimen host animation without replacing controllers or changing saved preferences or layout. False restores theme-controlled motion. Refuse if no specimen owns the host.
{"enabled":false}Preflight and tear down only the specimen runtime; refusal leaves it intact.

Presets are docks (Left/Right/Bottom, with two Left sections), tabs (two groups of two), collapsed (four full-width collapsed headers), floating (four in-app floating sections), overflow (24 instances), and pinned (an outer pinned Right section plus ordinary docks), columns (three independent Left columns), stack_tabs, content_fill (sole Content versus Fill), inline (one editable total row), and surfaces (mixed tab and explicit surface policies). Only surfaces opts out of compatibility opaque presentation. Requested opacity is 1 - translucency_percent / 100; the override never changes saved preferences. The retired rails preset is rejected. Original synthetic content retains editable drafts and controller state across hiding and moves. Services keep ticking while content is hidden. Apply/Finish/Cancel are synthetic actions; the Session is never passed to their command sink. The editor’s cancellation guard checkbox can refuse explicit Close and whole-runtime teardown while its synthetic transaction is live.

Enabling always rejects an existing runtime, including an already enabled specimen. To change preset, disable successfully first, then enable the next preset. Enable stages the runtime, first tick and host before publishing any of them; teardown preflights every retained controller. Wrong types, unknown parameters/presets and enabled:false combined with preset fail before mutation.

remember is an opt-in enable parameter, not a switch on an existing runtime. To review restart persistence, save through the normal application preference writer, restart with the same isolated settings, then explicitly enable the same specimen with remember:true. Loading settings alone never starts specimen content. A failed restore retains the saved record and reports a recovery notice. Any Modelling, Sketch, Render, Review or Classic arrangements registered by this fixture are synthetic demonstrations, not production workspace presets.

Replies include enabled, runtime when the specimen runtime exists, host (null before its first rendered frame), and session. runtime contains the committed LayoutReply, retained controller count and synthetic probe snapshots (typed instance/owner, process-local controller identity tokens, drafts, service/render/lifecycle/action counts, transaction and guard state). host reports the last actual host frame: full semantic control IDs/typed target descriptions, accessible names, enabled flags and logical rectangles; section/body rectangles, collapse/inline flags, declared surface policy and captured frame paint_alpha (null without a renderer snapshot), resolved unpresented content, bounded scroll extent/offset, paint/input/occlusion rectangles, render domain, placement capacity, usable-space geometry, viewport, exact prepared preview, menu bounds, observed host events and committed frame reply. The top-level effective_usable application contour additionally includes current persistent windows and independent navigation controls; the host’s own usable geometry describes host footprints. Sections and resolved geometry retain intended layout; paint/input/occlusion arrays and published controls describe actual paint, excluding invisible Area sizing passes. reply, committed and prepared_committed events describe authoritative post-dispatch state. Region events include the menu and owned overlays in effective paint/input/ occlusion rectangles. Those overlays do not shrink the render domain or become automatic camera targets. The viewport fills everything below the application bar, including behind the ribbon and panels; opacity never makes controls click-through. Ordinary layout commands and gestures preserve camera pose, zoom, projection and framing offset. Explicit Fit/Centre use safe usable space and the model extent. The effective contour combines host footprints with current persistent surfaces outside the host, such as the active sketch palette; hidden surfaces and transient menus do not create fitting reservations. Fit may change zoom. Existing Look At/view.normal_to centring preserves zoom/distance, requested orientation and selected focus: it places the whole footprint at the current zoom when possible, otherwise only the focus at a stable usable centre. It never silently zooms out, and generic camera apply/replay does not acquire a centring offset.

Ordinary title/tab drags accept explicit dock, split and tab targets. A landing preview describes the exact candidate, affected neighbours and insertion index. Empty space does not create a floating panel; use panel.float or the explicit menu action. Invalid/no target, cancellation and stale captured geometry commit nothing. A valid unchanged candidate commits once, consuming its owned prepared state without redispatch. Only its landing, changed neighbours and tab marker are painted. There are no idle target widgets. Full-width collapsed headers retain declared custom actions, menu, chevron and optional quick close; one click expands. Tiny windows retain panels at minima in a bounded scroll extent; narrow scroll tracks do not shield uncovered canvas.

Public Rust SolveApp::universal_panel_frame() also exposes HostFrame::next_layout, the resolved state pending repaint, and owned_overlays. The fixture JSON does not serialize those two as separate fields. Never infer painted controls from pending layout geometry. Compare the painted frame’s revision with committed replies and runtime revisions. An empty fixture query neither reconciles context nor ticks: the current Session may have changed since the retained runtime/frame was reconciled. Compare document scope as well as revision. Target descriptions are diagnostics, never command addresses or strings to parse.

session observes actual document ID/UID/revision/dirty/undo/redo, document/model/ cache Arc identities, recomputed feature counts, existing cached mesh identities (first 128 bodies, with total count), and camera. Pointer strings are opaque process-local equality tokens. Inspection uses only already cached meshes; evaluation_count counts live-model evaluations after Session initialization. It excludes construction, other Sessions and trial evaluations of cloned models; it does not count all geometry work or tessellation. These values permit before/after comparisons, not a claim that any invariant or visual result has already been verified.

In the real cad_ui, host drawing defers input finalization until after all foreground windows/modal layers. Finalization checks the current Session and the actual enabled/top-layer egui target before accepting an initial press, then uses the stored native adapter for dispatch. Exported rectangles alone do not authorize clicking through another window. The root host and each native-host presentation use complete separate mount sets; mounting or unmounting one does not deactivate the others. These are native-adapter seams, not an implementation of OS panel windows. The specimen/default adapter advertises no native windows, and the current opaque renderer rejects unsupported translucent policy requests.

Scenarios accept {"panel_fixture":{"enabled":true,"preset":"docks"}}, {"panel_fixture":{}} and {"panel_fixture":{"enabled":false}}, using this same control path and normal fail:true handling. Use the returned control rectangles with existing pointer/key, resize and screenshot requests. Capture real native ui.screenshot output and inspect it for chrome evidence; a model-only ui.render or headless scenario does not show a native panel host.

For UI-kit review, ui.status_fixture {"preset":"mode"} shows bounded status specimens in the real viewport. Presets are off, mode, result, attention, row, kinds, many and key; the reply carries the preset and item count. This scenario fixture does not change engine state or open a dialog. off clears it. The published viewport_status:<id>:item, :setting, :key and :close handles support the ordinary ui.at, ui.move, ui.click and keyboard controls. With viewport_status_summary enabled, kinds (three) and many (four) exercise the summary component, using explicit specimen newest metadata. The viewport_status:summary and menu:viewport_status_clear_all handles open its native menu and activate its footer. These specimens are not persistent engine results; actual production sources currently supply at most three Mode items.

Toasts the user may close have a close (×) at their top right instead of a Close button, published as toast:<name>:close: toast:status:close, toast:space_mouse_offer:close and toast:stand_up:close (#7971). Those that go by themselves draw a thin ring around it that shrinks over their time; the ring is full again while the pointer is on a toast that waits for it.

Live details publish details:field:<id>, details:show_all and details:action:copy handles. details_findings and details_readout are explicitly labelled component-only ui.status_fixture presets. Real card settings execute the normal analysis/Section commands; agents can read the same structured settings and retained Continuity records with the pure view.status_details engine command. Its current flag guards geometry targets, and its per-record limits prevent mixed measurements from being mistaken for one global tolerance.

The control channel is off by default: Preferences > Assistant > Agents through MCP > “Let AI agents connect” turns it on, and --control PORT opens it for one run. Once on, SolidCraft listens on 127.0.0.1 only, for JSON lines, so agents can drive the app without a launch flag. Every connection still needs this launch’s token (below); nothing else on the machine, and nothing off it, can send a command.

Internal rendering harness: ui.blur {radius_px?, surface_alpha?, fixture?} queries or sets session-only panel blur. Radius is finite physical pixels in 0..60; the optional capture tint is 0..1 (null restores ordinary fills). This is not a saved preference. Theme-authored blur is edited with ui.prefs {theme_options:{theme_id:{blur:18}}}; the theme must declare the option. Finite fractional values remain valid, although the UI slider edits in steps of one physical pixel. The ui.prefs option definition’s available/translated reason describes current app/device/OS availability; its value remains the saved/default intent while unavailable.

The ui.blur reply contains requested radius, renderer capability, app-aware available/reason, guard (tier cap, streak, notice, session opt-out), and cumulative actual effect counters, including live resources and encoded passes. Unsupported software/web/source contexts stay on ordinary painting. Use ui.screenshot for panel evidence; ui.render remains a model-only capture. Activation, full egui GPU fixture and manager-run evidence commands are in blur-behind.md. Supplying radius_px selects the legacy session-only rendering harness override; fixture:"clear" removes that override and any injected availability reason, returning to theme intent. It does not revoke a quality-guard Undo opt-out.

Explicit evidence-only fixtures are gpu-software, reduce-transparency, and web-unsupported (force an unavailable reason), plus guard-60 (inject sixty eligible 23 ms samples into the transient guard at the current positive radius). The reply labels the active injection as evidence_fixture; null means none. These are not end-user settings, real adapter/OS readings or performance measurements. There is no fixture that makes an unsupported renderer available. They do not change saved options, host OS settings or document history. All supplied fields are validated before mutation. Capture fixture screenshots with that label in the evidence description, and clear the fixture before checking real capabilities.

LaunchWhat listens
solidcraft --control PORT, or SOLIDCRAFT_CONTROL_PORT=PORTexactly 127.0.0.1:PORT (the flag wins over the variable), whatever the setting
neither, and Preferences > Assistant > Agents through MCP > “Let AI agents connect” on (the default)the setting’s port: the default 27878, or the next free one up to 27893 when it is taken (a second window, another program); any other port exactly, or nothing when it is taken (the panel says so)
neither, and the setting offnothing

Turning the setting on or off, or changing its port, takes effect at once: the channel closes (with its connections, token file and discovery file) and opens again where the setting asks. A launch with --control or the variable keeps its port for that run, and the setting waits for the next launch. Old preference files, which don’t have it, load with it on. A launch with --control or the variable behaves as it always did (the start page stays hidden, the window doesn’t ask for the foreground), because a scripted launch expects that; the default channel changes neither for a normal launch.

Finding the port

Each app that opens its channel writes the port to a discovery file beside the token files, control/current.port (same folders and permissions as below), and removes it at exit if no later app has replaced it: it names another port, or another app now holds that port’s token file (two Flatpak windows, each with its own loopback, can listen on the same port number). It holds only the port, never the token. solidcraft-cli mcp with no arguments reads it, checks that something listens there and bridges to that app; with no app running, or with --headless, it runs a session of its own. --connect HOST:PORT and the other arguments still decide first. With several windows open, the most recently started one is the one found; use --connect for another.

Requests are one line {"id": …, "method": "…", "params": {…}}; replies are described below. Each request is one line {"id": …, "method": "…", "params": {…}}; each reply is one line {"id": …, "ok": true, "result": …} or {"id": …, "ok": false, "error": "…"}. Requests are handled on the UI thread between frames; pointer and keyboard input is injected as real egui events, one pointer event per frame. A connection that sent control.subscribe is also sent event lines {"event": "…", "params": {…}}, which have no id (Following the design).

Token

Every connection must authenticate. At start the app makes a random 256-bit token for this launch and writes it, as one line of hex, to a file only the user can read:

SystemToken file
Linux$XDG_DATA_HOME/solidcraft/control/PORT.token (~/.local/share/solidcraft/control/PORT.token)
macOS~/Library/Application Support/SolidCraft/control/PORT.token
Windows%LOCALAPPDATA%\SolidCraft\control\PORT.token

On Unix the folder is 0700 and the file 0600; on Windows the file’s inherited permissions are replaced by a single entry for the user. SOLIDCRAFT_CONTROL_DIR moves the folder. If Windows doesn’t finish restricting the file within five seconds, the default folder (private to the user) keeps it, but a custom folder gets no token file, and clients then need SOLIDCRAFT_CONTROL_TOKEN. The file is removed when the app exits, unless another launch has written its own token there since, and the next launch writes a new token. The token file’s path is printed to stderr at start-up. Beside it, <port>.pid holds the app’s process id (and, on Linux, its start time).

An app that is force-quit can’t remove its files, so a later client may send that launch’s token. When the app refuses it, solidcraft-cli mcp names the token file it read and when it was written, and says the file is stale when the process in <port>.pid has exited. Delete the file or restart SolidCraft. On Windows, an app or client started from inside an MSIX-packaged app (and the tools it launches) has its AppData redirected: it writes and reads %LOCALAPPDATA%\Packages\<package>\LocalCache\Local\SolidCraft\control\ before the folder above.

The first request on a connection must carry the token as a top-level "token" member ({"id": 1, "method": "ui.inspect", "params": {}, "token": "…"}); later requests on the same connection may leave it out. A first line without the right token is answered with {"ok": false, "error": "unauthorized: …"} and the connection is closed, without running anything. Tokens are compared in constant time. A connection that hasn’t sent that first request within five seconds is closed without a reply, and blank lines don’t extend the time. So connect when you have something to send; once authenticated, a connection may stay idle.

solidcraft-cli mcp --connect 127.0.0.1:PORT reads the token file for that port by itself (--token T or SOLIDCRAFT_CONTROL_TOKEN give one explicitly). For a test harness, or a sandboxed package whose data folder the client can’t see, start the app with SOLIDCRAFT_CONTROL_TOKEN set to at least 32 hex characters (for example openssl rand -hex 32) and the app uses that token instead of making one; the file is still written. A one-line client in Python:

token = (Path.home() / ".local/share/solidcraft/control/7878.token").read_text().strip()
sock.sendall((json.dumps({"id": 1, "method": "ui.inspect", "params": {}, "token": token}) + "\n").encode())

Limits

The channel is untrusted input. A request line may be at most 16 MiB; a longer one is answered with {"ok": false, "error": "request too large"} and the connection is closed, as are connections that send eight lines that are not JSON. At most 16 connections are served at once (further ones get {"ok": false, "error": "too many connections"}), at most 64 queued requests run per frame so a flood cannot starve drawing, and a request that the UI thread has not answered within 60 seconds (3600 seconds for engine.batch) is replied to with {"ok": false, "running": true, "error": "timeout: … is still running …"}. The same 16 MiB line limit applies to the MCP server.

A timed-out request has not failed: it is still running in the app, and a change still applies when it finishes (a long fillet, say), so don’t simply send it again. The timeout’s text says what it still does: a request that only reads (ui.inspect, document.inspect, an inspection command) “only reads: nothing changes”; one that only writes a file (file.export, file.save, file.save_as, file.save_mesh, another export given a path, ui.render, or ui.screenshot with a path) “will still write the file when it finishes” and leaves the design as it is; any other request “will still apply when it finishes”. The connection keeps its reply (#1618). Ask for it with control.pending: {"pending": <id>, "method": …, "done": false} while it runs, then {"pending": <id>, "method": …, "done": true, "reply": {…}} (its own reply, once), and {"pending": null} when nothing is outstanding. Until it lands, any other request on that connection is refused at once with {"ok": false, "busy": true, "error": "busy: …"} and not run, so nothing is queued to apply unseen behind it. control.pending belongs to the connection: a new connection has nothing pending. The bridged MCP server does this itself (mcp.md).

control.pending answers only for its own connection. control.running answers for the whole app, the same on every connection, at once (it never waits behind a running request, and a connection may ask it while it is busy): every request the app has been sent and has not answered yet, oldest first, from any connection, including a timed-out one whose connection has since closed:

{"id":1,"method":"control.running","token":"…"}
→ {"id":1,"ok":true,"result":{"running":[
    {"connection":3,"id":17,"method":"engine.execute","command":"file.export","running_s":312.4,"queued":false},
    {"connection":5,"id":2,"method":"engine.batch","steps":12,"running_s":4.1,"queued":true,"self":true}]}}

running_s counts seconds since the request arrived; queued is true while it still waits its turn behind another. command is the command id of engine.execute (a known one only) and steps the length of engine.batch or engine.script; parameters are never shown, so file paths and expressions stay with the connection that sent them. self marks the asking connection’s own request. {"running": []} when the app is idle.

control.capabilities says what this app’s control channel does beyond the methods every version has, so a client can find out before it relies on it: {"param_check": ["off", "warn", "strict"]}, the levels a request’s own param_check takes (below). It is answered at once, like control.running, also on a busy connection. An app older than it answers unknown method `control.capabilities` ; a feature added later is listed here too.

Any request’s params may carry allow_paths: ["/abs/folder", …]: every file that request reads, writes or deletes (command paths such as file.export’s path or doc.recovery_*’s dir, ui.render, ui.screenshot) must then lie inside one of those folders after .. and symlinks are resolved, or the request fails with … outside the folders this agent may use …. The app adds its own recovery folder and, when it lies safely inside the home folder and not in a hidden folder, the open design’s folder (whose hidden files stay out of reach). With allow_paths, design_folders: ["/abs/folder", …] (at most 16) names folders the agent opened or saved a design in; each counts on the open design folder’s terms. The bridged MCP server sends its folders this way on every request (mcp.md). Without allow_paths file paths are not limited: a client holding the token can do what the user can.

engine.execute, engine.batch and engine.script may carry param_check: "off" | "warn" | "strict" in their params: the commands of that request are checked against their typed contracts at that level, whatever the app’s own (which is off), as a headless session’s --param-check checks them (#9184). warn runs a call that breaks its contract and adds param_warnings to its result; strict refuses it (a batch before its first step), and the design is left as it was. Every reply to such a request says the level it was checked at as param_check, beside ok. A command that switches to another design (component.edit_in_place, component.finish_edit, view.next_tab, view.previous_tab), or one that goes to the file loader (doc.open, file.insert_step, file.insert_mesh, file.export_view_dxf, pointcloud.insert, pointcloud.reload), has its parameters checked at that level before it runs, and then runs at the app’s own level, as each design keeps its own. Any other word is refused; other methods ignore the field. An app older than this ignores the field and its reply has no param_check, so a client that needs the check asks control.capabilities first and treats a reply without param_check as unchecked. The bridged MCP server asks once per connection (mcp.md).

Cancelling a request

control.cancel {id?} cancels the request the connection still waits for: the timed-out one control.pending reports (id, when given, must name it). A connection can only cancel its own requests; another id is refused with nothing to cancel: request … is not running on this connection. What happens depends on where the request is:

  • Waiting its turn (behind a design change that computes, a file opening, the agent cursor): it is removed and never runs. {"cancelled": <id>, "method": …, "outcome": "removed", "note": "cancelled: …"}; its own reply, if anyone still reads it, is {"ok": false, "cancelled": true, "error": "cancelled: …; nothing was applied"}.
  • Computing off the window thread (a design change or an export, see below, or one the agent cursor is acting out): it is stopped as Stop stops it, and nothing of it is applied: "outcome": "stopped". Its worker is told to stop as well: the evaluation skips the features it has not reached, the kernel starts no further boolean, and nothing it computed after that is kept. A boolean or tessellation already under way still runs to its end in the background. An export’s worker may still write its file; the design is unchanged.
  • Running on the window thread (with the preference off, while it applies after computing, or a file being opened or imported, which the app’s own Cancel stops): it can’t be cancelled. {"ok": false, "cancellable": false, "error": "cannot be cancelled: …"}; it will still finish, and control.pending gets its result.
  • Finished: its reply comes back as control.pending gives it (done: true, reply), with cancelled: null.

With nothing outstanding the reply is {"cancelled": null, "note": "nothing to cancel: …"}. After a request is cancelled or stopped the connection is free at once (control.pending says {"pending": null}). Since a connection sends one request at a time, only a request that outlasted its wait can be cancelled from the connection that sent it: 60 s, or 3600 s for engine.batch and for every request from the agent chat’s own connection.

Params

A field may be left out where a method marks it optional (?), but a field that is present must have the documented type: a wrong type, or null for a field with a default (such as shift or delta), is refused with an error naming the field, and nothing runs. An optional field without a default takes null as left out (not ui.scroll’s unit or ui.home’s sample, which refuse it); engine.execute takes params: null as {}, and ui.start as no params (so solid.extrude starts on null but refuses {}). ui.set, ui.blur, ui.editFolder, ui.resize, ui.palette, ui.agent_cursor and ui.icon_audit still read their fields one by one: the first four refuse a wrong type, the other three pass over it. Screen points (x, y) must lie within ±1 000 000. In particular, engine.execute takes command or its alias id, not both; ui.help refuses an item it doesn’t know; and ui.menu needs both x and y to open a menu (neither describes the open one).

Engine

MethodParamsResult
engine.executecommand: command id, params: JSON objectthe command’s result
engine.scriptcommands: [{command, params}, …]results. Each step runs as engine.execute runs it, so the window follows it as it does a batch step. The first error stops the script, and the steps before it stay
engine.batchcommands: [{command, params}, …] (at most 10 000), rollback?: bool (default true), timings?, profile?, coalesce_parameters?: bool (default false){completed, results, undone}, plus failed: {index, command, error} when a step fails, and with timings timings: [{index, command, ms}] (the failing step included) and total_ms; profile adds each step’s profile: {boolean_ms, tessellate_ms, other_ms}. Runs the steps in one request, so within one frame, each as engine.execute runs it. The first failure stops the batch, and with rollback the design goes back to how it was before it (undone: the steps taken back, rolled_back: true only when a rollback was asked for and the design is where the batch started, false when rollback: false or when the checkpoint could not restore it). Refused before anything runs, with an error starting engine.batch unavailable, when a step loads a file (doc.open, file.insert_step, file.insert_mesh, pointcloud.insert, pointcloud.reload) or when it goes to another design’s tab (component.edit_in_place, component.finish_edit, which engine.script refuses too). Send such steps one by one. A step the batch would certainly refuse at its turn is found before the first step runs (#5853): a step without a command, an unknown command id (with the closest ids) or, with strict parameter checking, a call that breaks its command’s contract. The reply is then that step’s failure with completed: 0, undone: 0, rolled_back as after a rollback that went back (true, or false when rollback: false: the design is where the batch started), not_run listing every step and a note; nothing has run. With coalesce_parameters, each run of consecutive plain parameter edits (parameters.change with only a name and an expression, or with changes) is set as one parameters.change {changes}, so the design rebuilds once for the run. Every step still gets its own result (with coalesced: the run’s length), and the run is one undo step. The run skips the states between its edits: an edit that would be refused on its own, but not once the run’s later edits are in, goes through. A run whose combined edit is refused (a new parameter, a bad expression) runs step by step, so the refused step is named. Answered when done, for up to 3600 s
engine.commands—every command: id, label, tab, panel, icon, shortcut (the user’s current first key, or null when cleared), params doc, enabled
document.revision—{document_id, revision}: the active design’s id and a number that moves whenever the design changes, for programs that follow it (Following the design)
control.subscribe—{document_id, revision}, as document.revision; the connection is then sent a design.changed line whenever they change (Following the design)
document.inspectmeasure?: bool, sketch_counts?: bool (default false), timeline?: true (default), "summary" (each feature as id, name, type, with error, warning, suppressed and rolled_back only when set) or false (left out), bodies?: true (default), "names" (only the body names, nothing measured) or false (left out)parameters, timeline (errors, timings), bodies, sketches, selection (assembly joints: engine.execute joint.inspect {joint?}, which reads only: each joint’s two origins with the occurrence, the snap as saved, the feature, face, edge or sketch curve its names refer to, and whether it resolves or why not; kind, offset, angle, flip, values, limits, expressions and motion links); with sketch_counts each sketch also gives its plane and its number of points and constraints
mcp.options—{"compact": bool}: the output the MCP server bridged to this app should give its agents (Preferences › AI › Agents through MCP, ui.prefs mcp_compact, default false; MCP)
document.export—{name, revision, dirty, format, text}: the active design as its file holds it, for a host that keeps the files itself (browser command API). text is the design’s JSON, what a .json design file holds; format is "json"; revision is the one document.revision answers for this state. Only reads
document.importtext: a design’s JSON; name?: the name it takes{name, features, errors, revision}: replaces the active design with text, timeline and parameters as saved; it opens saved, with a new document_id and nothing to undo. It does not ask about unsaved changes in the design it replaces: that is the caller’s to decide. A text that does not read changes nothing. Waits behind a file loading or a design change, and evaluates on the window thread in the frame it runs
document.savedrevision: as document.export gave it{revision, dirty}: the host saved the design at that revision, so it counts as saved from there. Refused when the design has changed since

The selection alone is the engine command selection.get (run it with engine.execute; read-only, no undo step, headless too): {count, items}, each item as select.set takes it (type, body, index, point, id, sketch, path…) with kind and, unless detail: false, what it resolves to: a face’s persistent name, surface (plane with normal, cylinder with origin, axis and radius, cone, sphere, torus, or other) and area_mm2; an edge’s persistent name, curve (line, circle, arc, other), length_mm, start, end and, when round, center, axis and radius; a body’s volume_mm3, feature and visible; a body item’s component and occurrence_path in an assembly; a sketch item’s sketch (its id, so the item can go back to select.set), sketch_name and entity (a line’s sketch_midpoint gives midpoint and its position). ui.selection (below) reports the window’s view of it with the open dialog’s inputs and the hovered item.

Commands never open dialogs when run this way. solidcraft-cli commands prints the same registry.

document.export, document.import and document.saved are control methods, not commands: they act on the app’s open design rather than on an engine session, and they are not among the methods the agent chat may use.

Following the design

A program that follows the open design (a renderer kept in step, a live link) asks document.revision, which only reads and does not wait behind design changes (a slow command computing, a file loading, the agent cursor acting one out), and exports only when the answer changes (#6646):

{"id":1,"method":"document.revision","token":"…"}
→ {"id":1,"ok":true,"result":{"document_id":"18f3a0c2d4e5b6a7-1","revision":3}}
  • document_id names the active design (its tab) while it stays open, through edits, saves and renames. Two open designs have different ids even with the same name, and each design started or opened gets a new one, also one opened or started in the same tab. Ids of one run never match those of another. Treat it as an opaque string.
  • revision moves whenever the design changes: an edit, a parameter, undo or redo, a design opened or started, what is hidden or shown (saved with the design), a component moved and not captured yet. It stays the same for reads, a selection, a camera move, a section or an analysis view. Revisions come from one counter for the whole run, so a revision never names two states, and it never goes back: changes undone before the next ask that end where the design was need no new one (its revision still names that design).

Switching tabs changes document_id; back on a tab its revision is the one it had, if nothing changed it meanwhile. Compare both values, and export when either differs from the last answer. The same answer comes from engine.execute document.revision (and so from scripts and MCP execute), which waits its turn behind design changes like any command.

engine.execute with view.status_items and {} reads the persistent viewport effects without evaluating the model or changing the design, undo history or view revision. It returns {items: [...]} in oldest activation order. Each item contains a stable slot id (surface_analysis, section, or the legacy independent continuity), kind, source command, structured setting, has_key and a clear: {command, params} request that can be executed unchanged. Settings retain numbers: lengths use millimetres, Draft’s angle_deg uses degrees, directions are normalised, and Continuity’s edge_count counts the seams currently kept for shading. Labels and units are formatted by clients. engine.describe {command: "view.status_items"} exposes both schemas.

By default, surface analyses, including Continuity, share one slot; successful replacement keeps that slot’s position and ends the previous analysis. Section stacks independently. Clearing and re-enabling a slot makes it newest. Ordering metadata belongs only to the running session; effects and ordering are not saved in the design. Existing analysis clear semantics apply: any surface-shading command with {clear: true} clears the active surface shading, whereas Continuity clears its seam records. Section clear never clears an analysis.

The standard Graphics → Viewport → Use a single surface analysis preference is single_surface_analysis (default true). ui.prefs {"single_surface_analysis": false} restores the legacy behavior: surface shading and Continuity remain independently active and clearable, and view.status_items faithfully lists both, using a separate stable continuity ID. Together with Section there are at most three slots. Turning the preference back on keeps the last successfully started or changed analysis at the oldest analysis-group position beside Section. Queries never normalize state. Graphics Restore page defaults restores true; other page resets leave it alone. It applies immediately to all open designs without document edits or undo. Cancel restores the opening preference value, but does not restart an analysis already stopped by switching to single mode. Run that analysis again to restore coexistence after cancelling. Successful Continuity measurements with no seams create no item: legacy mode keeps existing surface shading; single mode ends the previous analysis as for other successful replacements.

Headless SDK clients use Session::set_single_surface_analysis(bool) and Session::single_surface_analysis(). Configuration survives scratch/new/open session lifecycle; effects and chronology still follow their existing transient lifecycle. The policy is an application preference/session setting, never saved design-format data. Policy changes update the view revision only when visible effects or their declarations change.

A client need not ask again and again: after control.subscribe on its connection, the app sends it a line whenever the active design changes (#6985):

{"id":1,"method":"control.subscribe","token":"…"}
→ {"id":1,"ok":true,"result":{"document_id":"18f3a0c2d4e5b6a7-1","revision":3}}
{"event":"design.changed","params":{"document_id":"18f3a0c2d4e5b6a7-1","revision":4}}
{"event":"design.changed","params":{"document_id":"18f3a0c2d4e5b6a7-2","revision":5}}
  • The reply is what document.revision answers then (and, like it, does not wait behind design changes); no event comes before it. From there on, each design.changed line carries what document.revision would answer, sent when its document_id or revision differs from the last one this connection was told: an edit, undo or redo, a design opened or started, a tab switched.
  • Lines are coalesced, not sent per frame: at most about ten a second, and changes made faster (a parameter dragged) are followed by one line with the latest. Once the design stops changing, the last line names it as it is.
  • An event has event and params and no id; every reply still has its request’s id. Each event is a whole line of its own, between replies, never inside one. Ignore events you don’t know.
  • Only connections that subscribed get events, until they close (or shut their sending side). A second control.subscribe on a subscribed connection is refused with already subscribed; ask document.revision for the design now. A control.subscribe that timed out or failed did not subscribe the connection: send it again.

Slow commands and the window (#1989)

With the preference Preferences > AI > Keep the window responsive while agents run commands on (the default; ui.prefs {"agent_off_thread": false} turns it off), a design change in the desktop app that takes longer than about 150 ms (an engine.execute of a modelling command, an engine.batch of them) computes off the window thread, so the window keeps drawing and answering while it runs:

  • The command is evaluated ahead on a copy of the design, which also makes the display meshes of the bodies it ends with. Then it runs for real on the window thread, reusing every feature the evaluation computed (kept for it, whatever the bounded evaluation cache dropped meanwhile), so the window waits only for the bookkeeping. Its reply, undo step, autosave and crash journal are the same as before. With the agent cursor shown, the evaluation runs while the pointer glides to the button, and the pointer waits there until it is done.
  • Meanwhile ui.* requests, document.inspect and engine.commands are answered at once (inspection shows the design as it was), also while the agent cursor acts a command out. Later design changes, from any connection, wait their turn and run in the order they arrived.
  • A person’s edit made meanwhile goes first. The agent’s command is then computed again for the design as it is and lands after that edit, never in the middle of it. If the design was replaced (another tab made active, a new file opened, its tab closed), the request fails at once with … the design was replaced … nothing was applied, and the requests behind it go on.
  • A row of the operations panel in the view’s bottom-right corner names the command, with how many of a batch’s steps are done (hover it for the steps still to come) and a red X (with Preferences > Layout > Show operations in the view off, a chip with Stop instead; ui.operations lists and cancels the rows). Each design change waiting behind it is a row of its own, numbered by its place in the queue (1 runs next), whose X cancels just that one; the panel shows the first 4 and one “N more waiting” row whose X cancels the rest, while ui.operations lists every one of them, each cancellable by its own key. The X answers {"ok": false, "stopped": true, "error": "stopped: …; nothing was applied"} and the requests waiting behind it go on. The computation stops before its next feature or boolean, in the background. The agent can do the same for its own request with control.cancel (above).
  • file.export and file.save_mesh change nothing in the design. They run on a copy of the design as it was when they were asked for, and nothing waits behind them.
  • Batches with timings or profile still run on the window thread, so the times they report measure the real work.

A file being opened or inserted (from the window or the control channel) holds the agent’s design changes the same way until it is in, so an agent’s doc.open and the edits after it go in that order. The person at the window can go on working meanwhile: the file is a row of the operations panel, and its result lands after their edits (ui.prefs {"file_operations_wait": true} brings back the card over the design that makes everything wait for the file).

Timeouts and control.pending work as described above. A slow command is still answered when it is done, from a later frame. With the preference off, commands run on the window thread, and the window waits for each one.

Product images use render.preflight/start/status/cancel/export through engine.execute. Keep the returned session-scoped job ID for subsequent calls; status does not block or render. See render jobs for capture, cancellation and export semantics.

While an interactive native file picker is open, ui.inspect reports file_picker_busy: true. Engine commands, ui.file and ui.home requests wait for its answer; window resizing, inspection and screenshots remain available.

Every sketch.* command that changes a sketch adds created: {points, curves, constraints, dimensions} to its result (and wires for 3D curves): the ids of what it added, in creation order, next to whatever else the command returns (curves, sides, corners…). Sketch ids are stable and never reused, even after a delete (l1, l2, then l3 once l2 is gone; only undo rolls the numbering back with the design), so they can go straight into later commands:

{"id":7,"method":"engine.execute","params":{"command":"sketch.line","params":{"points":[[0,0],[40,0]]}}}
→ {"curves":["l1"],"created":{"points":["l1.start","l1.end"],"curves":["l1"],"constraints":["k1"],"dimensions":[]},…}
{"id":8,"method":"engine.execute","params":{"command":"sketch.dimension","params":{"entities":["l1"],"value":50}}}

A bare numeric value given to sketch.dimension (from the control channel, scripts or MCP) is read in the design’s units, exactly as if it were typed into the dimension box: in an inch design "value": 2 makes the line 2 in (50.8 mm) long. Point coordinates stay in millimetres. To pin a unit regardless of the design, pass an expression with one: "value": "50 mm" or "value": "0.5 in". Angles are degrees unless the expression says otherwise.

Any expression parameter of any command can also define a user parameter as it sets the value: "distance": "depth = 15" adds depth (15, read as the bare value would be) and stores depth in the input, in the same undo step. The reply names it in defined and lists every definition in definitions: [{name, expression, unit}]; a taken or reserved name fails the command and changes nothing. parameters.change with "expression": "height = 12" renames the parameter it changes instead (see the parameters guide).

The viewport also shows an X/Y/Z-aligned dimension chain between the endpoints of the currently shown distance (centre, minimum or maximum), with each world-axis distance labelled separately as an absolute value (including zero). It uses the design’s units and length precision, independently of the view or sketch plane. Preferences → Graphics → Show X/Y/Z distances controls these extra lines and labels (default on, saved between runs). Set ui.prefs {"measure_axis_distances": false} to hide them while keeping the total distance and the dialog’s ΔX/ΔY/ΔZ values. These overlays remain visible until Measure closes. Overlay handles measure:label:x, measure:label:y and measure:label:z identify the extra labels; prefs:measure_axis_distances identifies the checkbox. A ui.drag from measure:label or one of the axis labels moves that label (screen offset, kept while the measured items stay the same; a double-click puts it back). It picks nothing and starts no box selection.

UI

MethodParamsNotes
ui.inspect—UI state, pending file picker (file_picker_busy; compatibility alias file_dialog_open, no path), what is hidden (visibility: bodies, origin and construction items, sketches, joints (hidden_joints, joint ids); set it with the browser.visibility command), viewport rect, camera, the active tool (its command id), the open dialog (dialog, below, or else the “Recover unsaved design?” prompt shown after an unclean exit as {kind: "recover_unsaved", title, designs: [{id, name, path, features, saved_at, autosaved}], actions: ["recover", "discard", "later"]}, answered with ui.recover), the windows open beside the model (windows, each named by what opens it: the ui.start command parameters.change, document.metadata, sketch.inspect, assembly.bom or material.properties.validate, or the ui.help item about, updates, shortcuts, crashes, report, or messages), what is under the pointer (hover, in the form of a selection item: {"type": "edge", "body", "index", "mid", "point"}, or null), the status line (status: text and whether it is an error), renderer, timings, what the GPU drew last frame (draw: draw_calls, triangles, line_segments, supersampled; null without a GPU) and the graphics adapter, live preview (active, busy, error, ms, bodies it stands in for)
ui.setany UiState field (tab, perspective, showGrid, dark, layout, …), and the browser’s view of the design, saved with it: showOrigin (the Origin folder’s eye), showSketches (the design’s Sketches folder, c0/sketches in hiddenFolders), hiddenBodies, hiddenOrigin, hiddenSketches, shownSketches, hiddenJoints, hiddenFolders, hiddenProfiles, shownDims, lockedBodies (whole lists; browser.visibility changes single items)dark: false switches to the light theme; layout: "floating" (default) or "compact" ("docked" is also accepted) selects the transient shell layout without saving a preference. Scenarios use {"call":"ui.set","params":{"layout":"docked"}}
ui.viewview: front, back, top, bottom, left, right, iso, home, fit; animate (bool, default false: snap)
ui.cameratarget?: [x, y, z] (mm), yaw?, pitch?, roll? (radians), distance? (mm), fov?, up?; fit?: true, false or [occurrence id or name…] (root: the bodies outside every component)the viewport’s orbit camera: it looks at target from distance away; yaw 0 looks along +Y (from the front), pitch is the elevation (±π/2: straight down or up), roll turns the image. Fields given are set first (the others keep their values), then fit frames the listed occurrences (or everything), keeping the direction. The reply, passed back, sets the same view again, so a framing can be stored and replayed. fov is the angle the view is drawn with: 0 (parallel) with Perspective view off or while a sketch is open, else perspective_fov; in Render, the Render camera’s own. fov and up follow the perspective setting (ui.set), its angle (ui.prefs {perspective_fov}) and the up axis preference (ui.prefs) and can’t change here; a refused field changes nothing
ui.startcommand, params?like a toolbar click: starts the sketch tool or dialog for the command. params sets what the dialog opens on, and nothing runs until OK: joint.drive takes joint (id or name; without it, the joint picked in the browser, else the first joint that moves; a rigid joint opens to say why it has nothing to drive, with an Edit Joint button, handle drive:edit_joint) and values (numbers in degrees or millimetres, or expressions); motion.study takes study (id or name), which opens that study to edit and chart. A command that takes no params is refused before it starts; a param the dialog doesn’t take is refused and the dialog closes. Replies what it opened: tool, dialog and windows as ui.inspect gives them, status (where a command that starts nothing says why), and pending: true when the start waits for the history to compute and opens on a later frame
ui.clickx, y (screen points), button?: left/right/middle, shift?, ctrl?, double?real pointer input
ui.movex, y, shift?, alt?, ctrl?, cmd?pointer movement without a button; modifiers apply until the next synthetic move/click; a move with omitted modifiers releases them. With nav: "touchpad", Shift pans and Alt/Option orbits.
ui.dragx0, y0, x1, y1, via?, button?, shift?, ctrl?, steps?, hold?, hold_end?press, move, release; via is a list of [x, y] points passed through on the way (steps moves to each); a left drag on the model is a box selection (left to right: window, right to left: crossing), or with lassoSelect on (ui.set, the SELECT panel’s Lasso) a freehand lasso that takes what is wholly inside it; with OpenSCAD navigation, plain left-drag on empty space orbits, Shift+left-drag box-selects, and drags starting on sketch geometry edit it; hold keeps the button down that many frames first (a right press-and-hold opens the marking menu, releasing over a direction picks it), hold_end that many frames at the end (a sketch drag shows its snap)
ui.menux?, y?, target?, close?with a point: open a context menu there as a right-click would (the viewport’s marking menu, a browser menu with target: {"type": "body", "name"} | {"type": "sketch", "id"} | {"type": "component", "id"}, or a History bar feature’s with {"type": "timeline", "feature": id}); a target the design doesn’t have is refused (no body \Name`); without: the open menu (radialanditems: id, label, shortcut, enabled, icon, placeholder, and childrenfor a row that opens a flyout; blank icons on headings/separators haveplaceholder: false`)
ui.icon_auditreset?, stop?reset: true enables collection on the UI thread and clears previous requests; returns {requested: [{name, master, placeholder}]} sorted by requested id. Each read drains names collected since the previous read/reset. Collection stays enabled until stop: true (which returns pending names before disabling it). Includes aliases, variable/table ids and cached paints.
ui.menuPickitem: id or labelruns an item of the open menu or of any of its flyouts, as clicking it does; a row that only opens a flyout is refused
ui.selectOtherx?, y?, pick?, add?, hover?, close?Select Other: with a point, list compatible hidden and visible geometry near the pointer by default (as holding the left button does; disable select_other_hidden for the original visible-only list); the popup fits the available window space and scrolls only for overflow; pick: select row pick (add: as Shift-click; with a dialog open, into its active input); hover: light a row up; without: the open list (open, at, total: everything under the cursor, rows: label, icon, sel, at most 20 per kind, nearest first)
ui.renametext?, commit? (default true)finishes the rename box a Rename item opened
ui.selectionthe selection, the open dialog’s inputs, the hovered item, and the active sketch’s drawn dimensions (dimensions: id and text centre, dimension_selected, dimension_editing), and finished sketches’ dimensions shown in the model view (shown_dimensions: sketch, id and text centre; a double-click there edits the value)
ui.worldToScreenpoints: [[x, y, z]…] world pointstheir screen points
ui.triadscreen points of the 3D sketch move triad’s handles (x, y, z, plane)
ui.sketchToScreenpoints: [[x, y]…] in active-sketch coordinatestheir screen points (null when behind the camera)
ui.dialogInputindexwhat clicking an input’s Select chip does: picks go to that input
ui.atworld: [x,y,z] | sketch: [x,y] | plane: "XY" | axis: "Z" | dimension: "d1" (its value text) | handle: "arrow" (a manipulator handle, see scenario::publish_handle)the screen point (a scenario step’s target; see crates/ui-egui/src/scenario.rs)
ui.pickpixels?: [[x,y]…], rays?: [{origin, dir}…], frame?: "window"|"model", width?, height?, edges? (default true), edge_px? (default 6), all?, include_ghosted?read-only geometry under each pixel or ray (#6304): one entry per input, null where nothing is hit — body, face index, exact world point and outward normal, depth, occurrence/component for placed parts, edge/vertex within edge_px of a pixel, section_cap: true on a cut face. window pixels are physical pixels of a window screenshot; model pixels are pixels of a ui.render of width×height (default 960×600), without needing a window screenshot. Both frames and ray picking require the app’s control channel; they are unavailable through solidcraft-cli mcp --headless. As view.pick through engine.execute/engine.batch when connected to the app; absent from the engine command registry (list_commands and describe_command). See crates/ui-egui/src/view_pick.rs
ui.handlesprefix?the handles drawn last frame, [{name, x, y}]: toolbar buttons and drop-down items toolbar:<command id> (the button alone: button:<command id>; a drop-down row: dropdown:<command id>, highlighted dropdown_hot:<command id>, its pin dropdown_pin:<command id>; Customize Toolbar’s check boxes customize:<command id>), ribbon tabs tab:<name> (MODEL, SURFACE, ASSEMBLE, INSERT, INSPECT, MANAGE, VIEW, SHEET METAL, WELDMENTS; contextual SKETCH; workspace RENDER), the toolbar menu button ribbon:add_tab, the hint when every tab is hidden ribbon:tabs_hidden, Preferences tab check boxes prefs:toolbar_tab:<NAME>, panel labels panel:<tab>:<panel> (also ribbon:section:<TAB>:<SECTION>), document overflow list tab:overflow, workspace column ribbon:workspace, command-search launcher ribbon:search, browser rows row:<label>, where each row’s depth puts it indent:<label>, and fold arrows fold:<label>, manipulator handles; signed-axis tips axis_view:z, axis_view:-y; optional cube facets navigation_cube:top, navigation_cube:top_front_right, controls navigation_cube:control:home (and other actions), plus signed-axis aliases for visible cube faces; Preferences navigator picker prefs:navigator, options prefs:navigator:Signed axes and prefs:navigator:Cube; in the Motion Study dialog, where the chart puts its first and last step motion:chart_start and motion:chart_end, the Check collisions button motion:check and each collision found motion:collision:<n>; with Placement handles on, the move marker of an open placement dialog <tool>:position and its ring <tool>:rotate (rest, boss, snap_fit; primitive:position for Box, Cylinder, Cone, Sphere, Torus and Coil, which have no ring); with hole_moves on, a Single hole’s markers hole:<n>
ui.editFolderfolder (id or name)Edit Holes on a History folder that holds holes only: one Hole dialog, filled in from its first hole, whose OK gives every hole in the folder the fields changed there. Replies as ui.start does
ui.editFeaturefeature (id or name)what double-clicking a timeline item does: rolls back to it and opens its dialog filled in (sketches open in sketch mode). Items without an edit dialog show a status message naming their kind and saying there is nothing to edit; no command palette opens. Replies as ui.start does, with dialog: null for these items and the restored history marker
ui.scrollx, y, delta?, ctrl?, unit?: point | linewheel zoom at the cursor. ctrl: true replays a Ctrl+wheel pinch (Ctrl held and the wheel in points); unit selects the wheel unit and defaults to points with ctrl and lines without it. delta is in points either way. Invalid units or coordinates outside the finite screen-coordinate range are rejected before queuing input
ui.keykey (egui key name, Enter, Escape…), cmd?, ctrl?, shift?, alt?, pressed?By default, press and release, then release modifiers. With pressed: true or false, send only that edge; modifiers remain as given. cmd retains the legacy primary-modifier alias unless ctrl is explicit: cmd:true, ctrl:false sends physical Command, and both true sends Control+Command. ui.move can change modifiers between edges.
ui.texttexttyped text
ui.screenshotpath?PNG of the window (needs a presented frame: a minimised or fully covered window answers at once with hidden: true and an error; use ui.render)
ui.renderpath, width?, height?, grid?, axes?Render of the model with the current camera (no window needed), drawn on the app’s GPU device with the viewport’s pipelines (the reply’s renderer: gpu, or gpu-software on a software adapter), in the window’s theme (light or dark; solidcraft-cli snapshot keeps the light colours); cut by the section the viewport shows (inspect.section, an open Section Analysis dialog’s preview, a sketch’s Slice), with the cut face capped. The ground grid and the origin’s axes are drawn as the window shows them (ui.set showGrid and showOrigin, hiddenOrigin); grid and axes (booleans) say otherwise for this render
ui.confirmaccept?: boolanswers the Delete confirmation (shown when a delete takes other features with it or makes some fail); without accept, what it lists
ui.messagesclear?: true (empty the list), open?: bool (show or hide the Messages window)every status message of this run, oldest first (the last 200; a message repeated in a row is one entry with a count): messages [{text, error, at, count}] (at in ms since the Unix epoch), and whether the window is open (#2763)
ui.documentsaction?: new, switch, close, close_all, close_others, move, quit; index?; from?, to? (move); save_indices?: array of tab indices to tick in the grouped save prompt; save_row?, discard_row?: tab index to save or discard and close in that prompt; force? (close without asking); answer?: save, dont_save, cancel (answers “save changes?”); open_elsewhere?: read_only, anyway, cancel (answers “open elsewhere” for the first Open waiting); changed_on_disk?: save_copy, overwrite, cancel (answers “changed on disk” before a Save)the open designs (tabs): names, unsaved flags, the active one, a pending “save changes?” prompt, save_prompt (null or rows with index, name, path (null if never saved), selected), whether a quit is asking, whether the active design is read_only, the Opens waiting for “open elsewhere” (open_elsewhere) and a waiting “changed on disk” (changed_on_disk)
ui.operationscancel?: an operation’s keythe operations under way, newest first, as the view’s operations panel shows them: key, label, steps ({done, total} when the work counts them; percent: they are bytes, a file read or written), queued, cancelling, cancellable, next (on the row of an agent batch: the names of its steps still to come, at most 12, which the row shows as a tooltip); each design change held behind the agent’s running one is a queued row of its own, which the X cancels (it is answered as cancelled and never runs); those that ended in the last minute (finished, newest first, at most 16): key, label, outcome (done, failed with its error, or cancelled) and ms_ago (one done within the app’s wait was never under way, and isn’t listed); and whether the panel is shown. With cancel: cancels that operation as its X does (its work stops at its next check and nothing of it is applied; operations queued behind it go on); a save stops unless it is already replacing its file, and leaves the file, its backup and the design’s unsaved changes as they were (it then ends cancelled); an error when no such operation is under way or it can’t be cancelled (an opened design’s bodies being meshed)
ui.filepath; insert?: boolstarts an interactive background Open or Insert; poll ui.inspect.file_busy for completion; the window stays responsive
ui.homeopen?: bool; sample?: indexthe start page: show or hide it, open a built-in sample (once built); returns open and the recent designs
ui.shortcutcommand, key?, slot?, replace?a command’s keys (key: the first, shown in menus; keys: all), or rebinds it: key becomes its only key, or with slot replaces its key there (one past the last adds a key, "" there removes that key); "" without slot clears them; key names are case-insensitive and modifiers are normalized (shift+ctrl+t becomes Ctrl+Shift+T); unknown and reserved keys are refused; a key another command uses is refused unless replace
ui.helpitem?: about, updates (checks for updates and shows the result), shortcuts (read only), report (opens a report draft for review), crashes (the Crash Reports window), docs (the documentation in the browser), messages (the Messages window), install_cli (macOS: links solidcraft-cli into /usr/local/bin); another item is an errorthe version, commit, build date and diagnostic text
ui.prefsany of the ui.prefs settings below; also open, button and stand_up (listed there)sets preferences (kept between runs), returns them all plus dialog (the open page or null), apply_enabled (whether the dialog’s Apply has a change to keep), dark, viewport_dark (whether the 3D view is dark), stand_up_offer (the offered bodies, or null), stand_up_note (whether that offer is the note), theme_followed, theme_follow_error, theme_option_defs, theme_option_values and theme_option_notices
ui.captureaction?: capture, revert, cancelanswers the Capture Position question (asked when a command starts or the design is saved while components were moved without capturing); without action, what waits
ui.recoveraction?: recover, discard, later; id? (a design’s id; default: the first one listed)answers the “Recover unsaved design?” prompt as its buttons do: recover opens that design’s last autosave (in the background; poll ui.inspect.file_busy; refused while the open design has unsaved changes), discard deletes it (doc.recovery_discard), later closes the prompt and keeps them all for the next launch; a non-string id is refused, and so is recover while 32 designs are open and the active design is not blank (the entry stays offered); returns the prompt as ui.inspect reports it (dialog, null when no designs remain) and file_busy. Without action, returns only the current dialog
ui.windowaction: minimize, maximize, restore, toggle, closewhat the title bar’s caption buttons do (on Windows and Linux the application bar is the title bar); close asks about unsaved designs first
ui.agent_cursorshow (bool), speed: a number from 1 to 5 (times the normal speed) or "instant" (no animation: the command runs at once and the pointer jumps to where it acted), follow_camera (bool); all optionalthe settings. While the cursor is shown, engine.execute first glides to the command’s toolbar button and to each pick, then runs and replies; later requests wait their turn. The pointer shows the command’s localized name, including commands inside a closed toolbar group; an animated batch names each command it visits. It also follows the pointer input sent with ui.move, ui.click, ui.drag and ui.scroll, as each event reaches the app, and a press leaves a ripple. "animate": false on a call skips it
engine.execute → view.layoutmode: floating or compactApply and save the workspace layout without opening a dialog, changing the design, resetting the camera or cancelling its active tool. General preferences expose the same choice; ui.prefs {"layout":"compact"} also saves it.
engine.execute → browser.expand_allpath?: occurrence ids; component?: id; omit both for the whole treeOpen every Browser row under that scope, including folders that start closed (Origin, Named Views, Features). Display only: not saved in the design, not an undo step.
engine.execute → browser.collapse_allsameClose those rows. The design row stays open when the whole tree is collapsed.
engine.execute → view.history_layoutwidth?, dock?: bottom, left, under_browser, browser, collapsed?Update the History panel in the shared layout without changing the design. browser shows features in the Browser in place of a separate panel.
ui.agent_chatfeatures?, toolbar_button?, connect_at_start? and open_at_start? (connect the agent started last, and open the panel, when SolidCraft starts; both off by default, never with --control), instructions? (the custom instructions every new chat’s system note carries, Preferences → AI; at most 4000 characters are kept), permissions? (ask, edits, all), add_agent? ({name, kind: acp, api or anthropic (api is OpenAI-compatible, anthropic Anthropic-compatible), command or base_url + model + request_timeout_s? (seconds one model request may take, 300 by default, rounded and clamped to 30 to 3600), allow_insecure_http? (OpenAI-compatible endpoints only; false by default; explicitly permits sending the key over unencrypted HTTP to this endpoint, without opening a confirmation dialog)}; no keys), add_dialog? (installed, custom, api, anthropic, or false to close), edit_dialog? (the id of an agent you added: the same dialog filled with what it holds, as its Edit button opens it; the key is never shown), default_agent?, agent? (the conversation’s agent, by id), mode? (the agent’s own mode), provider? (which provider’s group the model picker opens first, when the models come from several), model? (one of its models, or see below), open?, toggle? (true: show or hide the panel as its button does, unfolding a folded one), side? (left or right), minimized?, width? (placement of the open shared Assistant), popped? (in its own window), picker?, refresh?, connect?, stop?, attach?: a file’s path (a picture, a text file or a CAD file, for the next message; CAD files are imported at once), remove_file? (index of a file on the next message), send?: text (files alone make a message too), send_input? (true: send what is in the message box, as Enter does), attach_view?, attach_selection?, pick? (true: open the context picker), pick_at? (with pick: the character of the message box its pill goes to; the end by default), permission?: an option id or "cancel" (the newest question of the agent’s own), approve?: true, false or "always" (the oldest request held for Allow/Deny), cancel? (Stop: the queue stays), send_now? (stop the running step, then send the queued messages), remove_pending? (index), new_chat? (clear the chat, new session), undo? (the last message’s changes)the chat’s state: features, agents (found and added), currentAgent, state (off, starting, ready, busy, auth, exited, failed: the agent never got ready, and Connect reads Retry), connectAtStart, openAtStart, instructions, lastAgent (the agent started last), popped, window (its place and size), agent, modes, mode, models, model, modelWarning (the model capability warning text, or null when unknown or tool support is reported; checked asynchronously and never blocks sending), turnWarning (the latest completed turn’s no-tool notice, otherwise null; cleared on the next turn or new chat), providers (name and count of each, empty with one provider), provider (the one listed), authMethods, toolsPort, pid, waiting, turn, pending (queued messages, with their files), input (the message box’s text, picks as «n: …» marks), pills (what the marks in the box stand for), picked (the same items as one list of label and detail, as before the pills), attachments (the files on the next message, by name), loading (pictures still being read for it, by name), stopped, detection and the conversation (items). With features off only the settings apply. Desktop only; see Agent chat
ui.resizewidth, height
app.quitforce? (quit without asking about unsaved designs)quitting (the window closes), asking (a “save changes?” prompt is open)

dialog (in ui.inspect, ui.start and ui.editFeature) is null, the recovery prompt described above, or the open feature dialog as an object that doesn’t change with the form’s internals:

{"command": "solid.extrude", "title": "EXTRUDE", "editing": null,
 "inputs": [{"label": "Profiles", "items": [{"type": "profile", "sketch": 10, "index": 0}], "active": true}],
 "error": null,
 "commands": [{"command": "solid.extrude", "params": {"sketch": 10, "profiles": [0], "distance": "10 mm",
               "direction": "positive", "operation": "join", "exact_regions": true}}],
 "waiting": null}

command is the command the dialog runs (null for a few forms that run none), title its header, editing the feature id an edit rebuilds, inputs its selection inputs (active: the one picks go to), error the refusal it shows. commands is what OK runs now, with the parameters as that command takes them (an edit runs timeline.redefine with the feature’s command and parameters inside); when OK can’t run yet, commands is null and waiting says what it waits for (select profiles first).

For an ACP agent advertising configuration options, ui.agent_chat also accepts {"config_option": {"id": "effort", "value": "high"}}. Use an option ID and choice value from its reported configOptions; IDs and available values belong to the agent. The reply’s configOptions is null for a legacy agent, or the current list of supported select options (including an empty list when none remain). configPending is true while a selection waits for the agent’s reply. Read the confirmed state after that reply; submitting a change is not its confirmation. The existing model and mode inputs use the corresponding configuration option when advertised, and retain their existing behavior for agents using legacy lists or API endpoints.

model may also name a model the agent did not list, as the picker’s custom row does: it goes to an API endpoint, to an agent with a model option, and to a legacy agent that listed models; the reply’s customModels says whether this agent takes one. Otherwise it is ignored. A model an agent refuses leaves its previous model and adds a notice. modelsNote says why an endpoint’s models could not be listed, or is null.

ui.prefs settings

render_previous_geometry_budget (Graphics, default false) restores the previous fixed Render preview geometry budget: 512 MiB on 64-bit desktop, 128 MiB elsewhere. With it off, desktop uses the device-based budget.

One setting per line, sorted by name. A change that adds a setting adds its own line in order, so two such changes only meet when their settings are neighbours.

  • angle_decimals
  • assembly_move_handles (default true): explicit Assembly Move uses the permitted joint-motion handles; false keeps the previous Move panel.
  • auto_operation (default true: feature windows choose Join, New Body or Cut from what their material touches until one is picked; false: each keeps the operation it opens with, Extrude its own Join or Cut)
  • background_image (string, default empty: a picture’s file, shown behind the model and covering the window from its centre).
  • browser_bodies_folders (several (default): a component with one body and no groups lists it straight under the component in the browser, without a Bodies folder; always; never: components list their bodies and groups straight under them, and the folder’s menu items are on the component’s menu; the design’s own Bodies folder, and one hidden with its eye, always show; #4217)
  • browser_components_open (default false: each component placement in the browser starts closed until it is opened, by its fold arrow, Expand All or Find in Browser; true: every placement starts open, as before #9274)
  • browser_eye_left (default false: each browser and History tree row’s eye, handle eye:<name>, sits in a column at the rows’ right end, clear of the fold arrow and the radio; true: between them, as before #8718)
  • check_updates (default true; updates)
  • color_vision (off (default), protan, deutan, tritan: colour vision modes, #778)
  • constraint_glyph_scale (1…3, default 1: how large the open sketch’s constraint glyphs are drawn, a multiple of their 15-pixel badge, letter and icon included; Preferences › Sketch › Constraint glyph size, #10794)
  • default_units
  • dialog_buttons (bottom (default), top: a feature dialog’s OK, Cancel and Apply sit in its title row instead of under its inputs, #6708; the buttons keep their handles, and dialog:close is then Cancel’s; the Sketch panel’s Finish Sketch row moves above its options, #9860)
  • dimension_colour (null (default) or [r, g, b] numbers 0…255: the colour a sketch dimension draws its lines, arrows and value in; null uses the theme’s dimension colours. Selection, hover and conflicts keep their own colours, #14121)
  • dimension_input (view (default): a sketch dimension’s value is typed in a box on the dimension; window: placing a dimension with the Dimension tool, or double-clicking one, opens a small window at it with the value field (handle dimwin:value), arrows that step the value (dimwin:up, dimwin:down), a check (dimwin:ok, also Enter) and a cross (dimwin:cancel, also Esc), #7246)
  • dimension_step_deg (default 1, 0.001…180: what the dimension window’s arrows, Up/Down and the wheel over its field add to an angle, in degrees)
  • dimension_step_mm (default 1, 0.001…1000: what the window’s arrows, Up/Down and the wheel over its field add to a length, in millimetres, written in the unit the value is written in)
  • dimension_text_scale (default 1, 0.5…2.5: how far a sketch dimension’s text is scaled; it applies in the open sketch and to Show Dimensions, #14121)
  • escape_twice_finishes_sketch (default true: in a sketch, two quick presses of Esc (0.4 s apart or less) with no tool or dialog left to cancel finish it, as before; false: Esc only cancels the tool, dialog or selection and the sketch stays open until Finish Sketch, #12069)
  • export_name (string, default empty: each export offers the design’s name, or a mesh its body’s; otherwise the name File › Export and Save As Mesh offer, with {design}, {body} (the exported body’s name) and the design properties ({project}, {part_number}, {revision}, {author}, {organization}, {description}, {license}) replaced; export file names)
  • extrude_arrow_face_stops (default true: dragging Extrude’s distance arrow stops at flat faces parallel to the profile that lie over it, takes their exact distance and outlines them, #2398; false: the drag moves in round steps only, as before)
  • extrude_auto_to_object (default true: in the Extrude dialog, with a profile picked, a face, vertex or parallel plane picked off its plane switches the extent to To Object, unless the Profiles box was clicked into; it applies to Extrude dialogs opened afterwards)
  • fillet_legacy_radius (default false: a new Solid Fillet opens at the radius the tool ships with, 2 mm, shown in the design’s units (“0.07874 in” in an inch design); true: it opens at the number 2 read in the design’s units (“2 in”), as before, #12418)
  • forget_stand_up: true (action: forget all remembered stand-up answers; also available beside the memory checkbox in Preferences › Design)
  • frame_rate_details (default false: with the frame rate, a frame-time graph of the frames drawn with the triangles over it, a frame-time histogram and what the GPU drew; clicking the readout switches it)
  • graphics_adapter (exact card name, or empty for Automatic; applies after restart; WGPU_ADAPTER_NAME takes precedence)
  • graphics_backend (native Graphics API: auto by default; Windows: dx12, vulkan, gl; macOS: metal; Linux: vulkan, gl. Saved before graphics initialization and applied on restart. Existing manual comma lists are preserved until an explicit API change. {"button":"restart_graphics"} asks about unsaved designs, then restarts with the saved API; unavailable in the browser or under an explicit launch override.)
  • ground_shadow_follows_model (Graphics, default false): raised models cast their ground shadow on the origin ground plane; true restores the shadow below the lowest visible body. Both modes keep the shadow below bodies extending beneath the origin, and follow the selected up axis.
  • hidden_toolbar_tabs (default []: all existing tabs, including Sheet Metal and Weldments, stay visible; lists tabs to hide, each once, e.g. ["SURFACE", "SHEET METAL"]; hidden tabs stay hidden regardless of the design’s features; ribbon layout)
  • history_in_browser (default false): list each component’s features in the Browser and hide the separate History panel.
  • history_sketches (in_order (default), under_feature: vertical History lists a sketch under the first feature built from it, #6489. The bottom strip keeps creation order.)
  • hole_group (default false: the holes one Hole command makes, two or more, go into a feature folder in History, closed at first)
  • hole_moves (default false: a placed Single hole has a drag handle (hole:<n>), and a pick on a face that has a hole moves it there)
  • hole_several (default false: a face takes several Single holes; a pick elsewhere on a face that has a hole adds another, and a pick on a hole takes it out)
  • hole_snaps (default false: a Single hole snaps to its face’s middle and round edges’ centres)
  • home_fit_size (0.1…1, default 1: how much of the view the model fills when Home, Fit, an opened design or a navigator view change frames it, with navigator_fit on or off; 1 fills the room between the panels, the compass and the navigation bar, as before; #10091)
  • joint_drag_trackball (default true: dragging a part that a ball joint holds to a part fixed in place turns it by the smallest turn about the joint, like a trackball the size of the part; false: the grabbed point follows the pointer and the part may spin about it, as before #13262)
  • joint_rotation_coarse (default 10°): coarse Assembly Move rotation step, in 0.001…180°.
  • joint_rotation_fine (default 0.1°): fine Assembly Move rotation step, in 0.001…180°.
  • keep_hover_while_navigating (default true: the face under the pointer keeps its highlight while the view moves under it; off: the highlight goes off while the view moves, as before, #2130)
  • keep_unsaved_also_asks (default true; web app, with keep_unsaved_in_browser on: the browser’s “leave site?” box still appears when the tab closes with unsaved work; off, it appears only while a copy isn’t current yet, #10220)
  • keep_unsaved_in_browser (default false; web app: each open design’s unsaved changes are kept current in the browser’s private storage, and the next visit offers them back in a “Restore unsaved design?” window with Restore, Open saved version and Discard; off, nothing is kept, #10220)
  • keep_work_on_tab_switch (default false: each design tab keeps its open dialog or tool, with what was picked and typed in it, and its toolbar tab while another design is shown, and has them back when it is shown again; off, a switch cancels them and shows MODEL, #9360)
  • language (en, ar, ja, de, fr, he, ko, ms, ru, uk, zh; see localization)
  • length_decimals
  • line_drag_draws_one (default false: in the Line tool, a press, a drag and the release draw one line from the press to the release and end there, snapping as clicks do; clicks still chain, and a drag under 24 points is a click; off, a drag places a point where it is let go, #7247)
  • line_hover_tangent_arc (default false: in the Line tool, after a segment, moving away from the chain’s last point and resting back on it for about a third of a second makes the next click a tangent arc, and the same again goes back to lines; off, resting there does nothing, #9489)
  • logo_accent (bool, default false: the application bar’s logo in shades of the theme’s accent instead of the theme’s logo colours)
  • look_at_anim
  • look_at_speed
  • max_design_file_mb (default 256): the largest design file opened, in MB, from the default up to 16384 (16 GB) (Preferences › General › Largest file to open, #4086; a --max-file-size launch flag overrides both for that run; the web app keeps the defaults and refuses other values; large files)
  • max_step_file_mb (default 512): the largest STEP or import file opened, in MB, from the default up to 16384 (16 GB) (Preferences › General › Largest file to open, #4086; a --max-file-size launch flag overrides both for that run; the web app keeps the defaults and refuses other values; large files)
  • maya_horizontal_zoom: Maya Alt+right drag zooms horizontally (right in, left out), default true; false restores vertical drag zoom. Other presets and the fixed Zoom tool are unchanged.
  • mcp_compact (default false): compact tool output for agents bridged to this app through the MCP server (Preferences › AI › Agents through MCP; the server reads it with mcp.options; MCP compact output)
  • middle_double_click (home (default), fit, off)
  • msaa
  • nav (fusion, solidworks, creo, inventor, alias, maya, blender, tinkercad, openscad, touchpad)
  • nav_bridge_enabled (default false) and nav_bridge_port (default 8765: a device’s own app on this computer moves the view, the navigation bridge)
  • navigator_arrows (default true: the custom view navigator has the cube’s orbit and roll arrows around its body; also view.navigator {arrows})
  • navigator_fit (default false: a view chosen on the view cube or the axis gizmo also looks at the middle of the model from the one distance that fits its bounding sphere in the free part of the view, at home_fit_size; Home and an opened design use the same zoom and centre while it is on, so views chosen after them only turn; #10091)
  • navigator_home (default true: the custom view navigator has the cube’s Home button beside it; also view.navigator {home})
  • navigator_style (axes (default), cube, custom: the body chosen with view.navigator, the cube until one is chosen)
  • navigator_texture (themed (default): the custom view navigator in the cube’s colours; modeled: in its body’s own appearance and edge colour)
  • offer_stand_up
  • orbit_around (view_centre (default: what Pan moves and Fit frames), origin (the world origin stays still on screen), cursor (the model point under the pointer when the drag began); the older orbit_cursor boolean still sets cursor when true)
  • orbit_cursor_wrap (default true: an orbit drag that reaches the edge of the view moves the cursor to the opposite edge and keeps turning)
  • orbit_reverse (default false: Shift with two-finger scrolling orbits the other way)
  • origin_axes_across_view (default false: short axes as before; opt in through Preferences → Design → Origin or the bottom bar’s Display menu. The command view.origin_axes_across_view {enabled: bool} sets the same preference. When true, the X, Y and Z origin axes run through the origin to the edges of the view, both ways, #11123; away from the origin they are picked only in an open sketch or by a dialog input that takes axes, after anything else under the pointer, and not where a body hides them; false: short axes from the origin, solid then dashed, as before)
  • origin_plane_names (xyz (default) or top_front_right: the browser, dialogs and lists call the origin planes Top, Front and Right; their ids, commands and saved designs keep XY, XZ and YZ)
  • pan_reverse_x (default false: horizontal scrolling pans the other way)
  • pan_reverse_y (default false: vertical scrolling pans the other way)
  • parameters_apply_live (default true: the Parameters dialog applies an expression when its field is left; false: edits wait for Apply or OK, which set them together in one rebuild and one undo step)
  • perspective_fov (Perspective view’s vertical field of view in radians, 0.2618…1.5708 (15°…90°), default 0.6; when it changes, the camera’s distance scales so the view keeps its size)
  • placement_handles (default false: handles on the preview of a Rest, Boss, Snap Fit, Box, Cylinder, Cone, Sphere, Torus or Coil that move it across the model and turn it where it can turn)
  • print_face_highlight (0…1, default 0.2: how strongly a print face is tinted and outlined in the view, #8429; 0 draws no highlight)
  • quick_measure (default false: the selection’s length, area or distance in the view’s bottom right corner, the rect measure:quick; #7000)
  • remember_stand_up (default true: reuse the stand-up or Leave it answer per SHA-256 of the original file bytes, at most 500 answers with oldest-first eviction; false restores asking on every open)
  • render_legacy_memory_admission (Graphics, default false): Use previous render memory checks. True skips native available-RAM/cgroup fit refusals for future CPU/GPU admissions; shared/host software limits, device limits, checked allocation and all held reservations remain enforced. It is a machine preference, not saved image quality. The CLI equivalent is solidcraft-cli render --legacy-memory-admission.
  • reuse_imports (default true: reopen unchanged STEP and IGES files using the local saved-model cache; false imports each time). doc.open {reuse_import: false} bypasses reading and writing this cache for one open; its reply includes import_reused and content_sha256. file.import_cache_clear {} removes cached imports and returns {removed}, keeping stand-up answers. SOLIDCRAFT_IMPORT_CACHE_DIR overrides the default imports folder under SolidCraft’s per-user data folder; SOLIDCRAFT_NO_MODEL_CACHE also bypasses import reuse.
  • ribbon_menus_follow_hover (default false). Click a ribbon panel label to open its menu. Hover another panel label to switch, or move outside the ribbon and open menu to close it.
  • ribbon_reveal (default false: hovering a result in Search commands switches the ribbon to the command’s tab and marks its button, or opens the panel drop-down that holds it on its row, handle ribbon:reveal; leaving the results goes back to the tab before; a command started from the search blinks there, and a running command from a drop-down marks its panel label, ribbon:running_section:<TAB>:<SECTION>, #7287)
  • right_drag_orbits (default true; false: a right drag the mouse preset would orbit leaves the camera alone, #6492)
  • save_prompt_one_at_a_time (default false: closing several tabs or quitting with more than one unsaved design asks once, in one “Save changes to N designs?” list; true restores one “Save changes?” prompt per design, #11680)
  • select_hidden_edges (default false: v0.30.1 visible-only edge picking; true allows ordinary edge picks through faces in Shaded with hidden edges, including Fillet inputs; hidden bodies and section-clipped edges stay excluded)
  • select_other_hidden (default true: Select Other includes hidden geometry near the pointer; false restores the original visible-only list under the pointer)
  • show_frame_rate (default false: frames per second in the view’s bottom-right corner, counting the frames the view draws anyway; it reads 0 FPS once the view is idle)
  • sign_saved_designs (default false: every save from the app adds a GnuPG OpenPGP signature to the design’s provenance; file.save’s sign is accepted only from the app’s own Save, because a script or agent must not make GnuPG sign with the user’s keyring; the key may be on a smartcard, where GnuPG uses it and asks for its PIN; signing)
  • signing_key (string, default empty: the GnuPG key a signed save uses, a fingerprint or user id; empty asks GnuPG for its default signing key)
  • sketch_auto_dimensions (default false: each shape drawn in a sketch gets all its dimensions at the sizes drawn, as sketch.draw.dimensioned with as_drawn; typed values win)
  • sketch_auto_locate (default false; with sketch_auto_dimensions, new shapes are also placed from the sketch origin until fully constrained, as sketch.draw.dimensioned with locate)
  • sketch_conflict_colour (default true: while the open sketch’s constraints conflict, the curves and points a failing constraint holds are drawn in the theme’s sketch_conflict colour too, not only its glyph or dimension; false: only the glyphs and dimensions are red, in error, as before, #7244)
  • sketch_construction_lines (default true: show the Construction Line tool family in Sketch CREATE; false restores the earlier menu)
  • sketch_exit_view (keep (default: the view stays where it is when a sketch ends), previous (back to the view from before the sketch), unless_rotated (back unless the sketch view was turned))
  • sketch_mirror_auto_line (default true: opening Sketch Mirror uses the only preselected straight construction line of the active sketch as its reference; unselected lines do not count; false keeps manual reference selection)
  • sketch_open_look_at (default true: creating or editing a sketch turns the view to face it; false leaves the view as it is, and Look At faces the sketch when asked)
  • sketch_recent_snap (default false: while drawing, the cursor snaps where the horizontal guide of one recent circle or arc centre or straight-edge midpoint meets the vertical guide of another; a free coordinate, with no constraint and no construction geometry. Off: those guides stay visual and the cursor does not move. The references come from sketch_tracking_guides, #8694)
  • sketch_snap_to_visible_grid (default true: a point in a sketch snaps to the crossings of the lines the grid draws when the pointer means one, and to the finer lattice between them otherwise; false: the point snaps to the finer lattice only, as before, #7114)
  • sketch_state_icons (default true: a sketch’s icon in History and the browser takes the colour of its solve state, sketch under constrained, sketch_fixed fully constrained, error while its constraints conflict, and its tooltip names the state; false: every sketch row’s icon and tooltip as before, #7245)
  • sketch_tab_always (default false: the SKETCH tab stays in the ribbon right after MODEL while no sketch is open; a tool in its CREATE or REFERENCE section then opens Create Sketch, on the selected plane or planar face or else on the plane picked next, and starts in the new sketch, #7904)
  • smooth_wheel_zoom (default true)
  • space_mouse_model_pivot (default false: Classic orbit around the view’s centre; true opts into Traced orbit around the model for the 3D mouse and navigation bridge)
  • space_mouse_sketch_orbit (default false: the 3D mouse and navigation bridge pan and zoom a sketch seen square on and turn it only after the view is tilted; true lets them turn it too, as the mouse can)
  • stand_up_unknown (note (default: a file whose header names no program known to save Y up and whose bodies seem to lie on their back (their flat area across Y outweighs their flat area facing down Z) gets a small note over the view, “Did this part open the wrong way up?”, with Stand it up and Don’t ask again as the handles stand_up:stand and stand_up:never, and its close (×) as toast:stand_up:close (also stand_up:dismiss); it goes by itself after 10 seconds and never blocks input), ask (the “Stand this part up?” dialog for every file from another program, SolidWorks too, without looking at how it rests, as before #7717), off (no offer); otherwise a SolidWorks file gets the dialog, or the note when its bodies clearly stand on Z already)
  • status_bar (default false: a thin bar along the window’s bottom edge, below History, handle status:bar: what is being edited (status:editing), the open sketch’s state in its colour (status:sketch) and the design’s units (status:units, a click opens the Units menu, whose rows run document.units); the sketch’s state then leaves the view’s top-left corner, #7243)
  • step_values (default false: Up/Down and the wheel step a plain number in a focused value field)
  • tab_double_click_renames (default true: a double-click on the selected design’s tab edits its name in the tab, handle tab:rename; Enter or a click elsewhere keeps the name through component.rename with component: 0, Esc cancels, and a name left as it was or blank changes nothing; false: a double-click on a tab maximizes the window, as the bar’s empty space does, as before)
  • theme (dark, light, system or a theme file’s id: a built-in one such as gruvbox-dark, or an imported one; the reply’s theme_ids lists them)
  • theme_export: true (the reply’s theme_file is the active theme, including a followed file; themes with options retain their authored definitions, defaults and overlays, while older themes keep the complete snapshot shape; saved option values are excluded; see colour themes)
  • theme_follow (desktop only, default "": a solidcraft-theme/1 file’s path to follow as the theme, reloaded within about a second whenever it changes and when the window gains the focus; the file is read at once, and a file that doesn’t read keeps the last good theme; "" stops following, as does setting theme; in the browser it is an error; the launch flag --theme-file PATH follows a file for one run without saving it; the reply’s theme_followed is the path followed now or null, and theme_follow_error why its last read was not applied, or null)
  • theme_import (a solidcraft-theme/1 file’s JSON or text: adds the theme and turns it on; the reply’s theme_imported is its id)
  • theme_options (default {}): a map of theme id to option-id/value map, merged per theme; {"theme_options":{"my-theme":{"enabled":true,"amount":5}}} changes those options, null resets one option, and an empty map resets that theme. Unknown themes or option ids, wrong types, unknown choices and out-of-range numbers refuse the entire call and name the key, before imports, removals, dialogs or other preference effects occur.
  • theme_remove (an imported theme’s id; also removes its saved option values)
  • timeline_origin_bodies (default false: the History on the left or under the browser starts with the active component’s Bodies folder and the Origin, the browser’s rows with their eyes, folds and menus; the bottom History is unchanged)
  • toast_place (corner (default: notices and offers stack in one column in the view’s bottom-right corner, each 360 points wide, buttons right-aligned with the main action right-most, #7893), centre (at the bottom centre above the view toolbar, each as wide as its text, as before))
  • trackpad_pan (default true: two-finger scroll pans, except Touchpad always zooms; pinch zooms)
  • trackpad_zoom_modifier (off default, cmd, alt, or ctrl: hold this key alone for vertical scroll to zoom)
  • trusted_signing_keys (string, default empty: fingerprints of signing keys whose signatures the app shows as verified, separated by commas; spaces inside a fingerprint, as gpg --fingerprint prints them, are ignored; any other valid signature is shown in grey with its fingerprint and names no user id; a user id inside a file is not proof)
  • ui_font (an installed font family or a font file for the interface; empty for the built-in font; a Family-Weight.ttf file also takes its heavier sibling files for bold text, see interface fonts)
  • ui_scale
  • up_axis (z, y)
  • verify_opened_designs (default false: opening a design verifies its signature and reports it, doc.open {verify: true}; off: opening does not read every payload again, and file.verify checks on request)
  • view_confirm_corner (default false: while a dialog or a sketch is open, a check and a cross at the view’s top right, handles view:confirm:ok and view:confirm:cancel, do what the dialog’s OK and Cancel do, or Finish Sketch and Cancel Sketch; the axis view or cube moves down below them, #6709)
  • viewport_theme (theme (default: the theme’s own), light, dark: the 3D view in the Light or Dark theme’s colours whatever the window uses, #4146)
  • wheel_zoom_speed (0.25 to 4, default 1: how far each wheel step zooms; the 3D mouse keeps its own speed)
  • window_system (Linux: auto (default: XWayland whenever there is an X display, native Wayland on niri and Hyprland), wayland, x11; saved in graphics.json, applies at the next start; SOLIDCRAFT_WAYLAND overrides it)
  • zoom_reverse
  • zoom_stop_at_model (default false: the 3D mouse and navigation bridge stop zoom at the surface in the middle of the view; drag-zoom holds its press point; independent of zoom_to_cursor)
  • zoom_to_cursor

The built-in gruvbox-dark and gruvbox-light themes each expose the contrast choice (soft, standard default, hard). For example: {"theme":"gruvbox-dark","theme_options":{"gruvbox-dark":{"contrast":"soft"}}}. The former gruvbox-dark-soft, gruvbox-dark-hard, gruvbox-light-soft and gruvbox-light-hard ids remain accepted for compatibility and select the matching family and contrast; replies and theme_ids expose only the two canonical families. An explicit canonical option change or reset in the same call wins over the legacy selection. Legacy option-map keys are also accepted; canonical keys take precedence when both occur in a call. Imported themes cannot claim the retired built-in ids.

Theme option values persist per theme, including a followed file’s own id, through the same preference store in the browser and on desktop. Switching themes restores their saved values; Interface’s Restore page defaults clears them. Preference exports and settings profiles carry values; theme_export carries the author’s defaults. Saved entries that no longer fit a definition remain stored and use the default with a notice; unknown ids have a notice and no effective value.

For the active theme, theme_option_defs lists the declared options in ordered_options order: id, type, default, type-specific min, max, step, unit or choice values (each with id and label), parent, advanced, section, label, help, standard and available. Labels, help and choice labels use the current language with English fallback. Unavailable definitions include reason; standard options currently have available: false and reason: "not implemented". theme_option_values contains the effective values, including defaults and values of inactive children. theme_option_notices contains {id, kind} entries, where kind is unknown_option, wrong_type, unknown_choice, non_finite or out_of_range. Without an active theme file these fields are [], {} and []. Portable input and output schema types live in solidcraft_engine::schema::ui_prefs (PrefsParams, PrefsReply).

Other ui.prefs parameters:

  • open opens Preferences at General; Look: Appearance, Layout, Graphics; Work: Design, Sketch, Tools; Input: Navigation, 3D mouse, Shortcuts; or Extend: Assistant, Plugins. Names are case-insensitive. Legacy aliases remain accepted: Interface → Appearance, Interface/navigation bar → Layout, Keyboard Shortcuts → Shortcuts, AI → Assistant, and Selection, Sketch & selection or Sketch and selection → Sketch. Replies use the canonical page name. The corresponding legacy prefs:page:<name> scenario handles also remain aliases of the canonical page rows.
  • button: ok, cancel, apply, defaults
  • stand_up: true stands up a just-opened Y-up file, stand_up: false leaves it as it is (“Leave it”: the offer, dialog or note, closes and offer_stand_up stays on)
  • the dialog’s buttons are the handles stand_up:stand, stand_up:leave and stand_up:never

ui.workspace {workspace?: "design" | "render"} reads or changes the active workspace. Entering Render requires finishing the current modelling operation; returning restores the Design camera. See the integrated Render workspace.

ui.inspect.render.live reads automatic image feedback without launching or pumping work. It includes enabled/paused, pending/running, capture generation, isolated job, completed passes, target, sequence, current/stale image and error. These job identities belong to the live scratch session and cannot be exported or selected as completed results through the original document session. view.render_live {paused: true|false} pauses/resumes feedback through the registered command; its headless form validates and echoes the request without changing a document.

GPU adaptive checkpoints include optional preview.adaptive metadata in render.status and ui.inspect.render.live.adaptive: stage is pilot or production, completed and target count that phase’s iterations, and pilot_samples, minimum_production_samples, maximum_production_samples retain the captured allocation bounds. The compatibility field samples_per_pixel is an absolute completed pilot-plus-production iteration when this metadata is present; it represents uniform SPP only when adaptive is absent. The checkpoint target is the validated pilot count plus maximum production count. Pilot images never enter the final image or raw master, and production iteration counts are not uniform per-pixel sample counts.

Sketch alignment guides

ui.prefs {sketch_tracking_guides: true} enables temporary horizontal and vertical dotted alignment guides from hovered circle/arc centres and straight-edge midpoints (default true). The guide spans the reference and cursor projection, including positions inside a circle. The two most recent references are kept, and both guides can be drawn. A single guide does not move the cursor. Set the preference to false to hide the guides. Snap suppression (Alt, unless rebound) does the same for that gesture. Snapping the cursor to where those guides cross is separate, and off unless sketch_recent_snap is true. While a drawing tool waits for a point, sketch_tracking_points (default true) makes every shown sketch point other than the origin a reference too (line and arc ends, rectangle corners, free points); false keeps centres and midpoints only.

Continuous camera motion

Continuous camera motion is also available through engine.execute with command view.navigate: pass camera (from ui.inspect) and motion: "pan" | "orbit" | "zoom". Pan and orbit take delta: [dx,dy] in screen points (each within ±10000); pan also requires viewport: [width,height] (each 1..1000000). Orbit accepts a world-space pivot with the viewport size. Zoom takes factor (0.05..20, below 1 zooms in) and an optional world-space anchor. The returned camera is applied immediately by the UI, without animation, document edits, undo steps or a success-log entry per movement.

MCP

solidcraft-cli mcp --connect 127.0.0.1:PORT bridges an MCP client (an AI agent) to this channel; see mcp.md.

Example

{"id":1,"method":"engine.execute","params":{"command":"sketch.create","params":{"plane":"XY"}}}
{"id":2,"method":"engine.execute","params":{"command":"sketch.rectangle.two_point","params":{"p0":[0,0],"p1":[40,30]}}}
{"id":3,"method":"engine.execute","params":{"command":"solid.extrude","params":{"distance":20}}}
{"id":4,"method":"engine.execute","params":{"command":"solid.fillet","params":{"edges":[[0,0,10]],"radius":3}}}
{"id":5,"method":"ui.screenshot","params":{"path":"/https/docs.solidcraft.io/tmp/part.png"}}

Preferences → Navigation → View navigator offers Signed axes (the original default), Cube and Custom; a right-click anywhere on the navigator offers the same three. This choice is saved independently of the mouse preset. The control channel selects it with ui.prefs {"navigator_style":"axes"}, "cube" or "custom". Existing settings without this field keep signed axes. Signed-axis tips choose principal views; clicking an aligned tip again reverses the view.

Custom draws a body in place of the cube, at the cube’s size; a click on one of its faces looks straight at it, and with navigator_fit on it also frames the model, as the cube does. Picking Custom from the navigator’s menu opens the Custom View Navigator panel, with OK and Cancel: pick the body in the view, then its texture (themed: the cube’s colours; modeled: the body’s own appearance and the view’s edge colour) and whether the cube’s rotation arrows and Home button go around it. The navigator previews the panel’s choices while it is open. OK runs view.navigator, which scripts and engine.execute run too without opening anything: {"style": "custom", "body": "Body1", "texture": "modeled", "arrows": true, "home": false} (any subset; "body": null goes back to the cube) returns the whole navigator. A body over 10,000 triangles is refused, and so is a long or flat one: measured across the axes and the diagonals, its thinnest width must be at least a fifth of its widest. The body is kept as a snapshot with the preferences and in settings profiles (settings.profile.*, as view_gizmo), so it stays across runs and designs and travels with an exported profile.

Hold Shift during an orbit gesture to snap to the nearest standard or diagonal view. This applies to right-drag, the Orbit navigation tool, and dragging either view navigator. Under the default preset, Shift+middle also orbits with snapping; middle alone pans. Cube faces choose principal views, and corners choose isometric views. The middle of each cube edge belongs to its face. Face names follow the up-axis preference; the small X/Y/Z triad shows world axes. Clicking the same face keeps that view. The inward-pointing arrows orbit by 90 degrees; the curved arrows at the upper right roll the view by 90 degrees. The Home button at the upper left returns to isometric and the lower-right circle fits the model.

The cube is subdued while idle. Hovering its area brightens the cube and navigation bar and reveals the surrounding controls; the picked face or corner gets a pale blue fill and blue outline. Moving away restores the subdued state. Navigation-bar controls also brighten when hovered or when a navigation tool is active.

The cube has six face targets and eight corner targets, with no edge targets. Corner picking regions lie on the six flat faces, preserving sharp corners with no permanent subdivision lines. Hovering a corner highlights its parts on the adjacent visible faces together.

The cube widget occupies a compact 112-point square in the viewport corner. A straight-on cube face is approximately 55 points wide; painting and picking scale together. Face labels are stretched vertically. View clicks ease through intermediate orientations when Navigation animated view changes is enabled (the default), at the configured animation speed.

The cube runs view.navigate through the command engine. Programmatic callers pass camera and either direction: [x,y,z] in the camera’s up-axis frame or action (left, right, up, down, clockwise, counterclockwise, home, fit). fit: true with a direction or a turn asks for the whole model to be framed from the new direction as well, as home does. The result contains camera and fit; UI callers apply the camera with their animation preference, without editing the design. The Navigation preference navigator_fit (off by default) makes every view chosen on the cube or on the axis gizmo frame the model this way: looking at its middle from the one distance that fits its bounding sphere, which Home then shares.

Shift-orbit snapping includes 45-degree edge views and isometric corner views, with a lightly damped spring transition and a small boundary margin to prevent flicker.

ui.palette {text?, open?} opens command search under the ribbon search field; open: false closes it. ui.start {command: "ui.palette"}, clicking ribbon:search, and S open the same popup. This is UI routing: engine.execute continues to run commands without opening dialogs. The query is limited to 4096 Unicode characters, with at most 80 visible results.

shortcut:search and palette:search identify the editor; ribbon:search_popup and palette:outer identify the popup. Command rows retain menu:<command id>; design rows use menu:search:design:<index>. Up/Down choose an enabled row, Enter runs it, Tab fills its id or name, and Esc closes. Typing command.id {"parameter": value} validates JSON live and runs through the same engine entry as engine.execute. Shift+Enter runs JSON and keeps the query open. Invalid JSON stays editable and reports the parser’s error. Feature results open their existing edit tools; parameter results open Change Parameters filtered to that name.

Saving several designs before closing

ui.documents {"action":"close_all"} (or close_others with the tab index to keep, or quit) returns one save_prompt list when more than one target design has unsaved changes. Only tabs in the addressed window participate. Each row starts selected. Read with ui.documents {}; change all ticks and answer in one call: ui.documents {"save_indices":[0,2],"answer":"save"}. Indices must belong to the pending list; invalid lists return an error without answering. An empty list saves nothing and still closes every target, discarding their unsaved changes. Without save_indices, answer:"save" saves the current ticks.

Answer an individual row with ui.documents {"save_row":0} or ui.documents {"discard_row":0}, using its current tab index from save_prompt. Save asks for a location if needed and closes only that tab after a successful write; a cancelled picker or failed save leaves the row and prompt unchanged. Discard closes only that tab without saving. Remaining tab indices shift after a tab closes, so read the returned list before answering another row. The grouped prompt stays open even with one row left; answering the last row completes the pending close or quit.

Selected designs save in tab order through the existing interactive save flow; never-saved designs ask for a location. Cancelling a picker or failing a save stops the close and keeps every remaining tab open, including tabs already saved successfully. answer:"dont_save" discards all target changes and completes the close; answer:"cancel" keeps every remaining tab open. This is an explicit UI workflow; engine.execute save commands continue to require paths and never open pickers. With ui.prefs {"save_prompt_one_at_a_time":true}, the previous individual prompts are used instead (default: false). One unsaved target always uses the original prompt.

Reference tests

Generated by cargo xtask oracle. Each case in tests/oracle/ is a part described by our own recipe.json. SolidCraft replays the recipe through its command engine and compares the result with expected.json: body count, volume and area (relative tolerance 1e-3 unless the case states another), centre of mass and bounding box (within 1e-3 of the part’s size), face count where the geometry fixes it, and, where the case has a reference.stl, the sampled symmetric (Hausdorff) distance between the two surfaces and the principal second moments of both triangulations. Every rebuilt part is also exported as STEP, validated and read back.

Where the expected values come from (the Source column):

  • analytic: closed-form geometry worked out by hand (prisms, cylinders, cones, spheres, tori and Pappus’ theorems, fillets on straight edges with a constant radius or one varying along the edge, sheet-metal bends as annular sectors). The scripts in tools/oracle-analytic/ compute them and give the formulas.
  • freecad <version>: the same recipe built with FreeCAD’s public Python scripting API by tools/freecad-oracle/, an independent kernel run as an external program. FreeCAD is not a dependency of SolidCraft. Where the two kernels legitimately build a different shape (for example the corner patch where fillets meet), only volume and area are compared, within the tolerance the case states.

No expected value comes from a commercial CAD product.

90 / 90 cases pass; sources: 69 analytic, 21 freecad. 0 known failure(s) tracked in issues; 0 case(s) without expected values yet.

Validation note: Case 95’s analytic centre-of-mass reference was corrected after the above successful run. Its previous pass status is historical and requires a new oracle replay against the corrected reference.

CaseSourceResultSurface distance mm (max / mean)MethodNotes
01-boxanalyticpassbox 40 x 30 x 20 from the origin: V = abc, A = 2(ab + bc + ca)
02-cylinderanalyticpasscylinder r=15 h=50 on XY: V = pi r^2 h, A = 2 pi r^2 + 2 pi r h
03-box-through-holeanalyticpassbox 60x40x20 minus centred through cylinder r=6: V = abc - pi r^2 c, A = 2(ab+bc+ca) - 2 pi r^2 + 2 pi r c; hole is centred so the centroid stays at the box centre
04-revolve-vaseanalyticpassrevolve of a line/3-point-arc profile (XZ, x = radius, z = -sketch y) 360 deg about Z. Pappus/Green: V = 2 pi * int x dA, z_c = int x z dA / int x dA, A = sum over profile edges of 2 pi int x ds (bottom disk r20, frustum r20->25, torus zone of the arc (centre (-16.000000, 33.000000), R=41.109610), frustum r15->18, top disk r18)
05-taper-extrudeanalyticpasssquare frustum: base 40 at z=0, top a1 = 40 + 230tan10 at z=30 (positive taper grows outward); V = h/3 (A0 + A1 + sqrt(A0 A1)), z_c = h (A0 + 2 sqrt(A0A1) + 3 A1) / (4 (A0 + sqrt(A0A1) + A1)), sides = 4 trapezoids (a0+a1)/2 * h/cos(10)
06-symmetric-extrudeanalyticpass50 (x) by 20 (z) rectangle on XZ extruded +-15 along Y: box 50x30x20 centred at origin
07-fillet-all-edgesanalyticpassbox 40x30x20 with every edge rounded r=3 = inner box (34x24x14) grown by a ball r (Steiner): V = abc + 2r(ab+bc+ca) + pi r^2 (a+b+c) + 4/3 pi r^3, A = 2(ab+bc+ca) + 2 pi r (a+b+c) + 4 pi r^2; faces 6 planes + 12 quarter cylinders + 8 sphere octants
08-chamferanalyticpassbox 40x30x20 with the 4 top edges chamfered 2: lower box 40x30x18 + top slab with section (36+2u)x(26+2u) at depth u (V, moment by exact polynomial integration); A = bottom + sides(h=18) + top 36x26 + 4 trapezoid bevels of slant 2 sqrt2
09-shellanalyticpass50x40x30 box shelled inward 2 with the top open: box minus cavity [2,48]x[2,38]x[2,30]; A = outer bottom + outer sides + rim + cavity floor + cavity walls
10-rect-pattern-holesanalyticpassplate 90x70x5 minus 12 through cylinders r=3 at (15+20i, 15+20j), i<4, j<3: V = abc - 12 pi r^2 t, A = 2(ab+bc+ca) + 12(2 pi r t - 2 pi r^2)
11-circular-pattern-flangeanalyticpassannulus R50/r15 h10 minus 6 through cylinders r=5 on a 40 radius: V = pi(R^2-r^2)h - 6 pi rh^2 h, A = 2 pi(R^2-r^2) + 2 pi(R+r)h + 6(2 pi rh h - 2 pi rh^2); 6-fold symmetry keeps the centroid on the axis
12-mirror-featureanalyticpassblock [-30,30]x[0,30]x[0,10] + two bosses r=6 h=15 at (+-15,15) on top (mirror across YZ): V = abc + 2 pi r^2 h, A = 2(ab+bc+ca) + 2 * 2 pi r h
13-combine-joinanalyticpassbox [0,40]^2x[0,20] union cylinder r=12 at (40,20) z 0..40: V = box + half-cylinder (x>40, z<20, centroid 40 + 4r/3pi) + full cylinder z 20..40; A = bottom (1600 + pi r^2/2) + box top (1600 - pi r^2/2) + cyl top + 3 full sides + x=40 side minus 24x20 + cyl side (pi r 40 outside half + pi r 20 upper inside half)
14-combine-cutanalyticpassbox [-25,25]^2x[-20,0] minus sphere r=15 at the origin (lower hemisphere removed): V = abc - 2/3 pi r^3, hemisphere centroid z = -3r/8; A = 2(ab+bc+ca) - pi r^2 + 2 pi r^2
15-combine-intersectanalyticpasscube [-20,20]^3 intersect sphere R=25: sphere minus 6 disjoint caps of height h=5 (caps cannot overlap: two would need x^2+y^2 > 800 > R^2); V = 4/3 pi R^3 - 6 pi h^2 (3R-h)/3, A = 4 pi R^2 - 6 * 2 pi R h + 6 pi (R^2-20^2)
16-sweepanalyticpasscircle r=3 swept along line 30 (+Z) + tangent R20 quarter arc + line 30 (+X): two cylinders + quarter torus (Pappus: V = pi r^2 R theta, A = 2 pi r R theta; torus centroid at rho = (R^2 + r^2/4)/R * sin(theta/2)/(theta/2) from the bend axis along the bisector) + 2 end disks
17-loftfreecad 1.1.4pass0.013 / 0.0017 (worst at 8.7, -6.7, 37.5)FreeCAD (OpenCASCADE) build of recipe.json by tools/freecad-oracle/build.py: Part.makeLoft
18-hole-featuresanalyticpassplate 60x40x15 minus: counterbore (r7 x 5 deep + r4 through), countersink (90 deg cone frustum r6->r3 over 3 mm + r3 through), blind r3 hole 10 deep to the shoulder with a 118 deg cone point (height 3/tan59, centroid 1/4 up from the apex); areas: removed top/bottom disks, cylinder walls 2 pi r h, cb floor annulus, cone laterals pi r s
19-l-bracket-constrainedanalyticpasssolved L: (0,0)-(60,0)-(60,8)-(8,8)-(8,40)-(0,40); area 60x8 + 8x32, extruded 20: V = A2 h, A = 2 A2 + perimeter*h, centroid of the two rectangles
20-slot-constrainedanalyticpassobround: arc centres (0,0) and (40,0), R6, tangent horizontal lines y=+-6: A2 = 2rL + pi r^2, V = A2 h, A = 2 A2 + perimeter (2L + 2 pi r) h
21-draftanalyticpass40x40x30 box, 5 deg draft hinged on the bottom (z=0), pull -Z: square frustum 40 -> 40 + 230tan5; V = h/3 (A0 + A1 + sqrt(A0A1)), sides 4 trapezoids of slant h/cos5
22-split-bodyanalyticpasscylinder r20 z -30..30 split by the plane through the X axis tilted 30 deg (z = y tan30, stays within /z/<=11.6 inside the cylinder): 2 congruent halves (point symmetry), V total = pi r^2 h, A total = cylinder + 2 ellipses pi r (r/cos30)
23-bracket-multifeaturefreecad 1.1.4pass0.007 / 0.0010 (worst at 62.7, 48.7, 25.8)FreeCAD (OpenCASCADE) build of recipe.json by tools/freecad-oracle/build.py: Part extrude, Part extrude join, Part makeFillet, Part extrude join, Part extrude cut, Part makeChamfer
24-user-parametersanalyticpasswidth 60 x depth 40 x height 15 block from the origin with a centred d10 through hole: V = (wd - pi r^2) h, A = 2(wd - pi r^2) + 2(w+d)h + 2 pi r h
25-sketch-triangleanalyticpasssolved triangle (0,0), (50,0), P = t(cos60, sin60) with t = 25 + sqrt(150) from /P-(50,0)/ = 45 (the root nearest the drawn apex); V = (1/2 * 50 * Py) * 5, A = 2 A2 + perimeter*5, centroid = vertex mean
26-sketch-beltanalyticpassbelt: convex hull of circles R20 @ (0,0) and r10 @ (50,0) (external tangents, sin a = (R-r)/d) minus the d8 hole at (50,0), extruded 5 as one body; hull area = (R+r) sqrt(d^2-(R-r)^2) + R^2(pi+2a)/2 + r^2(pi-2a)/2; moments by Green’s theorem over the line/arc boundary
27-sketch-trapezoidanalyticpasssolved trapezoid bottom (-30,0)-(30,0), top (-15,25)-(15,25) minus two d6 holes at (+-18, 8) (symmetric about the axis, 36 apart, 8 above l1); trapezoid centroid y = H(a+2b)/(3(a+b)); V = A2 t, A = 2 A2 + perimeter t
28-blind-pocket-filletsfreecad 1.1.4pass0.009 / 0.0015 (worst at 60.0, 15.1, 13.4)FreeCAD (OpenCASCADE) build of recipe.json by tools/freecad-oracle/build.py: Part extrude, Part extrude cut, Part makeFillet
29-two-sided-and-partial-revolveanalyticpassblock [-20,20]x[-25,10]x[0,20] minus the 270 deg revolve (from +X towards +Y) of the 8x8 square r 4..12, z 12..20; the removed section is the annular sector minus the circular segment y > 10 (R^2 acos(d/R) - d sqrt(R^2-d^2), y-moment 2/3 (R^2-d^2)^1.5) that lies outside the block; sector moments (R^3-r^3)/3 (sin, 1-cos); A = box - top opening + floor - front-face notch 2 sqrt(44) x 8 + inner wall r th h + outer wall (th - pi + 2 asin(5/6)) R h + 2 end caps
30-fillet-after-shellfreecad 1.1.4pass0.008 / 0.0025 (worst at 56.3, 35.7, 3.5)FreeCAD (OpenCASCADE) build of recipe.json by tools/freecad-oracle/build.py: Part extrude, makeThickness shell, Part makeFillet
31-fillet-l-all-edgesfreecad 1.1.4pass0.009 / 0.0018 (worst at 6.0, 38.6, 19.9)FreeCAD (OpenCASCADE) build of recipe.json by tools/freecad-oracle/build.py: Part extrude, Part makeFillet
32-boss-plate-filletsanalyticpassplate 60x60x8 + boss (r10, z 8..23) with R3 concave base and R2 convex top fillets taken as one solid of revolution: V, z-moment and lateral area from Pappus/Green on its (r,z) profile (quarter-circle fillet arcs); plate top loses the r13 disc
33-fillet-tangent-chainanalyticpassobround (R8, centres 40 apart) x 10 minus R2 fillet spandrel R^2(1-pi/4) and 1 mm chamfer triangle swept round the whole loop. Swept volumes use Pappus; Z moments account for inward-offset perimeter P(u)=P-2piu: fillet top-depth moment PAd-2pir^4*(19/24-pi/4), A=r^2*(1-pi/4), d=r*(10-3pi)/(3*(4-pi)); bottom chamfer Z moment Pc^3/6-pic^4/12; surfaces use fillet arc centroid inset r-2r/pi
34-chamfer-then-filletfreecad 1.1.4pass0.009 / 0.0022 (worst at 38.5, 5.6, 19.9)FreeCAD (OpenCASCADE) build of recipe.json by tools/freecad-oracle/build.py: Part extrude, Part makeChamfer, Part makeFillet
35-fillet-curved-curvedfreecad 1.1.4pass0.011 / 0.0024 (worst at -9.5, 5.3, 14.6)FreeCAD (OpenCASCADE) build of recipe.json by tools/freecad-oracle/build.py: Part extrude, Part extrude join, Part makeFillet
36-corner-blend-two-radiifreecad 1.1.4pass0.012 / 0.0026 (worst at 45.4, 39.2, 26.5)FreeCAD (OpenCASCADE) build of recipe.json by tools/freecad-oracle/build.py: Part extrude, Part makeFillet
37-pyramid-apex-blendfreecad 1.1.4pass0.020 / 0.0029 (worst at -0.0, 0.0, 28.4)FreeCAD (OpenCASCADE) build of recipe.json by tools/freecad-oracle/build.py: Part.makeLoft, Part makeFillet
38-coincident-cylindersanalyticpassunion of r10 z0..30 and r10 z20..50 = cylinder r10 h50: V = pi r^2 h, A = 2 pi r h + 2 pi r^2
39-tee-pipefreecad 1.1.4pass0.008 / 0.0014 (worst at 5.4, 9.1, 11.9)FreeCAD (OpenCASCADE) build of recipe.json by tools/freecad-oracle/build.py: Part extrude, Part extrude join, Part extrude cut
40-box-sphere-torusanalyticpassbox 60x60x10 (z -10..0) + upper hemisphere r12 + spherical cap h2 below z=-10 (V = pi h^2(3R-h)/3, A = 2 pi R h) - lower half torus R22 r4 (Pappus: V = pi r^2/2 * 2 pi R, centroid -4r/3pi, A = pi r * 2 pi R); top loses disc r12 and annulus 18..26, bottom loses disc r sqrt(44)faces: bottom, 4 sides, top split by the groove into an annulus (r 12..18) and the outer part, upper sphere, lower cap, torus
41-tube-radial-holesfreecad 1.1.4pass0.010 / 0.0012 (worst at 1.9, 19.9, 24.8)FreeCAD (OpenCASCADE) build of recipe.json by tools/freecad-oracle/build.py: Part extrude, Part extrude cut, pattern (tools transformed and re-applied)
42-overlapping-patternsfreecad 1.1.4pass0.009 / 0.0010 (worst at -1.0, -10.1, 2.2)FreeCAD (OpenCASCADE) build of recipe.json by tools/freecad-oracle/build.py: Part extrude, Part extrude cut, pattern (tools transformed and re-applied), Part extrude cut, pattern (tools transformed and re-applied)
43-cut-splits-bodyanalyticpasstwo boxes 35x20x20 (x 0..35 and 45..80): V = 2abc, A = 2*2(ab+bc+ca)
44-shell-l-bracketanalyticpassL prism (area 736, perimeter 200) x 20 minus cavity = L shrunk by 2 (SolidCraft’s intersection-join shell convention: inner faces offset by 2 and extended to meet, sharp inner corner at (6,6); area 352) from z=2 to 20. A rounded (arc) join would give V 8368.5487, A 8768.5487, 16 faces
45-shell-boss-plateanalyticpassplate 60x60x10 + boss r12 h20 minus cavity (box 56x56x8 + cylinder r10 z8..28) under SolidCraft’s intersection-join shell convention (offset faces extended to meet: sharp circular inner edge at r10, z8). A rounded (arc) join would give V 13620.2567, A 14104.4455, 16 faces (torus round r2)
46-shell-two-open-facesanalyticpassbox 50x40x30 minus cavity x3..47, y0..37, z3..30 (3 mm walls, top and front open)
47-draft-curved-pocketfreecad 1.1.4pass0.008 / 0.0019 (worst at 22.1, 21.3, 15.0)FreeCAD (OpenCASCADE) build of recipe.json by tools/freecad-oracle/build.py: Part extrude, Part extrude cut, PartDesign Draft
48-pulleyanalyticpassrevolve of the 12-segment (r,z) polygon about Z: V = 2 pi int r dA, Mz = 2 pi int r z dA, A = 2 pi int r ds (Pappus)
49-helical-sweepanalyticpasstrue helix coil (screw motion of the axial d4 section, R20, pitch 8, 3 turns): V = turns * 2 pi R * pi a^2; lateral A = turns * 2 pi * int sqrt(r^2 + (p/2pi)^2 (dr/ds)^2) ds (periodic trapezoid, exact to round-off) + two end discs; COM z = pitch*turns/2
50-sweep-tight-bendanalyticpasstube r5 along line 20 + R5 quarter bend + line 20: V = pi r^2 L, A = 2 pi r L + 2 pi r^2 (Pappus, valid while bend radius >= r; here equal so the inner side pinches to a line); bend centroid at (R^2 + r^2/4)/R * sinc(pi/4) along the bisector
51-loft-rect-circle-offsetfreecad 1.1.4pass0.033 / 0.0028 (worst at 17.5, -14.9, 0.5)FreeCAD (OpenCASCADE) build of recipe.json by tools/freecad-oracle/build.py: Part.makeLoft
52-circular-pattern-filletanalyticpassdisc r40 h8 + 6 x (boss r6 h10 with R2 concave base fillet as a solid of revolution, Pappus/Green on its (r,z) profile) at radius 28; disc top loses 6 discs r8
53-path-patternanalyticpassplate 120x60x5 minus 7 through holes d6 at arc lengths k*17.875031 along the arc through (100,15),(60,45),(20,15) (centre (60,10/3), R 125/3), translated copies
54-mirror-body-filletsanalyticpassprism h10 of the 60x40 rectangle with four R5 corner rounds and two d8 holes at (+-15,20): V = A h, area = 2A + perimeter*h
55-tapped-hole-cosmeticanalyticpassplate 40x30x15 minus d6.8 cylinder 12 deep (to the shoulder) and 118 deg drill point cone (h = r/tan59, V = pi r^2 h/3, lateral pi r * r/sin59); cosmetic thread adds no geometry
56-holes-on-curved-facefreecad 1.1.4pass0.009 / 0.0018 (worst at -16.9, 18.5, 0.0)FreeCAD (OpenCASCADE) build of recipe.json by tools/freecad-oracle/build.py: Part extrude, hole tool (cylinder/cone) cut
57-hole-pattern-sketch-pointsanalyticpassplate 80x50x10 minus 6 counterbored through holes (cb d10 x 5.5, hole d5.5) at x 15/40/65, y 15/35
58-phone-standfreecad 1.1.4pass0.007 / 0.0010 (worst at 82.5, 11.7, 6.1)FreeCAD (OpenCASCADE) build of recipe.json by tools/freecad-oracle/build.py: Part extrude, Part makeFillet, Part extrude cut
59-enclosure-basefreecad 1.1.4pass0.008 / 0.0017 (worst at 90.9, 53.0, 6.0)FreeCAD (OpenCASCADE) build of recipe.json by tools/freecad-oracle/build.py: Part extrude, Part makeFillet, makeThickness shell, Part extrude join, Part extrude cut
60-pipe-flangefreecad 1.1.4passFreeCAD (OpenCASCADE) build of recipe.json by tools/freecad-oracle/build.py: Part revolve, Part extrude cut, pattern (tools transformed and re-applied), Part makeChamfer, Part makeFilletNo reference.stl: over 409600 bytes even at 0.02 mm deflection (11380 facets).
61-shaft-keywayfreecad 1.1.4pass0.008 / 0.0019 (worst at 12.2, 2.8, 72.1)FreeCAD (OpenCASCADE) build of recipe.json by tools/freecad-oracle/build.py: Part revolve, Part makeChamfer, Part extrude cut
62-heat-sinkanalyticpassbase 60x50x5 + 15 fins 1.5x50x25 at x = 2 + 4k: sums of boxes; fin ends coplanar with the base endsfaces: bottom, x=0, x=60, y=0 and y=50 (each merged with the fin ends), 16 base-top strips, 15x3 fin faces
63-hinge-knucklefreecad 1.1.4pass0.008 / 0.0016 (worst at 28.0, 36.9, 4.7)FreeCAD (OpenCASCADE) build of recipe.json by tools/freecad-oracle/build.py: Part extrude, Part extrude join, Part extrude cut
64-sketch-tangent-arc-chainanalyticpasssolved sketch: 80x20 rectangle whose top is 4 tangent R12 arcs on 20 mm chords alternating up/down (net area 1600); prism h3: V = A h, area = 2A + perimeter*h, Green’s theorem for centroid
65-sketch-gear-toothfreecad 1.1.4pass0.008 / 0.0017 (worst at 6.5, 39.5, 8.0)FreeCAD (OpenCASCADE) build of recipe.json by tools/freecad-oracle/build.py: Sketcher-solved sketch, Part extrude
66-sketch-linkageanalyticpasssolved sketch: hull of circles R12 at (0,0) and R8 at (60,0) (outer tangent lines, cos a = 4/60) minus holes d12, d8 (concentric) and d4 at (30,+-4) (symmetry keeps radii equal); prism h6
67-sm-base-flangeanalyticpassplate 100 x 60 x t, t = 2.5 (Steel (mm) rule), thickness along +Z: V = 10060t, A = 2(ab+bc+ca)
68-sm-edge-flangeanalyticpassextrusion along x (100) of the YZ section: base y 5..60 x t; 90 deg bend annulus R 2.5..5 about (y 5, z 5); flange y 0..2.5, z 5..20. Setback (R+t)tan45 = 5 (inside bend position, outer-face height datum: outer face of flange reaches z = 20); V = L*A_sec, A = 2 A_sec + L P_sectrim/setback and height datum are SolidCraft’s documented edge-flange conventions (sheet.rs add_flanges)
69-sm-trayanalyticpasstray: base 90 x 50 x 2.5 (each edge trimmed by the setback 5); four 90 deg bends R 2.5..5 along the trimmed edges (90, 50, 90, 50 long); four walls 2.5 thick, z 5..25, each running on past its bend by ext = (R+t) - (t + gap/sqrt2) = 0.732233047 at both ends; open corners. A = sum of prisms - 2 x jointscorner treatment is SolidCraft’s convention (sheet.rs close_corners: panels extended until their inner corners are the rule gap = t apart; no corner fill, open corner relief below the walls)
70-sm-flange-45analyticpassas 68 with a 45 deg bend: setback (R+t)tan22.5 = 2.071068, base y 2.071068..60, bend annulus 2.5..5 over 45 deg about (y 2.071068, z 5), flat leg 15 - setback = 12.928932 turned 45 deg; extrusion along x 100SolidCraft convention: flange height (15) is measured along the flange’s outer face from the virtual sharp at the original edge (y 0, z 0), so the outer top corner is at (y, z) = (-15 cos45, 15 sin45).
71-sm-custom-ruleanalyticpassU channel, rule t 1.5, R 3, K 0.33 (K unused folded): base y 4.5..45.5 x 1.5; two 90 deg bend annuli R 3..4.5; flanges y 0..1.5 and 48.5..50, z 4.5..20; extrusion along x 80the rule edit applies to the whole sheet body (base built before it is 1.5 thick too, as the recipe’s edge picks at z = 1.5 state)
72-sm-contour-flangeanalyticpasscontour flange, width 50 along +Y, XZ section: plate x 0..35 z 0..2.5; bend R 2.5..5 about (35, 5) (sketch corner = outer virtual sharp, setback R+t); wall x 37.5..40 z 5..27.5; bend R 2.5..5 about (42.5, 27.5) (sketch corner = inner virtual sharp, setback R); plate x 42.5..70 z 30..32.5SolidCraft convention: the sketch lines are the sheet face away from the material (material right of travel); thickness grows to the material side
73-sm-hemanalyticpass68’s part (height 20) plus a flat hem on the flange top edge: 180 deg bend, inner radius = hem gap 0.01, outer 2.51, about (y 2.51, z 20), folded toward +y (inside); hem leg 8 - (0.01 + 2.5) = 5.49: y 2.52..5.02, z 14.51..20; extrusion along x 100SolidCraft conventions: hem gap 0.01 mm (rule hem_gap), hem length measured to the outer face of the hem bend (top at z 22.51), hem folds over the flange’s inner face.
74-sm-unfold-refoldanalyticpassU channel 80x50 (t 2.5, R 2.5, base y 5..45, flanges z 5..20) with the flat-pattern slot x 35..45, y -12..10 refolded: BA = (pi/2)(R + K t) = 5.654866776, so the slot removes base y 5..10, the whole y=0 bend and the flange from z 5 to 5 + (12 - (BA - 5)) = 16.345133224; x-segments 0..35 / 35..45 / 45..80unfold/refold maps flat distance through the K-factor neutral layer (K 0.44)
75-sm-bracket-holesanalyticpassbase x 0..75, y 5..40, z 0..2.5 (trimmed 5 by each flange’s setback); bend 1 (R 2.5..5 about y 5, z 5) x 0..75; flange 1 y 0..2.5, z 5..30, x 0..80; bend 2 (R 2.5..5 about x 75, z 5) y 5..40; flange 2 x 77.5..80, z 5..15, y 5..40; minus two d6 through holes at (25,20), (55,20) in the base. A = sum of prism areas - 2 x 4 joint areas (75t, 75t, 35t, 35t) + hole walls - hole discsSolidCraft convention: the second flange trims the base under the first bend, so bend 1 stops at x 75 while flange 1’s panel runs to x 80 (open corner relief, no fill).
76-sm-thickness-variantanalyticpassfinal rule thickness 3 (bend radius = Thickness = 3): base y 6..60 x 3; bend annulus R 3..6 about (y 6, z 6); flange y 0..3, z 6..20; extrusion along x 100the rule’s thickness edits regenerate the whole sheet body; only the final 3 mm state matters
77-fillet-variable-linearanalyticpassbox 40x30x20 with its top front edge (y=0, z=20) rounded at a radius running straight from 2 (x=0) to 5 (x=40): sections are quarter rounds, V = abc - (1 - pi/4) L (a^2 + ab + b^2) / 3; moments and the round’s area integrated along the edge (tools/oracle-analytic/cases_fillets.py)
78-fillet-variable-to-zeroanalyticpassas 77 with the radius running from 4 (x=0) down to 0 (x=40), where the round closes to the corner: V = abc - (1 - pi/4) L a^2 / 3; the round is three-sided and the corner at x=40 stays
79-fillet-chord-lengthanalyticpassbox 40x30x20, top front edge rounded 4 wide across (chord c): r = c / (2 cos 45 deg) = 2 sqrt 2; V = abc - (1 - pi/4) r^2 L, A = 2(ab + bc + ca) - 2 r L + (pi/2) r L - 2 (1 - pi/4) r^2
80-fillet-taper-to-fitanalyticpassplate (top 20 -> 2 wide over x 0..40, 15 high) with its top front edge filleted at 3, fit: taper. The radius is the taper law: a clamped cubic B-spline in u = x/40 on 8 equal spans, control radii min(3, 0.9 room) at the Greville abscissae, room = min(20 - 18u, 15) tan 45 deg (top face width, front face height): 3.0000, 3.0000, 3.0000, 3.0000, 3.0000, 3.0000, 3.0000, 3.0000, 3.0000, 2.4750, 1.8000. Sections are quarter rounds; V, moments and areas integrated along the edge
81-fillet-spline-radiusanalyticpassbox 40x30x20 with its top front edge (y=0, z=20) rounded at a radius that is a clamped cubic B-spline in u = x/40, knots 0,0,0,0,0.25,0.6,1,1,1,1, control radii 2, 5, 1.5, 4, 1, 3 (r(0) = 2, r(40) = 3). Sections are quarter rounds; V, moments and areas integrated along the edge, exactly per knot span
82-thin-extrude-frameanalyticpasssquare frame: the lines’ 40 mm square walled 3 mm inside and 1 mm outside (mitred corners) is 42 x 42 less 34 x 34, 10 mm tall: V = (42^2 - 34^2) * 10, A = 2 (42^2 - 34^2) + 4 * 42 * 10 + 4 * 34 * 10
83-boundary-loop-thickenanalyticpassregion = 10 x 10 square + half disc r 5 on its top side: A = 100 + 12.5 pi, centroid by moments (half disc’s at 4r / (3 pi) above its diameter); slab of thickness 2 over it (symmetric about z = 0): V = 2A, area = 2A + 2 (30 + 5 pi)
84-plate-two-boresanalyticpassPlate L=100, W=80, T=20 mm; Bore1 at (20,20) Ø10; Bore2 at (60,50) Ø10. Box minus independently located through-cylinders: V=LWT-sum(pid²T/4), area=2(LW+LT+WT)+sum(pidT-pi*d²/2), mass moments from exact box minus cylinders. Shape: rectangular
85-plate-width-100analyticpassPlate L=100, W=100, T=20 mm; Bore1 at (20,20) Ø10; Bore2 at (60,50) Ø10. Box minus independently located through-cylinders: V=LWT-sum(pid²T/4), area=2(LW+LT+WT)+sum(pidT-pi*d²/2), mass moments from exact box minus cylinders. Shape: rectangular
86-plate-first-bore-12analyticpassPlate L=100, W=80, T=20 mm; Bore1 at (20,20) Ø12; Bore2 at (60,50) Ø10. Box minus independently located through-cylinders: V=LWT-sum(pid²T/4), area=2(LW+LT+WT)+sum(pidT-pi*d²/2), mass moments from exact box minus cylinders. Shape: rectangular
87-plate-bore1-deletedanalyticpassPlate L=100, W=80, T=20 mm; Bore2 at (60,50) Ø10. Box minus independently located through-cylinders: V=LWT-sum(pid²T/4), area=2(LW+LT+WT)+sum(pidT-pi*d²/2), mass moments from exact box minus cylinders. Shape: rectangular
88-plate-four-boresanalyticpassPlate L=150, W=80, T=10 mm; Bore1 at (15,15) Ø8; Bore2 at (135,15) Ø8; Bore3 at (15,65) Ø8; Bore4 at (135,65) Ø8. Box minus independently located through-cylinders: V=LWT-sum(pid²T/4), area=2(LW+LT+WT)+sum(pidT-pi*d²/2), mass moments from exact box minus cylinders. Shape: rectangular
94-plate-four-rounded-cornersanalyticpassPlate L=100, W=80, T=20 mm; Bore1 at (20,20) Ø10; Bore2 at (60,50) Ø10. Box minus independently located through-cylinders: V=LWT-sum(pid²T/4), area=2(LW+LT+WT)+sum(pidT-pid²/2), mass moments from exact box minus cylinders. Shape: four exterior vertical R10 corner rounds; four corner spandrels subtract volume T(4-pi)R² at symmetric centroid (L/2,W/2,T/2); rounded perimeter P=2(L+W)-8R+2piR
95-plate-rounded-top-chamferanalyticpassPlate L=100, W=80, T=20 mm; Bore1 at (20,20) Ø10; Bore2 at (60,50) Ø10. Box minus independently located through-cylinders: V=LWT-sum(pid²T/4), area=2(LW+LT+WT)+sum(pidT-pid²/2), mass moments from exact box minus cylinders. Shape: four exterior vertical R10 corner rounds; full upper tangent loop 2 mm chamfer; four corner spandrels subtract volume T(4-pi)R² at symmetric centroid (L/2,W/2,T/2); rounded perimeter P=2(L+W)-8R+2piR; upper tangent chamfer subtracts volume Vc=Pc²/2-pic³/3 at (L/2,W/2,T-(Pc³/6-pic⁴/12)/Vc); area from upper inset face, side strips and sqrt(2)c(P-pi*c) bevel surface

Not covered by a reference case

FeatureTested by
Boss (draft, root fillet, hole, ribs)post, hole and rib volumes (boss_with_hole_fillet_and_ribs)
Lip / Grooveband offsets and groove clearance on a shelled box (lip_and_groove_on_a_shelled_box, groove_takes_the_rule_clearance)
Snap Fitarm and catch volume, hook direction (snap_fit_arm_and_catch)
Restrectangle, round, hollow and drafted pads (rest_pads)
Plastic Rules (manage, assign)library values, Thickness-based values, rule draft as the boss default (plastic_rules_library_edit_and_assign)

Features

SolidCraft’s commands by area, generated by cargo xtask features from the command registry (solidcraft-cli commands). Planned commands are marked in their rows. Every command runs the same way from the toolbar, a script, solidcraft-cli exec <id> '<json>', the control channel and MCP; parameters are in the command reference (cargo xtask book). Status and plans: ROADMAP.

Sketch

CommandIdToolbar
Center Point Arcsketch.arc.center_pointSKETCH › CREATE
Tangent Arcsketch.arc.tangentSKETCH › CREATE
3-Point Arcsketch.arc.three_pointSKETCH › CREATE
3D Arcsketch.arc3d–
AutoConstrainsketch.auto_constrainSKETCH › CONSTRAINTS
AutoConstrain from Datumsketch.auto_constrain_from_datumSKETCH › CONSTRAINTS
Auto-Project Edges on Referencesketch.auto_project–
Blend Curvesketch.blend_curveSKETCH › MODIFY
Breaksketch.breakSKETCH › MODIFY
Break Linksketch.break_link–
Cancel Sketchsketch.cancelSKETCH › FINISH SKETCH
Centerlinesketch.centerlineSKETCH › CREATE
Chainsketch.chain–
Distance and Angle Chamfersketch.chamfer.distance_angleSKETCH › MODIFY
Equal Distance Chamfersketch.chamfer.equal_distanceSKETCH › MODIFY
Two Distance Chamfersketch.chamfer.two_distanceSKETCH › MODIFY
Center Diameter Circle (C)sketch.circle.centerSKETCH › CREATE
3-Point Circlesketch.circle.three_pointSKETCH › CREATE
3-Tangent Circlesketch.circle.three_tangentSKETCH › CREATE
2-Point Circlesketch.circle.two_pointSKETCH › CREATE
2-Tangent Circlesketch.circle.two_tangentSKETCH › CREATE
Conic Curvesketch.conicSKETCH › CREATE
Sketch Constrainersketch.constrainerSKETCH › CREATE
Coincidentsketch.constraint.coincidentSKETCH › CONSTRAINTS
Collinearsketch.constraint.collinearSKETCH › CONSTRAINTS
Concentricsketch.constraint.concentricSKETCH › CONSTRAINTS
Curvaturesketch.constraint.curvatureSKETCH › CONSTRAINTS
Equalsketch.constraint.equalSKETCH › CONSTRAINTS
Fix/Unfixsketch.constraint.fixSKETCH › CONSTRAINTS
Horizontal/Verticalsketch.constraint.horizontal_verticalSKETCH › CONSTRAINTS
MidPointsketch.constraint.midpointSKETCH › CONSTRAINTS
Parallelsketch.constraint.parallelSKETCH › CONSTRAINTS
Perpendicularsketch.constraint.perpendicularSKETCH › CONSTRAINTS
Polygonsketch.constraint.polygonSKETCH › CONSTRAINTS
Symmetrysketch.constraint.symmetrySKETCH › CONSTRAINTS
Tangentsketch.constraint.tangentSKETCH › CONSTRAINTS
Normal/Construction (X)sketch.constructionSKETCH › CREATE
Construction Linesketch.construction_lineSKETCH › CREATE
Construction Line at Anglesketch.construction_line.angleSKETCH › CREATE
Angle Bisectorsketch.construction_line.bisectorSKETCH › CREATE
Horizontal Construction Linesketch.construction_line.horizontalSKETCH › CREATE
Parallel Construction Linesketch.construction_line.parallelSKETCH › CREATE
Perpendicular Construction Linesketch.construction_line.perpendicularSKETCH › CREATE
Tangent at Curve Pointsketch.construction_line.tangent_atSKETCH › CREATE
Tangent through External Pointsketch.construction_line.tangent_externalSKETCH › CREATE
Common Tangentsketch.construction_line.tangent_twoSKETCH › CREATE
Vertical Construction Linesketch.construction_line.verticalSKETCH › CREATE
Copy Sketch Entitiessketch.copy–
Create Sketchsketch.createSOLID › CREATE
Cut Sketch Entitiessketch.cut–
Delete Sketch Entitiessketch.delete–
Sketch Dimension (D)sketch.dimensionSKETCH › CREATE
Move Dimension Textsketch.dimension_text–
Drag Sketch Geometrysketch.drag–
End Sketch Dragsketch.drag_end–
Draw Dimensioned Sketch Geometrysketch.draw.dimensioned–
Drawing Sizesketch.drawing_bounds–
Edit Sketchsketch.edit–
Edit Textsketch.edit_text–
Ellipsesketch.ellipseSKETCH › CREATE
Equation Curvesketch.equation_curveSKETCH › CREATE
Edit Equation Curvesketch.equation_curve.edit–
Save As DXFsketch.export_dxf–
Extendsketch.extendSKETCH › MODIFY
Filletsketch.filletSKETCH › MODIFY
Finish Sketchsketch.finishSKETCH › FINISH SKETCH
Finish with AutoConstrainsketch.finish_auto_constrainSKETCH › FINISH SKETCH
Fit Curves to Mesh Sectionsketch.fit_curves_to_sectionSKETCH › CREATE
Constraint Glyphssketch.glyphs–
Include 3D Geometrysketch.include_3dSKETCH › CREATE
Infinite Linesketch.infinite–
Insert DXFsketch.insert_dxfSKETCH › INSERT
Insert SVGsketch.insert_svgSKETCH › INSERT
Inspect Sketchsketch.inspectSKETCH › INSPECT
Intersectsketch.intersectSKETCH › CREATE
Intersection Curvesketch.intersection_curveSKETCH › CREATE
Isoparametric Curvesketch.isoparametric_curveSKETCH › CREATE
Line (L)sketch.lineSKETCH › CREATE
Midpoint Linesketch.line.midpointSKETCH › CREATE
3D Linesketch.line3d–
Loose Endssketch.loose_ends–
Tie Loose Endssketch.loose_ends.tie–
Mirrorsketch.mirrorSKETCH › PATTERN
Move/Copy Sketch Entitiessketch.moveSKETCH › MODIFY
Move 3D Pointsketch.move3d–
Drag Sketch Pointsketch.move_point–
Move Sketch Pointssketch.move_points–
Offset (O)sketch.offsetSKETCH › MODIFY
Break Offset Associationsketch.offset.break–
Sketch Palette Optionssketch.options–
Paste Sketch Entitiessketch.paste–
Circular Patternsketch.pattern.circularSKETCH › PATTERN
Rectangular Patternsketch.pattern.rectangularSKETCH › PATTERN
Pointsketch.pointSKETCH › CREATE
Divide Curvesketch.point.divideSKETCH › CREATE
3D Pointsketch.point3d–
Circumscribed Polygonsketch.polygon.circumscribedSKETCH › CREATE
Edge Polygonsketch.polygon.edgeSKETCH › CREATE
Inscribed Polygonsketch.polygon.inscribedSKETCH › CREATE
Project (P)sketch.projectSKETCH › CREATE
Project To Surfacesketch.project_to_surfaceSKETCH › CREATE
Center Rectanglesketch.rectangle.centerSKETCH › CREATE
Rounded Rectanglesketch.rectangle.roundedSKETCH › CREATE
3-Point Rectanglesketch.rectangle.three_pointSKETCH › CREATE
2-Point Rectangle (R)sketch.rectangle.two_pointSKETCH › CREATE
Redefine Sketch Planesketch.redefine–
Replace Sketchsketch.replace–
Sketch Scalesketch.scaleSKETCH › MODIFY
Center Point Arc Slotsketch.slot.arc_centerSKETCH › CREATE
Three Point Arc Slotsketch.slot.arc_three_pointSKETCH › CREATE
Center Point Slotsketch.slot.center_pointSKETCH › CREATE
Center to Center Slotsketch.slot.center_to_centerSKETCH › CREATE
Overall Slotsketch.slot.overallSKETCH › CREATE
Snap Inferencesketch.snap–
Solve Sketchsketch.solve–
Control Point Splinesketch.spline.control_pointSKETCH › CREATE
Control Point Spline (Degree 5)sketch.spline.control_point_5SKETCH › CREATE
Fit Point Splinesketch.spline.fit_pointSKETCH › CREATE
Add Tangent Handlessketch.spline.handles–
Add Spline Pointsketch.spline.insert_point–
Remove Spline Pointsketch.spline.remove_point–
Reset Tangent Handlessketch.spline.reset_handles–
Reverse Spline Directionsketch.spline.reverse–
3D Splinesketch.spline3d–
Spun Profilesketch.spun_profileSKETCH › CREATE
Textsketch.textSKETCH › CREATE
Toggle Radius / Diametersketch.toggle_diameter–
Toggle Driven Dimensionsketch.toggle_driven–
Transform (G)sketch.transformSKETCH › MODIFY
Trim (T)sketch.trimSKETCH › MODIFY
Trim Along Pathsketch.trim.path–

Solid modelling

CommandIdToolbar
Alignsolid.alignSOLID › MODIFY
Boundary Fillsolid.boundary_fillSOLID › CREATE
Bounding Solidsolid.bounding_solidSOLID › CREATE
Boxsolid.boxSOLID › CREATE
Cell Gridsolid.cell_grid–
Chamfersolid.chamferSOLID › MODIFY
Coilsolid.coilSOLID › CREATE
Combinesolid.combineSOLID › MODIFY
Conesolid.coneSOLID › CREATE
Cylindersolid.cylinderSOLID › CREATE
Draftsolid.draftSOLID › MODIFY
Embosssolid.embossSOLID › CREATE
Extrude (E)solid.extrudeSOLID › CREATE
Fillet (F)solid.filletSOLID › MODIFY
Hole (H)solid.holeSOLID › CREATE
Loftsolid.loftSOLID › CREATE
Mirrorsolid.mirrorSOLID › CREATE
Move/Copy (M)solid.moveSOLID › MODIFY
Offset Facesolid.offset_faceSOLID › MODIFY
Circular Patternsolid.pattern.circularSOLID › CREATE
Pattern on Pathsolid.pattern.pathSOLID › CREATE
Rectangular Patternsolid.pattern.rectangularSOLID › CREATE
Pipesolid.pipeSOLID › CREATE
Press Pull (Q)solid.press_pullSOLID › MODIFY
Removesolid.removeSOLID › MODIFY
Replace Facesolid.replace_faceSOLID › MODIFY
Revolvesolid.revolveSOLID › CREATE
Ribsolid.ribSOLID › CREATE
Scalesolid.scaleSOLID › MODIFY
Shellsolid.shellSOLID › MODIFY
Spheresolid.sphereSOLID › CREATE
Split Bodysolid.split_bodySOLID › MODIFY
Split Facesolid.split_faceSOLID › MODIFY
Sweepsolid.sweepSOLID › CREATE
Threadsolid.threadSOLID › CREATE
Torussolid.torusSOLID › CREATE
Websolid.webSOLID › CREATE

Surfaces

CommandIdToolbar
Blend Surfacesurface.blendSURFACE › CREATE
Boundary Surfacesurface.boundarySURFACE › CREATE
Extendsurface.extendSURFACE › MODIFY
Surface Extrudesurface.extrudeSURFACE › CREATE
Fair Surfacesurface.fairSURFACE › MODIFY
Surface Fillet (planned: #769)surface.filletSURFACE › MODIFY
Surface Loftsurface.loftSURFACE › CREATE
Match Surfacesurface.matchSURFACE › MODIFY
Offset Surfacesurface.offsetSURFACE › CREATE
Patchsurface.patchSURFACE › CREATE
Reverse Normalsurface.reverse_normalSURFACE › MODIFY
Surface Revolvesurface.revolveSURFACE › CREATE
Ruled Surfacesurface.ruledSURFACE › CREATE
Stitchsurface.stitchSURFACE › MODIFY
Surface Sweepsurface.sweepSURFACE › CREATE
Thickensurface.thickenSURFACE › CREATE
Trimsurface.trimSURFACE › MODIFY
Unstitchsurface.unstitchSURFACE › MODIFY
Untrimsurface.untrimSURFACE › MODIFY

Construction geometry

CommandIdToolbar
Axisconstruct.axisSOLID › CONSTRUCT
Axis Through Edgeconstruct.axis.edgeSOLID › CONSTRUCT
Axis Perpendicular To Faceconstruct.axis.normal_to_faceSOLID › CONSTRUCT
Axis Through Cylinder/Cone/Torusconstruct.axis.through_cylinderSOLID › CONSTRUCT
Axis Through Two Planesconstruct.axis.two_planesSOLID › CONSTRUCT
Axis Through Two Pointsconstruct.axis.two_pointsSOLID › CONSTRUCT
Planeconstruct.planeSOLID › CONSTRUCT
Plane Along Pathconstruct.plane.along_pathSOLID › CONSTRUCT
Plane at Angleconstruct.plane.at_angleSOLID › CONSTRUCT
Plane Through Axis And Pointconstruct.plane.axis_pointSOLID › CONSTRUCT
Midplaneconstruct.plane.midplaneSOLID › CONSTRUCT
Offset Planeconstruct.plane.offsetSOLID › CONSTRUCT
Perpendicular Planeconstruct.plane.perpendicularSOLID › CONSTRUCT
Tangent Planeconstruct.plane.tangentSOLID › CONSTRUCT
Plane Through Three Pointsconstruct.plane.three_pointsSOLID › CONSTRUCT
Plane Through Two Edgesconstruct.plane.two_edgesSOLID › CONSTRUCT
Pointconstruct.pointSOLID › CONSTRUCT
Point Along Pathconstruct.point.along_pathSOLID › CONSTRUCT
Point At Center Of Circle/Sphere/Torusconstruct.point.centerSOLID › CONSTRUCT
Point At Edge And Planeconstruct.point.edge_planeSOLID › CONSTRUCT
Point Through Three Planesconstruct.point.three_planesSOLID › CONSTRUCT
Point Through Two Edgesconstruct.point.two_edgesSOLID › CONSTRUCT
Point At Vertexconstruct.point.vertexSOLID › CONSTRUCT

Sheet metal

CommandIdToolbar
Convert to Sheet Metalsheet.convertSHEET METAL › CREATE
Export Bend Tablesheet.export_bend_table–
Export Flat Pattern as DXFsheet.export_dxf–
Flangesheet.flangeSHEET METAL › CREATE
Create Flat Patternsheet.flat_patternSHEET METAL › CREATE
Foldsheet.foldSHEET METAL › CREATE
Hemsheet.hemSHEET METAL › CREATE
Sheet Metal Rulessheet.manage_rulesSHEET METAL › MODIFY
Path Flangesheet.path_flangeSHEET METAL › CREATE
Perforatesheet.perforateSHEET METAL › CREATE
Refoldsheet.refoldSHEET METAL › MODIFY
List Sheet Metal Rulessheet.rules–
Unfoldsheet.unfoldSHEET METAL › MODIFY

Plastic parts

CommandIdToolbar
Assign Plastic Ruleplastic.assign_rulePLASTIC › SETUP
Bossplastic.bossPLASTIC › CREATE
Lipplastic.lipPLASTIC › CREATE
Manage Plastic Rulesplastic.manage_rulesPLASTIC › SETUP
Restplastic.restPLASTIC › CREATE
List Plastic Rulesplastic.rules–
Snap Fitplastic.snap_fitPLASTIC › CREATE

Components

CommandIdToolbar
Activate Componentcomponent.activate–
Break Linkcomponent.break_link–
Capture Positioncomponent.capture_positionSOLID › POSITION
Find Identical Copiescomponent.copies–
Paste Bodiescomponent.copy_bodies–
New Componentcomponent.createSOLID › ASSEMBLE
Cycle Component Colorcomponent.cycle_color–
Delete Componentcomponent.delete–
Edit in Placecomponent.edit_in_place–
Extract to Filecomponent.extract–
Finish Editcomponent.finish_edit–
Create Components from Bodiescomponent.from_bodies–
Hide Component Historycomponent.hide_history–
Insert Designcomponent.insert_designSOLID › ASSEMBLE
List Linkscomponent.links–
List Componentscomponent.list–
Part Propertiescomponent.metadata–
Change Part Propertiescomponent.metadata.set–
Move Bodies to Componentcomponent.move_bodies–
Move Features to Componentcomponent.move_features–
Move Sketches to Componentcomponent.move_sketches–
Move Sketches to Their Partscomponent.move_sketches.to_parts–
Paste from Another Designcomponent.paste_from_design–
Paste Newcomponent.paste_new–
Component Physical Propertiescomponent.properties–
Relink Sourcecomponent.relink–
Rename Componentcomponent.rename–
Replace with Linkcomponent.replace_with_link–
Revert Positioncomponent.revert_positionSOLID › POSITION
Show Component Historycomponent.show_history–
Unlock Contentscomponent.unlock–
Update Linkscomponent.update_links–

Assembly joints

CommandIdToolbar
As-Built Jointjoint.as_builtSOLID › ASSEMBLE
Camjoint.camSOLID › ASSEMBLE
Joint (J)joint.createSOLID › ASSEMBLE
Delete Jointjoint.delete–
Drive Jointsjoint.driveSOLID › ASSEMBLE
Edit Jointjoint.edit–
Find Joint Limitsjoint.find_limits–
Gearjoint.gearSOLID › ASSEMBLE
Inspect Jointjoint.inspect–
Joint Limitsjoint.limits–
List Jointsjoint.list–
Motion Linkjoint.motion_linkSOLID › ASSEMBLE
Joint Originjoint.originSOLID › CREATE
Rack and Pinionjoint.rack_pinionSOLID › ASSEMBLE
Reach Pointjoint.reach–
Rigid Groupjoint.rigid_groupSOLID › ASSEMBLE
Screwjoint.screwSOLID › ASSEMBLE
Solve Jointsjoint.solve–
Sweep Jointjoint.sweep–

Configurations

CommandIdToolbar
Activate Configurationconfig.activate–
Configuration Columnconfig.column–
Configureconfig.configureSOLID › CONFIGURE
Preview Configurationconfig.preview–
Configuration Rowconfig.row–
Configuration Ruleconfig.rule–
Configuration Rulesconfig.rules–
Display Configuration Tableconfig.tableSOLID › CONFIGURE

Timeline

CommandIdToolbar
Add Recorded Featuretimeline.add–
Compute Alltimeline.compute_allSOLID › MODIFY
Copy Featurestimeline.copy–
Delete (Delete)timeline.deleteSOLID › MODIFY
Feature Dependentstimeline.dependents–
Edit Featuretimeline.edit–
Export Command Scripttimeline.export_script–
New Feature Foldertimeline.group–
Inspect Featuretimeline.inspect–
Move Timeline Itemstimeline.move_many–
Move Features to Foldertimeline.move_to_group–
Paste Featurestimeline.paste–
Play Historytimeline.playINSPECT › HISTORY
Edit Featuretimeline.redefine–
Renametimeline.rename–
Rename Feature Foldertimeline.rename_group–
Reorder Featuretimeline.reorder–
Reorder Featurestimeline.reorder_many–
Roll History Markertimeline.roll_to–
Move Timeline Markertimeline.rollback–
Suppress Featuretimeline.suppress–
Remove Feature Folderstimeline.ungroup–

Inspect

CommandIdToolbar
Accessibility Analysisinspect.accessibilitySKETCH › INSPECT
View Analysisinspect.analysis–
Center of Massinspect.center_of_massSKETCH › INSPECT
Clearanceinspect.clearance–
Component Colorsinspect.colored_componentsSOLID › INSPECT
Compare Bodiesinspect.compare–
Compare Pictureinspect.compare_picture–
Continuity Analysisinspect.continuitySURFACE › INSPECT
Curvature Comb Analysisinspect.curvature_combSKETCH › INSPECT
Curvature Map Analysisinspect.curvature_mapSKETCH › INSPECT
Draft Analysisinspect.draftSKETCH › INSPECT
Environment Map Analysisinspect.environment_mapSKETCH › INSPECT
Interferenceinspect.interferenceSOLID › INSPECT
Isocurve Analysisinspect.isocurveSKETCH › INSPECT
Measure (I)inspect.measureSOLID › INSPECT
Minimum Radius Analysisinspect.minimum_radiusSKETCH › INSPECT
Parameter Sweepinspect.parameter_sweep–
Compare Parameter Variantsinspect.parameter_variants–
Section Analysisinspect.sectionSOLID › INSPECT
Silhouetteinspect.silhouette–
Zebra Analysisinspect.zebraSKETCH › INSPECT

Files

CommandIdToolbar
Exportfile.export–
Export 3MFfile.export_3mf–
Export OBJfile.export_obj–
Export STEPfile.export_step–
Export STLfile.export_stl–
Export URDFfile.export_urdf–
Export Current View as DXFfile.export_view_dxf–
Signing Keysfile.gpg_keys–
Clear Reused Importsfile.import_cache_clear–
Insert Meshfile.insert_meshSOLID › INSERT
Insert STEPfile.insert_stepSOLID › INSERT
New Design (Ctrl+N)file.new–
Open with EasyPrintfile.open_in_easyprint–
Save (Ctrl+S)file.save–
Save As (Ctrl+Shift+S)file.save_as–
Save As Meshfile.save_meshINSERT › FILES
Send to Slicerfile.send_to_slicerINSERT › FILES
Installed Slicersfile.slicers–
Verify Signaturefile.verify–

Documents

CommandIdToolbar
Open (Ctrl+O)doc.open–
Recover Designdoc.recover–
Discard Recovered Designdoc.recovery_discard–
Recoverable Designsdoc.recovery_list–

Edit

CommandIdToolbar
Checkpointedit.checkpoint–
Redo (Ctrl+Y)edit.redo–
Repeat Last Command (Shift+R)edit.repeat–
Restore Checkpointedit.restore_checkpoint–
Undo (Ctrl+Z)edit.undo–

appearance.*

CommandIdToolbar
Appearance (A)appearance.assignSOLID › MODIFY
Appearance Chainappearance.chain–
Copy Look (Ctrl+Alt+C)appearance.copy–
Favorite Lookappearance.favorite–
Appearance Libraryappearance.library–
List Appearancesappearance.list–
Paint Appearance (B)appearance.paint–
Paste Look (Ctrl+Alt+V)appearance.paste–
Pick Up Appearance (I)appearance.pick_up–
Replace Appearanceappearance.replace–

assembly.*

CommandIdToolbar
Bill of Materialsassembly.bomSOLID › INSPECT
Constrainassembly.constraint.createSOLID › ASSEMBLE
Edit Constraintassembly.constraint.edit–
Mass Propertiesassembly.mass_properties–

body.*

CommandIdToolbar
Print Face (Ctrl+B)body.print_face–
Rename Bodybody.rename–
Repair Partbody.repair–

browser.*

CommandIdToolbar
Collapse Allbrowser.collapse_all–
Expand Allbrowser.expand_all–
New Groupbrowser.group–
Move Browser Itemsbrowser.move–
Order Browser Itemsbrowser.order–
Rename Groupbrowser.rename_group–
Move to Componentbrowser.reparent–
Ungroupbrowser.ungroup–
Show/Hidebrowser.visibility–

canvas.*

CommandIdToolbar
Calibrate Canvascanvas.calibrate–
Decalcanvas.decalSKETCH › INSERT
Delete Canvascanvas.delete–
Edit Canvascanvas.editSKETCH › INSERT
Canvascanvas.insertSKETCH › INSERT
Straighten Canvascanvas.straighten–
Canvas to Sketchcanvas.to_sketch–

contact.*

CommandIdToolbar
Check Contactcontact.check–
New Contact Setcontact.createSOLID › ASSEMBLE
Disable Contactcontact.disable_allSOLID › ASSEMBLE
Edit Contact Setcontact.edit–
Enable All Contactcontact.enable_allSOLID › ASSEMBLE
Enable Contact Setscontact.enable_setsSOLID › ASSEMBLE
List Contact Setscontact.list–

diagnostics.*

CommandIdToolbar
Crash Reportdiagnostics.crash_report–
Crash Reportsdiagnostics.crash_reports–
Delete Crash Reportsdiagnostics.crash_reports.clear–
Crash Testdiagnostics.crash_test–
Profilediagnostics.profile–
Validationdiagnostics.validation–

document.*

CommandIdToolbar
Inspect Designdocument.inspect–
Design Propertiesdocument.metadata–
Remove Design Propertydocument.metadata.remove–
Change Design Propertiesdocument.metadata.set–
Design Revisiondocument.revision–
Change Design Unitsdocument.units–

engine.*

CommandIdToolbar
List Commandsengine.commands–
Describe Commandengine.describe–

explode.*

CommandIdToolbar
Exploded Viewexplode.createSOLID › ASSEMBLE
Delete Exploded Viewexplode.delete–
List Exploded Viewsexplode.list–
Show Exploded Viewexplode.show–

expr.*

CommandIdToolbar
Evaluate Expressionexpr.evaluate–

face.*

CommandIdToolbar
Delete Faceface.deleteSOLID › MODIFY

frame.*

CommandIdToolbar
End Capframe.end_capWELDMENTS › CREATE
Frame Memberframe.memberWELDMENTS › CREATE
Insert Profileframe.profile.insert–
Profile Libraryframe.profile.list–
Profile Propertiesframe.profile.properties–

layout.*

CommandIdToolbar
Apply Layoutlayout.apply–
Inspect Layoutlayout.get–
Reset Layoutlayout.reset–
Save Layoutlayout.save–
Select Layout Workspacelayout.workspace.select–

library.*

CommandIdToolbar
Apply Local Library Entrylibrary.apply–
Export Design Librarylibrary.export–
Inspect Local Librarylibrary.inspect–

material.*

CommandIdToolbar
Physical Materialmaterial.assignSOLID › MODIFY
New Physical Materialmaterial.create–
Delete Physical Materialmaterial.delete–
Edit Physical Materialmaterial.edit–
Inspect Physical Materialmaterial.inspect–
List Materialsmaterial.list–
Directional Elastic Modulusmaterial.properties.directional–
Validate Physical Material Datamaterial.properties.validate–

mesh.*

CommandIdToolbar
Convert Meshmesh.convert–

model.*

CommandIdToolbar
List Body Featuresmodel.body_features–
List Edgesmodel.edges–
List Facesmodel.faces–
Read Body Meshmodel.mesh–
List Threadsmodel.threads–

motion.*

CommandIdToolbar
Delete Motion Studymotion.delete–
Export Motion Studymotion.export–
Export Motion Animationmotion.export_animation–
Motion Interferencemotion.interference–
List Motion Studiesmotion.list–
Play Motion Studymotion.play–
Plot Motion Studymotion.plot–
Motion Studymotion.studySOLID › ASSEMBLE
Motion Tracemotion.traceSOLID › ASSEMBLE
Edit Motion Tracemotion.trace.edit–
Keep Motion Tracemotion.trace.keep–

note.*

CommandIdToolbar
Add Notenote.add–
Delete Notenote.delete–
List Notesnote.list–
Edit Notenote.update–

occurrence.*

CommandIdToolbar
Align Occurrenceoccurrence.align–
Paste (Instance)occurrence.copy–
Drag Occurrenceoccurrence.drag–
Groundoccurrence.ground–
Move Occurrenceoccurrence.move–
Occurrence Physical Propertiesoccurrence.properties–
Move to Componentoccurrence.reparent–

panel.*

CommandIdToolbar
Run Panel Header Actionpanel.action–
Activate Panel Tabpanel.activate–
Close Panelpanel.close–
Collapse Panel Sectionpanel.collapse–
Dismiss Panel Peekpanel.dismiss–
Float Panelpanel.float–
Focus Panelpanel.focus–
Focus Next Panelpanel.focus.next–
Focus Previous Panelpanel.focus.previous–
List Panelspanel.list–
Move Panelpanel.move–
Move Panel to Next Zonepanel.move.next_zone–
Redock Native Panel Sectionpanel.native_close–
Open Panelpanel.open–
Peek Collapsed Panelpanel.peek–
Pop Out Panelpanel.popout–
Reset Panel Sashpanel.reset_pair–
Resize Panel Sectionpanel.resize–
Resize Panel Sashpanel.resize_pair–
Resize Panel Sash Down (Down)panel.sash.down–
Resize Panel Sash Left (Left)panel.sash.left–
Reset Focused Panel Sashpanel.sash.reset–
Resize Panel Sash Right (Right)panel.sash.right–
Resize Panel Sash Up (Up)panel.sash.up–
Toggle Focused Panelpanel.toggle–

parameters.*

CommandIdToolbar
Add User Parameterparameters.add–
Change Parametersparameters.changeSOLID › MODIFY
Comment Parameterparameters.comment–
Complete Expressionparameters.complete–
Delete Parameterparameters.delete–
Export Parametersparameters.export–
Favorite Parameterparameters.favorite–
Parameter Graphparameters.graph–
Import Parametersparameters.import–
List Parametersparameters.list–
Move Parameterparameters.move–
Rename Parameterparameters.rename–
Parameter Usersparameters.users–

parts.*

CommandIdToolbar
Insert Fastenerparts.fastenerSOLID › INSERT
Insert Partparts.insert–
Standard Partsparts.library–
Change Part Sizeparts.resize–

plugin.*

CommandIdToolbar
Inspect Pluginplugin.inspect–
Install Pluginplugin.install–
List Pluginsplugin.list–
Run Pluginplugin.run–

pointcloud.*

CommandIdToolbar
Delete Point Cloudpointcloud.delete–
Edit Point Cloudpointcloud.edit–
Insert Point Cloudpointcloud.insertSOLID › INSERT
Point Cloudspointcloud.list–
Reload Point Cloudpointcloud.reload–

render.*

CommandIdToolbar
Layered Surface Agingrender.aging.describe–
Render Backendsrender.backends–
Render All Shotsrender.batchRENDER › BATCH
Frame Render Subjectrender.camera.fit–
Inspect Render Camerarender.camera.get–
Set Render Camerarender.camera.set–
Cancel Renderrender.cancel–
Save Clear Coatrender.coat.bind–
Remove Clear Coatrender.coat.unbind–
Compare Imagesrender.compareRENDER › RESULT
Move Render Comparison Dividerrender.compare.divider–
Lens and Creative Effectsrender.effects.describe–
Set Render Environmentrender.environment.set–
Export Render Imagerender.export–
Inspect HDR Sourcerender.hdr.inspect–
Apply Library Opaque Appearancerender.library.appearance.apply–
Inspect Render Library Resourcerender.library.describe–
Apply Built-in HDR Environmentrender.library.environment.apply–
Browse Render Libraryrender.library.list–
Prepare Physical Referencerender.library.physical.prepare–
Inspect Original Surface Maprender.library.preview–
Apply Authored Studio Lightsrender.library.studio.apply–
Prepare Authored Studio Lightsrender.library.studio.prepare–
Save Surface Bindingrender.library.surface.bind–
Prepare Authored Surface Mapsrender.library.surface.prepare–
Remove Surface Bindingrender.library.surface.unbind–
Find Render Library Targetsrender.library.targets–
Add Lightrender.lights.add–
Duplicate Render Lightrender.lights.duplicate–
List Render Lightsrender.lights.list–
Mute Lightrender.lights.muteRENDER SCENE › LIGHTS
Remove Render Lightrender.lights.remove–
Set Render Lightrender.lights.set–
Solo Lightrender.lights.soloRENDER SCENE › LIGHTS
Save Optical Bindingrender.optics.bind–
Remove Optical Bindingrender.optics.unbind–
Set Render Output Sizerender.output.size–
Check Render Requestrender.preflight–
Copy Render Check Reportrender.preflight.copy–
Apply Native Studiorender.preset.apply–
Review Native Studiorender.preset.resolve–
Review Fixed Sampling Budgetrender.quality.prepare–
Close Render Image (Escape)render.result.close–
Copy Render Imagerender.result.copy–
Discard Render Resultrender.result.discard–
Retain Displayed Render Resultrender.result.pin–
Regrade Linear Masterrender.result.regrade–
Select Render Resultrender.result.select–
View Render Imagerender.result.view–
Inspect Render Scenerender.sceneRENDER › SCENE
Apply Render Camerarender.settings.apply–
Save Render Settings with HDRrender.settings.capture_hdr–
Render Settingsrender.settings.get–
Save Render Settingsrender.settings.set–
Duplicate Render Shotrender.shot.duplicate–
Remove Render Shotrender.shot.remove–
Rename Render Shotrender.shot.rename–
Save Physical Render Backgroundrender.stage.backdrop–
Remove Render Stagerender.stage.clear–
Save Render Groundrender.stage.ground–
Render Image (R)render.startRENDER › IMAGE
Render Progressrender.status–
Import Surface Mapsrender.surface.import–
List Embedded Surface Imagesrender.texture.list–
Remove Embedded Surface Imagerender.texture.remove–

select.*

CommandIdToolbar
Clear Selectionselect.clear–
Selection filterselect.filter–
Select Same Lookselect.same_look–
Selectselect.set–

selection.*

CommandIdToolbar
Deleteselection.delete–
Get Selectionselection.get–

settings.*

CommandIdToolbar
Apply Settings Profilesettings.profile.apply–
Delete Settings Profilesettings.profile.delete–
Export Settings Profilesettings.profile.export–
Import Settings Profilesettings.profile.import–
Settings Profilessettings.profile.list–
Rename Settings Profilesettings.profile.rename–
Save Settings Profilesettings.profile.save–

ui.*

CommandIdToolbar
Toggle Assistant (Ctrl+Shift+A)ui.assistant–
Search Commands (S)ui.palette–
Show/Hide Selection (V)ui.toggle_visibility–

view.*

CommandIdToolbar
Ambient Occlusionview.ambient_occlusion–
View Anglesview.angle_readout–
Restore Named Viewview.apply–
Back View (Ctrl+2)view.back–
Bottom View (Ctrl+6)view.bottom–
Delete Named Viewview.delete–
Fit (F6)view.fit–
Front View (Ctrl+1)view.front–
Graphics Presetview.graphics_preset–
Gridview.grid–
Ground Shadowview.ground_shadow–
History layoutview.history_layout–
Home Viewview.home–
Isometric View (Ctrl+7)view.isometric–
Workspace layoutview.layout–
Left View (Ctrl+3)view.left–
List Named Viewsview.list–
Navigate Viewview.navigate–
Navigation Barview.navigation_bar–
Custom View Navigatorview.navigator–
Next Tab (Ctrl+Tab)view.next_tab–
Normal to Selectionview.normal_to–
Origin Axes Across the Whole Viewview.origin_axes_across_view–
Perspective viewview.perspective–
Previous Tab (Ctrl+Shift+Tab)view.previous_tab–
Rename Named Viewview.rename–
Render Browserview.render_browser–
Move Render Dockview.render_dockRENDER › SCENE
Render Focus Viewview.render_focus–
Render Frame Guidesview.render_frame–
Render Inspectorview.render_inspector–
Pause or Resume Live Renderview.render_live–
Render selectionview.render_select–
Render Stageview.render_stage–
Ribbon Tabs Onlyview.ribbon_tabs_only–
Right View (Ctrl+4)view.right–
New Named Viewview.save–
Shots Stripview.shots_strip–
Continuous Orbitview.spin–
Viewport Status Detailsview.status_details–
Viewport Status Itemsview.status_items–
Navigation Toolview.tool–
Drag to Zoomview.tool.zoom–
Top View (Ctrl+5)view.top–
Visual Styleview.visual_style–

STEP import

SolidCraft reads STEP (ISO 10303-21; AP203, AP214 and AP242 geometry) with its own reader in crates/kernel/src/step_in (written from the public standards; no third-party STEP code). A STEP file becomes an Import base feature on the timeline: it holds the file’s B-rep (the STEP text is kept in the design, so the design reopens without the file) and later features — fillets, holes, cuts sketched on its faces, shells — build on its bodies.

  • Open: File → Open (doc.open) accepts .step/.stp in any case and starts a new design; solidcraft part.step; solidcraft-cli eval part.step, solidcraft-cli run script.json --in part.step; the MCP open tool.
  • Insert into the current design: File → Insert STEP… or SOLID → INSERT (file.insert_step {path}).
  • Drag and drop a STEP file on the window: inserted when the design has features, opened otherwise.

Imports are components

Every import (STEP, IGES, 3MF, STL, OBJ; inserted, dropped, opened as a new design, or run as file.insert_step, file.insert_mesh or doc.open from a script, the CLI or MCP) becomes a new component named after the file (bracket.step → component bracket), placed in the active component at the file’s origin. The Import feature belongs to it, so the file’s bodies never land loose next to your own. Opening a file as a design makes the component too, so the browser shows the file’s name. The command’s reply reports it: component (id), component_name and occurrence. With the top level (or any component above the part) active, an extrude, revolve or press-pull from a sketch on a part’s face cuts or joins that part’s body in its own component, wherever the part is placed, and every placement of the part shows the change. It is refused, with the reason, for a part linked to another design and for a face on a further placement of a part placed more than once (activate that placement to change it there).

A STEP assembly keeps its structure: each product under the file’s root becomes a component nested under the import’s, named after the product, at the place the file gives it. IGES groups and subfigures become nested components the same way. A part (or sub-assembly) the file places more than once becomes one component with an occurrence per placement: the Import feature builds the part once and skips the file’s later copies (skip in the feature, format 22), and the occurrences show it everywhere else. Placements that are scaled, mirrored or otherwise non-rigid keep their geometry in place in the parent component. The reply adds subcomponents (how many components were made below the import’s) and rigid_joints.

The Browser reuses body, sketch and construction-plane membership, child lists and resolved placements while the camera moves. Large assemblies keep their tree open without resolving every part and folder again on each frame. Geometry edits, assembly changes and uncaptured or reverted positions refresh those queries; selection, visibility and locking are read live.

Hover picking pauses while the mouse moves the view and resumes when navigation stops. A 3D mouse moving the view leaves it on: the pointer hand is free, so preselection and clicks keep working while the other hand turns the view. Picking uses the current camera and the cached body bounds directly in model order, so finding each body’s bounds does not scan the assembly again.

Hovering an assembly’s Browser row retains its whole-body highlight buffers during rotation, panning and zoom; the GPU transforms those buffers with the camera. The origin and construction widgets resize independently. Whole-body highlight generation scans each mesh once rather than scanning it again for every face. Sketch profile feedback still updates its projected curves and lift toward the eye as the view changes.

The contents of an imported assembly are rigid by default: rigid joints (a rigid group, its glyphs not drawn) hold each part where the file put it in its parent, so moving the import’s component moves everything, and an inner part doesn’t move on its own; joint degrees of freedom count the whole import as one body. To joint the parts one by one, release them with Unlock Contents on the component’s browser menu (component.unlock {component}), one undo step. Undo takes a whole import away in one step.

Designs saved before imports made components keep their Import where it is (no component is made for it) and rebuild as they did.

Read: manifold solid breps (with voids), faceted breps, shell-based surface models; planes, cylinders, cones, spheres, tori, B-spline and rational B-spline surfaces (with knots, Bézier, uniform, quasi-uniform), surfaces of revolution and linear extrusion, trimmed surfaces; lines, circles, ellipses, hyperbolas and parabolas (as exact rational curves, which export writes back as exact rational B-splines), B-spline curves, polylines, trimmed/surface/seam curves; length and angle units (SI prefixes, conversion-based units such as inches); product names (meaningful solid names are kept, placeholders use part names; names below chooses); surface colours; assemblies (next-assembly-usage occurrences placed by item-defined transformations, and mapped items) — the product tree is kept in the feature and becomes the import’s components (above). Files are untrusted: sizes, entity counts and nesting are capped and every kernel call is guarded. What cannot be read becomes a warning on the feature (a face left out gives an open body); not read yet: offset surfaces, pcurve-only edges.

Extruded circular and elliptical faces keep their profile’s parameter seam outside the face trim, or on an explicit seam edge. Narrow faces such as rounded gear tooth tips therefore stay narrow through the full thickness instead of wrapping around the supporting ellipse in the middle.

Files from other CAD systems

Files from different systems differ in which way is up and in what they call their solids. doc.open, file.insert_step and the MCP open tool take two options for STEP and IGES files; without them a file comes in as written.

  • up: "z" (the default) keeps the file’s axes. Many systems write Y up, so such a part lies on its back in SolidCraft’s Z-up space. "up": "y" turns the import’s component a quarter turn about X (+90°: Y becomes Z), as the window’s “Stand it up” offer does, so an assembly’s parts and instances turn together. The import and the turn are one undo step, the reply names the turned occurrence as stand_up, and only the file’s component turns, not what is already in the design. The window offers to stand a part up after a STEP file opens unless the call said which way is up (up: "z" as well as "y") or the file came from SolidCraft, which already writes Z up. A STEP reply’s system is the originating system the file’s header names (SolidCraft for SolidCraft’s own exports). A header that names SolidWorks (in FILE_NAME’s originating system or preprocessor version, or in FILE_DESCRIPTION; for IGES, the start of the global section’s native system ID, which becomes the originating system) says the file has Y up: the reply carries exporter (SolidWorks) and exporter_up (y), and the window asks in the “Stand this part up?” dialog, which says so (“Exported by SolidWorks, which usually saves Y up”). The first opening never turns a file without asking, because the exporter may have saved it Z up; unchanged files reuse a remembered stand-up answer unless Preferences › Design › Remember stand-up answers is off; a component’s right-click menu has Placement → Turn Up Axis (Y up → Z up, Z up → Y up: a quarter turn about X, one undo step) for fixing it any time. SolidCraft’s own files say z and are left alone. Any other exporter is unknown, and such files are often upright already, so by default the window first looks at how the bodies rest: a part rests on its flat faces, so the flat area facing down Z is weighed against the larger flat area facing down or up Y (faces within 10° of the direction; one side must have at least 1.5 times the other’s, else the thinner of the box’s Y and Z sizes decides). Unless the file clearly stands upright, the window shows a small note over the view (“Did this part open the wrong way up?” with Stand it up and Close) that goes by itself after 10 seconds and never blocks input; a file that clearly stands upright gets nothing, and an unclear shape still gets the note. A SolidWorks file whose bodies clearly stand on Z already gets the note instead of the dialog, since it may have been saved Z up. Preferences › Design › “For files from unknown programs” picks the dialog for every file from another program, SolidWorks too, without looking at how it rests (Ask in a dialog, as before) or no offer (Don’t offer) instead. The dialog and the note say “assembly” when the file brought more than one body or a component tree.
  • names: "auto" (the default) keeps meaningful solid names and replaces placeholders such as Body1 or Body 2 (3) with the nearest named product, then the occurrence name if no product is named. "solid" keeps the solid’s name; "product" chooses the part’s name; "instance" chooses the assembly occurrence (Post-2), falling back to the product. If the chosen name is missing, the solid’s name remains. Repeated names are numbered (Post, Post (2)). Names are stored with the Import feature and survive reopening the design. Files without names or assembly metadata retain generated body names and a flat structure.
  • Vertices off their curves: some systems write an edge’s vertex a little beside the end of its curve, often further off than the accuracy the file declares (UNCERTAINTY_MEASURE_WITH_UNIT). A vertex up to the file’s own tolerance off, or up to 1/1000 of the model’s size (never more than 1 mm), is read as that end of the edge: the curve’s end is moved onto the vertex. A vertex further off leaves its face out, with a warning that says how far off it is.
{"command": "file.insert_step", "params": {"path": "frame.step", "up": "y", "names": "instance"}}

Imported appearances

Imported body colours and face colours are retained through later features. Body colours remain the fallback under explicit design appearances and materials; face colours follow persistent face names. A removed face has no colour assignment to display on the resulting body.

IGES

IGES 5.3 files (.igs/.iges) open, insert and drop the same way: crates/kernel/src/iges_in.rs (written from the published specification) restates the file as STEP and the STEP reader reads it, so the feature holds STEP text. Read: manifold solid B-reps (186) with their shells, faces, loops, edge and vertex lists; trimmed (144) and bounded (143) surfaces, and bare B-spline patches, sewn within their owning group into a solid by their shared edges (an open body when they do not close); groups (402 forms 1, 7, 14 and 15), subfigure definitions (308) and instances (408), retaining names, nesting and placements; lines, circular arcs, ellipses, composite curves, rational B-spline curves; planes (108, 190), cylinders, cones, spheres, tori, rational B-spline surfaces, surfaces of revolution and tabulated cylinders; transformation matrices; units; colours (314 and the predefined numbers); body names (name properties, else labels). Not read yet: ruled and offset surfaces, curves on surfaces given only in parameter space. Export (file.export to .igs) writes each body as a manifold solid B-rep on the same surfaces a STEP export uses, with its name and colour.

FreeCAD and Rhino

FreeCAD .FCStd and Rhino .3dm files open, insert and drop the same way, in the desktop app, the browser app and the CLI. They are read with cadmpeg (Apache-2.0, crates/io/src/native_cad.rs): cadmpeg detects the format from the bytes, decodes it into its neutral model under resource caps, checks that model and writes it as STEP AP214, which the STEP reader then reads. The feature holds that STEP text, so the design reopens without the original file. A FreeCAD body’s intermediate shapes (each pad’s, pocket’s and tool’s, and its tip’s, which FreeCAD shows through the body) and shape binders are not imported: only the bodies come in. Losses cadmpeg reports become warnings on the feature, and a file without an importable solid is refused with a message. cadmpeg runs inside the panic guard. How much of a file is read follows cadmpeg’s support for that format (format support).

FreeCAD feature history

A FreeCAD file’s design history is rebuilt as features by default (history: features on doc.open and file.insert_step; Preferences › General › FreeCAD files). cadmpeg decodes the file at full depth and crates/io/src/native_cad/design.rs writes one feature per FreeCAD feature, in history order, into the file’s component:

FreeCADSolidCraft
Sketcher sketch: lines, circles, arcs, pointsSketch on the same plane, with FreeCAD’s solved geometry and each constraint that keeps it (coincident, horizontal, vertical, parallel, perpendicular, tangent, equal, point on object, symmetric, midpoint, fixed, distance, horizontal and vertical distance, radius, diameter, angle); dimensions become parameters
Pad, Pocket, Part Extrusion: blind, through all, symmetric, two-sided, along the sketch normal or an edgeExtrude (new body, join, cut); a loop inside another is a hole, as FreeCAD makes faces
Revolution, Groove, Part Revolution: angle, symmetric, two-sidedRevolve
Fillet (constant radius), Chamfer (distance, two distances, distance and angle)Fillet, Chamfer on the same edges
Part and PartDesign box, cylinder, cone, sphere, torus (whole)Box, Cylinder, Cone, Sphere, Torus
Mirrored, LinearPattern (one direction), PolarPattern of featuresMirror, Pattern of the same features
Body placement, datum planes, axes and points, shape bindersapplied to the features / not rebuilt (sketches on them keep their place)

The rebuilt design is evaluated and compared with the file’s own solids: each body must have the volume, extent and centre of one of them. When a feature cannot be rebuilt (another operation, an up-to-face extent, a tapered pad, an ellipse or B-spline in a sketch, a MultiTransform, a feature after a body’s tip), SolidCraft cannot build one (some fillets) or the comparison fails, the file’s solids come in as an Import feature as before, with a warning that says why. A constraint that would move FreeCAD’s geometry is left out, and a warning counts those. Expressions and spreadsheets are not carried over: the parameters hold the values FreeCAD computed. Rhino files have no such history; their solids come in as before.

Only codecs for open formats are compiled in: the workspace turns off cadmpeg’s default features and enables fcstd, rhino and step (the writer). cadmpeg’s codecs for commercial CAD formats are not built, and neither is its sat codec, which decodes ShapeManager streams with ACIS as one dialect, so ACIS .sat/.sab files are not read yet. crates/io/src/native_cad.rs is the one place cadmpeg is called; a cadmpeg-based STEP reader could be added there later behind a setting, to compare with the STEP reader above without changing it.

Command reference

Browse commands by their names and default keyboard shortcuts. Entries are organized by command group and panel; ungrouped commands follow by area.

For step-by-step instructions, start with the window tour, sketching guide or solid features guide. Practical command guides below explain prerequisites, options and examples in place. Entries without a practical guide link to an overview instead. Contribute a guide, tip, demo or model link.

Expand Automation details for the command ID and parameters used by scripts, the CLI and MCP. In that notation, ? marks an optional parameter and expr means a number or an expression with units.

This catalog is generated from solidcraft-cli commands by cargo xtask book (646 commands).

Practical guides:

SOLID › CREATE

Create Sketch

Command group: SOLID › CREATE

Read the solid features guide.

ParameterTypeRequiredDescription
planeXY|XZ|YZ| construction plane name or id | {origin?: [x,y,z] numbers in mm, default [0,0,0] (not an expression), x_dir, y_dir}| {face: [world x,y,z], body?: exact world body, index?: face index, path?: complete occurrence ids, fixed?: bool}Yessketch x, y along world +X, +Y on XY; +X, -Z on XZ; +Y, +Z on YZ; in world coordinates unless frame says otherwise; a named plane is the active component's own
offsetexprNo
flipbool | "x" | "y"Nonot on a face: sketch the plane from its other side, so the normal and the default extrude direction are reversed; a half turn about the plane's own x or y axis, true picks the one keeping the sketch's y axis upward; offset still moves along the unflipped normal
frameworld| componentNodefault; which coordinates an explicit plane is in; the reply's `frame` says which, with a `note` when the active component is moved or turned
project_edgesboolNo
activate_partboolNodefault false; on a face of a body of another component than the active one: activate that component at the picked placement first, as component.activate would, and make the sketch there; it stays active afterwards and the reply's `activated` names it
nameNo
Automation details

Command ID: sketch.create

Parameters:

plane: XY|XZ|YZ (sketch x, y along world +X, +Y on XY; +X, -Z on XZ; +Y, +Z on YZ) | construction plane name or id | {origin?: [x,y,z] numbers in mm, default [0,0,0] (not an expression), x_dir, y_dir} (in world coordinates unless frame says otherwise; a named plane is the active component's own) | {face: [world x,y,z], body?: exact world body, index?: face index, path?: complete occurrence ids, fixed?: bool}; new face attachments require a unique certified source (a flat face of a sheet metal body attaches to its panel instead, in the flat pattern's coordinates), fixed:true explicitly freezes the selected plane; offset?: expr, flip?: bool | "x" | "y" (not on a face: sketch the plane from its other side, so the normal and the default extrude direction are reversed; a half turn about the plane's own x or y axis, true picks the one keeping the sketch's y axis upward; offset still moves along the unflipped normal), frame?: world (default) | component (which coordinates an explicit plane is in; the reply's `frame` says which, with a `note` when the active component is moved or turned), project_edges?: bool, activate_part?: bool (default false; on a face of a body of another component than the active one: activate that component at the picked placement first, as component.activate would, and make the sketch there; it stays active afterwards and the reply's `activated` names it), name?

Extrude

Command group: SOLID › CREATE

Default keyboard shortcut: E

Turn a flat sketch region into a solid, or add or remove material from an existing body. For example, a rectangle extruded 20 mm becomes a rectangular block.

Before you start

  • For a solid extrusion, finish a sketch with at least one closed region. Select the region you want, rather than an individual edge or point.
  • You can also select a planar face of an existing body instead of a sketch.
  • To add or remove material, an existing body must meet the extrusion. Choose New Body when you want an independent piece.
  • Thin Extrude uses sketch curves instead of filled regions; open chains are allowed. This is useful for a wall or a ring.

Options

ChoiceWhat it doesWhen to use it
ProfilesChooses the filled sketch regions to extrude. A loop inside another loop makes a hole.Select just the regions you need when a sketch contains several.
DistanceSets how far the shape extends. Values can include units, such as 20 mm, or a named design parameter.Make a part to a known size.
Reverse DirectionSends the extrusion to the other side of the sketch.The preview points away from the intended body.
SymmetricSplits the entered total distance equally across the sketch plane. 10 mm extends 5 mm on each side.Keep the sketch at the middle of a part.
Two SidesSets a separate distance on each side.The sketch is between two unequal extensions.
Through AllReaches through the target body.Cut a hole that must stay open when the body becomes thicker.
To ObjectStops at a selected target face, vertex or point. An offset can stop before or beyond that target.Let the end follow another part of the design.
StartStarts at the sketch, at an offset from it, or from a selected object.Leave a gap before the extrusion begins.
Taper AngleSlopes the side walls.Make drafted sides; reduce the angle if the profile’s narrow details cannot support it.
OperationNew Body makes a separate piece; Join adds material; Cut removes it; Intersect keeps only the overlap.Choose the intended result explicitly when the automatic preview is not what you want.
Target bodiesChooses which existing bodies take part in a join, cut or intersection.Several bodies lie in the extrusion’s path.
Thin ExtrudeBuilds walls along curves; thickness and side control where the material lies. Two-sided walls can have different thicknesses.Make ribs, walls or rings without drawing both wall boundaries.

Try it: a 20 mm block

  1. Create a sketch on the XY plane and draw a closed rectangle.
  2. Finish the sketch and choose Extrude.
  3. Select the rectangular region, set Distance to 20 mm, and choose New Body.
  4. Check the preview’s direction and confirm. You should have one solid block.
  5. Edit the extrusion in the timeline to change its distance later.

Tips and common mistakes

  • Do check the operation before confirming. Automatic operation can join an addition or cut into an existing body; it is not a promise of a separate part.
  • Do use Through All for a cut that should follow later thickness changes.
  • Don’t expect an open outline to make a filled solid. Close it, or choose Thin Extrude for a wall.
  • Don’t assume a symmetric distance is the distance on each side. New symmetric extrusions use the total distance; older saved designs can retain their earlier convention.
  • Taper with a two-sided or symmetric extent is not supported. A very steep taper can also be refused rather than creating an invalid result.

Examples and community resources

The bracket starts with a closed L-shaped profile; another extrusion removes the two holes. Its dimensions are controlled by design parameters.

ResourceWhat to exploreAuthor and licence
Editable bracket recipeA complete SolidCraft example using an extrusion, a fillet and a cut.SolidCraft contributors; MIT OR Apache-2.0.

Contribute a tip, demo, model or screenshot to add a reviewed resource beside this explanation.

See also

Revolve turns a profile around an axis. Sweep carries a profile along a path. Flange creates sheet metal with a thickness rule.

ParameterTypeRequiredDescription
distanceexprYesor through_all: true
taperangle exprNopositive: outward, so the far end is larger along the extrusion direction; negative: inward, so the far end is smaller
cornerpreserve_curvature|tangent|roundNohow a taper closes a sharp corner its offset opens: each wall continues as itself, a line's plane or an arc's cone, until it meets its neighbour; each continues straight along its end tangent; or an arc about the corner. Without it, each profile preserves curvature where every curve is a line or an arc and takes tangent where another curve has a corner, and the feature's warning says so; a corner rule given is kept, and refused where the kernel cannot build it
sketchid|nameNodefault: active or last sketch
profilesall | [index] | [{curves: [ids]}]| [{point:[x,y]}]Nothe profile whose outer loop is made of exactly these curves, or pieces of them; found again on every recompute, so it follows parameter changes; islands inside are its holes; an error when no profile or several match; index and point picks are stored by their curves when that finds the same profile; a pick on a drawn profile that reference geometry, such as the projected outline of the face the sketch is on, cuts into regions takes the whole drawn profile
exact_regionsboolNoeach index or point pick is exactly the region it names, as the dialogs send
face[x,y,z]Noextrude a planar body face instead of a sketch profile
faces[[x,y,z],…]Nocoplanar faces of one body
face_bodynameNowhose face, where faces of several bodies meet at the point
fixedtrueNowith face or faces: start on the face's plane as a fixed plane, which won't follow later changes to the face; for a face whose history can't keep an attachment, such as a converted mesh's
directionpositive|negative|symmetricNodistance is total thickness, split evenly around the start plane
conventiontotal_distance|legacy_per_sideNohow a symmetric distance is read; default total_distance, split evenly around the start plane; legacy_per_side: the distance on each side, as older symmetric extrudes keep
distance2exprNoTwo Sides: a second distance on the side direction leaves free, the first side may be through_all or to; with direction: negative the first side goes against the normal and distance2 along it; a negative distance only turns its own side over
start_offsetexprNoalong the sketch normal, also with direction: negative
to[x,y,z]NoTo Object: a face, vertex or point; to_offset?: expr past it
from[x,y,z]NoFrom Object: start at that face or point
operationnew|join|cut|intersect|autoNocut into a body, otherwise join existing solids; new in an empty component
touchingseparate|one_bodyNowhat a join makes of pieces that share no face: separate, the default, makes each piece that touches another only along an edge or at a point a body of its own and keeps pieces that touch nothing in one body; one_body keeps every piece in one body
targets[body]NoJoin from a face, or from a sketch on a body's face, defaults to that face's body; explicit targets or participants override
nameNo
body_nameNo
thintrueNoThin: a wall along sketch curves instead of the profiles
curves[curve ids]Noopen chains and closed loops, joined where they meet end to end; default: every drawn curve of the sketch
thicknessexprYes
sidecenter|inside|outside|two_sidesNoa closed loop's inside is the area it encloses, an open chain's the side it bends toward, a straight line's the left of its first curve as drawn; default center
thickness2exprNotwo_sides: the thickness outside
Automation details

Command ID: solid.extrude

Parameters:

distance: expr (or through_all: true); taper?: angle expr (positive: outward, so the far end is larger along the extrusion direction; negative: inward, so the far end is smaller); corner?: preserve_curvature|tangent|round (how a taper closes a sharp corner its offset opens: each wall continues as itself, a line's plane or an arc's cone, until it meets its neighbour; each continues straight along its end tangent; or an arc about the corner. Without it, each profile preserves curvature where every curve is a line or an arc and takes tangent where another curve has a corner, and the feature's warning says so; a corner rule given is kept, and refused where the kernel cannot build it); sketch?: id|name (default: active or last sketch); profiles?: all | [index] | [{curves: [ids]}] (the profile whose outer loop is made of exactly these curves, or pieces of them; found again on every recompute, so it follows parameter changes; islands inside are its holes; an error when no profile or several match) | [{point:[x,y]}] (index and point picks are stored by their curves when that finds the same profile; a pick on a drawn profile that reference geometry, such as the projected outline of the face the sketch is on, cuts into regions takes the whole drawn profile); exact_regions?: bool (each index or point pick is exactly the region it names, as the dialogs send); default: every drawn profile, except that a loop inside another is its hole and a loop inside that hole is material again; face?: [x,y,z] (extrude a planar body face instead of a sketch profile); faces?: [[x,y,z],…] (coplanar faces of one body); face_body?: name (whose face, where faces of several bodies meet at the point); fixed?: true (with face or faces: start on the face's plane as a fixed plane, which won't follow later changes to the face; for a face whose history can't keep an attachment, such as a converted mesh's); direction?: positive|negative|symmetric (distance is total thickness, split evenly around the start plane); convention?: total_distance|legacy_per_side (how a symmetric distance is read; default total_distance, split evenly around the start plane; legacy_per_side: the distance on each side, as older symmetric extrudes keep); distance2?: expr (Two Sides: a second distance on the side direction leaves free, the first side may be through_all or to; with direction: negative the first side goes against the normal and distance2 along it; a negative distance only turns its own side over); start_offset?: expr (along the sketch normal, also with direction: negative); to?: [x,y,z] (To Object: a face, vertex or point; to_offset?: expr past it); from?: [x,y,z] (From Object: start at that face or point); operation?: new|join|cut|intersect|auto (cut into a body, otherwise join existing solids; new in an empty component); touching?: separate|one_body (what a join makes of pieces that share no face: separate, the default, makes each piece that touches another only along an edge or at a point a body of its own and keeps pieces that touch nothing in one body; one_body keeps every piece in one body); targets?: [body] (Join from a face, or from a sketch on a body's face, defaults to that face's body; explicit targets or participants override); name?; body_name?; thin?: true (Thin: a wall along sketch curves instead of the profiles) with curves?: [curve ids] (open chains and closed loops, joined where they meet end to end; default: every drawn curve of the sketch), thickness: expr, side?: center|inside|outside|two_sides (a closed loop's inside is the area it encloses, an open chain's the side it bends toward, a straight line's the left of its first curve as drawn; default center), thickness2?: expr (two_sides: the thickness outside); corners are mitred, crossing walls are one

Revolve

Command group: SOLID › CREATE

Turn a sketch profile around an axis to make a round part: a shaft, a pin, a pulley or a bushing. You draw the half-section the way it is drawn on a lathe print, and Revolve spins it.

Before you start

  • Draw a closed profile of one side of the part: the half-section, from the centre line out.
  • You need an axis to spin about: a sketch line, an origin axis, a construction axis, a straight edge, or a cylinder face.
  • The profile must stay on one side of the axis. It can touch the axis but not cross it.
  • A flat face of the part can also be used as the profile.

Options

Choice in the dialogMeaningExample or reason to choose it
ProfileThe closed area to spin.The half-section of a stepped shaft.
AxisThe line it spins around.The sketch line along the centre of the shaft, or the X axis.
TypeAngle spins by the angle you type. To Object spins until it reaches a face or point you pick in the To Object box, which replaces Direction and Angle.Angle for most parts.
DirectionOne Side spins one way. Two Sides spins a different angle each way. Symmetric splits the angle evenly both ways from the sketch. Shown for Type: Angle.Symmetric for a 90° segment centred on the sketch.
AngleHow far to spin.360 deg for a full round part.
Angle 2The angle on the other side, for Two Sides.30 deg one way, 60 deg the other.
OperationNew Body, Join, Cut or Intersect. Cut removes material from an existing body.Cut to turn a groove into an existing shaft.

Try it: a stepped pin

  1. Start a sketch on the XY plane. If the document is in mm, type in after each value (for example 0.25in). Draw this closed outline from the origin along the X axis: up 0.25 in, over 1 in, up 0.125 in, over 0.5 in, down 0.375 in back to the X axis, and close it along the axis. Click Finish Sketch.
  2. Click Revolve. Click the profile.
  3. Click Axis, then click the sketch line along the X axis. Check the Axis box: if it reads Y, you picked the wrong axis and the preview comes out flat. Pick again.
  4. Leave Type on Angle, Direction on One Side, and Angle at 360 deg. Set Operation to New Body so the pin is its own part.
  5. Click OK. You have a pin 0.5 in diameter by 1 in long with a 0.75 in diameter head 0.5 in long.
  6. For a groove, start a new sketch on the XY plane (it runs through the pin’s centre line). Draw a rectangle that cuts into the shaft, with corners at (0.4 in, 0.20 in) and (0.5 in, 0.30 in), and click Finish Sketch. Revolve it with Operation set to Cut. For the Axis, click the sketch line along the X axis, or click the round side of the pin (not the flat end); its centre line is used. The result is a groove 0.1 in wide and 0.05 in deep.

Tips and common mistakes

  • Do draw the centre line as part of the profile, or as a sketch line in the same sketch. Then picking the axis is one click and it moves with the sketch.
  • Do use Direction: Symmetric for a partial revolve that should sit centred on the sketch plane.
  • Don’t let the profile cross the axis. Keep the whole half-section on one side.
  • Don’t draw the full section of the part. Draw one half only; Revolve makes the other half.

See also

Extrude pushes a profile straight instead of spinning it. Thread adds a thread to the revolved shaft. Chamfer breaks its edges.

ParameterTypeRequiredDescription
axissketch line id | x | y| X|Y|Z| {type: world, axis: X|Y|Z}| {sketch, curve}| construction axis name| {origin, dir}| {edge|face: [x,y,z]}Yessketch axes; world; as returned by timeline.inspect; a line of another sketch, followed in that sketch; followed when edited; origin: numbers or length expressions, followed when parameters change; a straight or circular edge, or a cylinder, cone or torus face: about its axis, followed as the model changes
angleexprNodefault 360 deg
angle2exprNoTwo Sides: the other side's angle
directionsymmetricNoangle split either side, independent of axis direction
conventionsketch_normal|legacy_axis_startNodefault sketch_normal; legacy retains older angular starts
to[x,y,z]NoTo Object: turn until that face, vertex or point
sketchNo
profiles[x,y,z]Noa planar body face as the profile
face[x,y,z]Noa planar body face as the profile
face_bodynameNowhose face, where faces of several bodies meet
fixedtrueNothe face's plane as a fixed plane, as solid.extrude's
operationNo
touchingNo
targetsNo
nameNo
body_nameNo
Automation details

Command ID: solid.revolve

Parameters:

axis: sketch line id | x | y (sketch axes) | X|Y|Z (world) | {type: world, axis: X|Y|Z} (as returned by timeline.inspect) | {sketch, curve} (a line of another sketch, followed in that sketch) | construction axis name (followed when edited) | {origin, dir} (origin: numbers or length expressions, followed when parameters change) | {edge|face: [x,y,z]} (a straight or circular edge, or a cylinder, cone or torus face: about its axis, followed as the model changes); angle?: expr (default 360 deg); angle2?: expr (Two Sides: the other side's angle); direction?: symmetric (angle split either side, independent of axis direction); convention?: sketch_normal|legacy_axis_start (default sketch_normal; legacy retains older angular starts); to?: [x,y,z] (To Object: turn until that face, vertex or point); sketch?, profiles? | face: [x,y,z] (a planar body face as the profile) and face_body?: name (whose face, where faces of several bodies meet) and fixed?: true (the face's plane as a fixed plane, as solid.extrude's), operation?, touching?, targets?, name?, body_name?

Sweep

Command group: SOLID › CREATE

Drag a sketched profile along a path to make a solid of the same section all the way: bent flat bar or angle, a handle, a rail, or, with Cut, a groove that follows a curve, such as an O-ring face groove around a rounded-rectangle cover.

Before you start

  • You need a profile sketch (a closed shape) and a path: either a second sketch of connected curves, or a chain of a part’s edges. An edge path needs no second sketch.
  • Draw the profile on a plane square to the start of the path, touching the path. The profile then runs along the path from there.
  • For a plain round tube or rod, Pipe is quicker: it needs only the path and a diameter, plus a wall thickness for a hollow tube. Without a wall it makes a solid rod.

Options

Choice in the dialogMeaningExample or reason to choose it
ProfileThe closed sketch shape to sweep.A 0.5 in × 0.25 in rectangle for flat bar.
PathThe curves or edges the profile follows.A line, an arc and a line for a bend.
OrientationTurn with Path: the profile turns to stay square to the path, as bent bar does. Keep as Drawn: the profile keeps the direction it was drawn in. Follow Direction: the profile stays square to the path, and the Direction you pick fixes how it turns about the path.Turn with Path for almost everything.
DirectionAn axis, sketch line or straight edge, used only with Follow Direction.The Z axis, to keep a profile upright on a path that climbs.
Rail, Second RailGuide curves the profile’s edge follows. With a rail, the profile turns and changes size to stay on it. Rails replace Orientation, Twist and End Scale; Rail Fit sets how they reshape the profile.A curve that widens a handle toward one end.
OperationNew Body when the design has no body yet; otherwise it starts at Join. Cut takes the swept shape out of the part.Cut for a groove.
Twist (Advanced)Turns the profile by this angle from the start of the path to the end. It starts at 0 deg.90 deg for a twisted bar.
End Scale (Advanced)The profile’s size at the end of the path, as a factor of its size at the start. It starts at 1.0.5 for a section that tapers to half.
Rail Fit (Advanced)With a rail: Scale grows the whole profile evenly to meet the rail; Stretch stretches it only toward the rail.Scale to keep the section’s proportions.

Try it: bend a piece of flat bar

  1. Start a sketch on the XY plane. From the origin, draw a 3 in line along X, a tangent arc of 1 in radius turning 90°, and a 2 in line. Finish the sketch.
  2. Start a sketch on the YZ plane. Draw a 0.5 in × 0.25 in rectangle centered on the origin. Finish the sketch.
  3. Click Sweep. Click Profile and pick the rectangle. Click Path and pick the three path curves.
  4. Leave Orientation on Turn with Path and click OK. The bar follows the bend, square to the path all the way round.

Tips and common mistakes

  • Do start the profile on the path, square to it. A profile away from the path sweeps offset from it.
  • Do keep bend radii bigger than the profile’s half-width, or the inside of the bend folds into itself.
  • Do use Cut for a groove of constant section along a curve, such as an O-ring face groove: sketch the groove’s section where the path starts and sweep it around.
  • Do type the unit with the number when the print uses another unit. In a millimetre design, typing 0.5 in enters 12.7 mm.

See also

Pipe sweeps a round section with only a diameter. Extrude sweeps a profile along a straight line. Revolve sweeps it around an axis.

ParameterTypeRequiredDescription
sketchprofile sketchYes
profilesNo
path_sketchsketchYes
path[curve ids in order]Yes
path_bodybody nameNo
path_edges[points on its edges]Nothe path is a chain of that body's edges, instead of a sketch's curves
orientationperpendicular| parallel| [x,y,z]Nodefault: square to the path, turning with it; as drawn: the profile moves without turning; the path must keep crossing its plane; square to the path, its turn about the path fixed by this direction
twistangle exprNothe profile turns this much about the path over its length; within 4 turns
scaleexprNothe profile's scale at the end, linear along the path; 0.01…100
rails[{sketch?: sketch (default path_sketch), curves: [curve ids in order]}]Noguide rails, one or two, each meeting every plane square to the path once along it, from the end nearest the path's start: one rail turns the profile so its point where the rail starts stays on the rail and scales it with the rail's distance from the path; two rails carry it between them, its points where they start staying on both; rails take the default orientation, no twist and no scale, and a path without sharp corners
rail_fitscale| stretchNodefault: the profile grows and shrinks evenly; only across to the rail, between the rails
operationNo
touchingNo
targetsNo
nameNo
body_nameNo
Automation details

Command ID: solid.sweep

Parameters:

sketch: profile sketch; profiles?; path_sketch: sketch; path: [curve ids in order]; path_body?: body name; path_edges?: [points on its edges] (the path is a chain of that body's edges, instead of a sketch's curves); orientation?: perpendicular (default: square to the path, turning with it) | parallel (as drawn: the profile moves without turning; the path must keep crossing its plane) | [x,y,z] (square to the path, its turn about the path fixed by this direction); twist?: angle expr (the profile turns this much about the path over its length; within 4 turns); scale?: expr (the profile's scale at the end, linear along the path; 0.01…100); rails?: [{sketch?: sketch (default path_sketch), curves: [curve ids in order]}] (guide rails, one or two, each meeting every plane square to the path once along it, from the end nearest the path's start: one rail turns the profile so its point where the rail starts stays on the rail and scales it with the rail's distance from the path; two rails carry it between them, its points where they start staying on both; rails take the default orientation, no twist and no scale, and a path without sharp corners); rail_fit?: scale (default: the profile grows and shrinks evenly) | stretch (only across to the rail, between the rails); operation?, touching?, targets?, name?, body_name?

Loft

Command group: SOLID › CREATE

Read the solid features guide.

ParameterTypeRequiredDescription
sections[{sketch, profiles?} | {sketch, point: id} (an apex)]Yes2…50, in order; matching hole counts, no holes at an apex
rails[{sketch, curves: [ids in order]}]Noone or two guide rails from the first section to the last, through every section's outline: the loft follows them between the sections; not with sections that have holes
rail_fitscale|stretchNodefault scale: with one rail the sections grow evenly with the rail's distance from their middle, with two with the distance between the rails; stretch: only that way
operationNo
touchingNo
targetsNoJoin excludes hidden bodies from automatic targets; explicit targets or participants may include them
nameNo
body_nameNo
Automation details

Command ID: solid.loft

Parameters:

sections: [{sketch, profiles?} | {sketch, point: id} (an apex)] (2…50, in order; matching hole counts, no holes at an apex); rails?: [{sketch, curves: [ids in order]}] (one or two guide rails from the first section to the last, through every section's outline: the loft follows them between the sections; not with sections that have holes); rail_fit?: scale|stretch (default scale: with one rail the sections grow evenly with the rail's distance from their middle, with two with the distance between the rails; stretch: only that way); operation?, touching?, targets? (Join excludes hidden bodies from automatic targets; explicit targets or participants may include them), name?, body_name?

Box

Command group: SOLID › CREATE

Make a box from a length, width and height: a block of stock, a plate, a fixture base, or a block to cut features into. Place it by a corner or by its center, at a point you pick or at the X, Y and Z you type.

Before you start

  • You don’t need a sketch or a body. A box can start a new design.
  • Length runs along X, Width along Y and Height along Z.
  • Placement decides what the point is: Corner puts one corner of the box there, Center puts the middle of the box there.

Options

Choice in the dialogMeaningExample or reason to choose it
PointA vertex, edge, face or point to place the box at. Leave it empty to use X, Y and Z.A corner of another block, to stack a box on it.
Length, Width, HeightThe size of the box along X, Y and Z. Each starts at 20 mm.3 in × 2 in × 0.75 in for a plate.
PlacementIt starts at Corner. Corner: the point is the box’s low corner, and the box runs in +X, +Y and +Z from it. Center: the point is the middle of the box.Corner at the origin so the part’s dimensions start from 0, 0, 0, like a work offset on a corner. Center for a part dimensioned from its middle.
X, Y, ZWhere the point is, when none is picked.0, 0, 0.
OperationIt starts at New Body. New Body makes a separate body. Join adds it to a body it touches. Cut takes it out. Intersect keeps only the material inside both.New Body for a fresh block. Cut for a square pocket or step.

Try it: make a plate from the corner

  1. In a new design, click Box.
  2. Set Length to 3 in, Width to 2 in and Height to 0.75 in.
  3. Set Placement to Corner and leave X, Y and Z at 0. Click OK.
  4. The plate’s corner sits at the origin, so every dimension on it reads from 0, 0, 0, the way you’d set a work offset on that corner.

Tips and common mistakes

  • Do pick Corner or Center to match how the print dimensions the part. It makes later features easier to locate.
  • Do type the unit with the number when the print uses another unit. In a millimetre design, typing 3 in enters 76.2 mm.
  • Do use Extrude from a sketch when the outline isn’t a plain rectangle.
  • Don’t confuse Length and Width when the part has a long side. Length runs along X.

See also

Cylinder makes a round blank the same way. Bounding Solid puts a box around a part you already have.

ParameterTypeRequiredDescription
lengthexprYes
widthexprYes
heightexprYes
corner[x,y,z]No
center[x,y,z]Nonumbers in mm or length expressions; default: the origin; a box placed by its center stays centred there when its size changes
operationNo
touchingNo
targets[body]No
nameNo
body_nameNo
Automation details

Command ID: solid.box

Parameters:

length, width, height: expr; corner?: [x,y,z] | center?: [x,y,z] (numbers in mm or length expressions; default: the origin; a box placed by its center stays centred there when its size changes); operation?, touching?, targets?: [body], name?, body_name?

Cylinder

Command group: SOLID › CREATE

Make a cylinder from a diameter and a height: a round blank, a pin, or a boss standing on a face. Stand it on a face and locate it from two edges, the way a print dimensions it, and it can join the part. A cylinder on a face grows outward, so Cut only removes material where the cylinder overlaps the part; to cut into the part, leave Face empty and set the base below the face with X, Y and Z.

Before you start

  • To stand the cylinder on a part, you need a flat face for it to stand on. With no face picked, it stands at the X, Y and Z you type, along the Z axis.
  • Locate it from edges of that face the same way a print dimensions a boss from two edges. Pick an edge as Reference 1 and type its distance, then do the same for Reference 2. Both edges must be straight and must not be parallel to each other.
  • Height runs from the face out along the face’s normal, away from the part.

Options

Choice in the dialogMeaningExample or reason to choose it
FaceThe flat face (or a vertex) the cylinder stands on. Leave it empty to place it by X, Y and Z.The top face of a plate, for a boss.
Reference 1 and Distance 1An edge of the face, and how far the cylinder’s center is from it.The left edge, 0.75 in.
Reference 2 and Distance 2A second edge, and how far the center is from it.The front edge, 0.75 in.
SizeWhether you type a Diameter or a Radius.Diameter, to match the print.
DiameterThe cylinder’s diameter (or Radius, if Size is set to Radius). It starts at 20 in the design’s unit.0.500 in for a boss.
HeightHow long the cylinder is, from the face out. It starts at 20 in the design’s unit.0.25 in.
Placement, X, Y, ZWith no face picked, where the center of the cylinder’s base goes.0, 0, 0 to start a round blank at the origin.
OperationIt starts at New Body. New Body makes a separate body. Join adds it to the part. Cut takes it out of the part. Intersect keeps only the material inside both.Join for a boss. New Body for a round blank or a pin.

Try it: put a boss on a plate

  1. Make a 3 in × 2 in × 0.75 in block.
  2. Click Cylinder. Click Face and click the top face of the block.
  3. Click Reference 1, pick the left edge of the top face and set Distance 1 to 0.75 in. Click Reference 2, pick the front edge and set Distance 2 to 0.75 in.
  4. Set Diameter to 0.5 in and Height to 0.25 in. Set Operation to Join and click OK. A 0.500 in boss stands 0.25 in tall, 0.75 in from each edge.

Tips and common mistakes

  • Do use Diameter when the print gives a diameter. Switching Size between Diameter and Radius converts the value for you.
  • Do use New Body for a round blank or a dowel pin that is its own part, so it doesn’t merge with the part around it.
  • Do use Hole for a drilled or tapped hole instead of a Cut cylinder. The hole carries its callout and tap drill.
  • Do type the unit with the number when the print uses another unit. In a millimetre design, typing 0.5 in enters 12.7 mm.

See also

Hole locates a hole from edges the same way. Extrude makes a boss of any shape from a sketch. Thread adds a thread to the cylinder.

ParameterTypeRequiredDescription
radiusexprYes
diameterexprYes
heightexprYes
base[x,y,z]Nocentre of the bottom: numbers in mm or length expressions; default: the origin
axisX|Y|Z|[x,y,z]Nodefault Z
references[{edge: [x,y,z] on a straight edge of the face it stands on, distance: expr}]Noat most two, as solid.hole's: the base centre goes that far from each edge's line, in the face's plane, and follows the edges; with a numeric base only
operationNo
touchingNo
targets[body]No
nameNo
body_nameNo
Automation details

Command ID: solid.cylinder

Parameters:

radius | diameter, height: expr; base?: [x,y,z] (centre of the bottom: numbers in mm or length expressions; default: the origin); axis?: X|Y|Z|[x,y,z] (default Z); references?: [{edge: [x,y,z] on a straight edge of the face it stands on, distance: expr}] (at most two, as solid.hole's: the base centre goes that far from each edge's line, in the face's plane, and follows the edges; with a numeric base only); operation?, touching?, targets?: [body], name?, body_name?

Sphere

Command group: SOLID › CREATE

Make a ball from a diameter: a tooling ball, a ball end on a shaft, or, with Cut, a ball seat or detent pocket. Place its center at a point you pick or at the X, Y and Z you type.

Before you start

  • You don’t need a sketch or a body. A sphere can start a new design.
  • The point is always the center of the sphere.
  • To cut a ball seat into a part, put the center on or near the face and use Cut. How deep the seat goes depends on how far the center sits below the face.

Options

Choice in the dialogMeaningExample or reason to choose it
PointA vertex, edge, face or point for the center. Leave it empty to use X, Y and Z.The end of a shaft, for a ball end.
SizeWhether you type a Diameter or a Radius.Diameter, to match a ball’s size.
DiameterThe sphere’s diameter (or Radius, if Size is set to Radius). It starts at 20 mm.0.500 in for a tooling ball.
Placement, X, Y, ZPlacement is always Center. X, Y and Z set the center when no point is picked.0, 0, 0.
OperationIt starts at New Body. New Body makes a separate body. Join adds it to a body it touches. Cut takes it out. Intersect keeps only the material inside both.New Body for a ball. Cut for a ball seat or a detent pocket.

Try it: cut a ball seat into a plate

  1. Make a 3 in × 2 in × 0.75 in block with its corner at the origin.
  2. Click Sphere. Set Diameter to 0.5 in.
  3. Set X to 1.5 in, Y to 1 in and Z to 0.75 in, so the center sits on the middle of the top face.
  4. Set Operation to Cut and click OK. A half-ball seat, 0.500 in across and 0.25 in deep, is cut into the top face.

Tips and common mistakes

  • Do raise the center above the face for a shallower seat. With a 0.500 in ball and the center 0.1 in above the top face, the seat is 0.15 in deep and about 0.46 in across.
  • Don’t put the center below the face for a ball seat. The pocket then goes deeper than half the ball and is narrower at the top than inside: an undercut a ball end mill can’t cut.
  • Do use Diameter when the print gives a ball diameter. Switching Size converts the value for you.
  • Do type the unit with the number when the print uses another unit. In a millimetre design, typing 0.5 in enters 12.7 mm.
  • Don’t forget a ball seat cut this way is a full sphere shape. For a drilled seat with a drill point or a flat bottom, use Hole.

See also

Cylinder and Box place the same way. Revolve makes a ball end that blends into a turned profile.

ParameterTypeRequiredDescription
radiusexprYes
diameterexprYes
center[x,y,z]Nonumbers in mm or length expressions; default: the origin
operationNo
touchingNo
targets[body]No
nameNo
body_nameNo
Automation details

Command ID: solid.sphere

Parameters:

radius | diameter: expr; center?: [x,y,z] (numbers in mm or length expressions; default: the origin); operation?, touching?, targets?: [body], name?, body_name?

Torus

Command group: SOLID › CREATE

Make a ring with a round section: an O-ring, a round handle, or, with Cut, a round-bottom groove around a shaft or boss. You set two radii and the center point.

Before you start

  • You don’t need a sketch or a body. A torus can start a new design.
  • The ring lies flat around the Z axis, centered on the point.
  • Both sizes are radii, not diameters. Major radius runs from the center to the middle of the ring’s section. Minor radius is the radius of the section itself.
  • To model an O-ring from its inside diameter (ID) and cross-section (CS): Major radius = ID ÷ 2 + CS ÷ 2, and Minor radius = CS ÷ 2.

Options

Choice in the dialogMeaningExample or reason to choose it
PointA vertex, edge, face or point for the center. Leave it empty to use X, Y and Z.The center of a boss’s end face.
Major radiusFrom the center of the ring to the middle of its section. It starts at 20 mm.0.5625 in for an O-ring 1.000 in ID × 0.125 in CS.
Minor radiusThe radius of the round section. It starts at 5 mm. Keep it smaller than Major radius.0.0625 in for a 0.125 in cross-section.
Placement, X, Y, ZPlacement is always Center. X, Y and Z set the center when no point is picked.0, 0, 0.
OperationIt starts at New Body. New Body makes a separate body. Join adds it to a body it touches. Cut takes it out. Intersect keeps only the material inside both.New Body for an O-ring. Cut for a round-bottom groove.

Try it: model an O-ring

  1. In a new design, click Torus.
  2. For an O-ring 1.000 in ID × 0.125 in CS, set Major radius to 0.5625 in and Minor radius to 0.0625 in.
  3. Leave X, Y and Z at 0 and Operation at New Body. Click OK.
  4. The ring lies flat around the Z axis: 1.000 in inside, 1.250 in outside, 0.125 in thick.

Tips and common mistakes

  • Do halve the sizes on the print. An O-ring is called out by ID and cross-section diameter; the dialog wants radii.
  • Don’t use a Cut torus for an O-ring gland. A standard gland has a flat bottom and square walls sized from the O-ring maker’s table; sketch it and use Revolve instead.
  • Do type the unit with the number when the print uses another unit. In a millimetre design, typing 0.5625 in enters 14.2875 mm.

See also

Sphere, Cylinder and Box place the same way. Revolve turns any sketched section into a ring.

ParameterTypeRequiredDescription
majorexprYes
minorexprYesradii
center[x,y,z]Nonumbers in mm or length expressions; default: the origin
operationNo
touchingNo
targets[body]No
nameNo
body_nameNo
Automation details

Command ID: solid.torus

Parameters:

major, minor: expr (radii); center?: [x,y,z] (numbers in mm or length expressions; default: the origin); operation?, touching?, targets?: [body], name?, body_name?

Cone

Command group: SOLID › CREATE

Make a cone or a tapered cylinder from two diameters and a height: a lathe center point, a tapered plug or pin, a spot-drill point, or a tapered boss. Its base circle sits at the X, Y and Z you type, and it runs up the Z axis.

Before you start

  • You don’t need a sketch or a body. A cone can start a new design.
  • Diameter is the base, at the X, Y and Z point. Top diameter is the other end, Height away along Z. A Top diameter of 0 makes a full cone that comes to a point.
  • A negative Height runs the cone down from the base instead of up. Use it to point a cone into a part from its top face.
  • To get a cone for an included angle: Height = (Diameter − Top diameter) ÷ 2 ÷ tan(half the angle). For a 60° point on a 1 in diameter, Height = 0.5 ÷ tan 30° = 0.866 in.

Options

Choice in the dialogMeaningExample or reason to choose it
DiameterThe diameter of the base circle. It starts at 20 mm and must be more than 0.1 in for a 1 in shaft.
Top diameterThe diameter of the other end. It starts at 0, a point. More than 0 makes a tapered cylinder (a frustum).0 for a center point. 0.75 in for a plug that tapers from 1 in.
HeightHow far the top is from the base, along Z. It starts at 20 mm. Negative runs it down.0.866 in for a 60° point on a 1 in diameter.
Placement, X, Y, ZPlacement is always Base Center: X, Y and Z set the center of the base circle.0, 0, 0.
OperationIt starts at New Body. New Body makes a separate body. Join adds it to a body it touches. Cut takes it out. Intersect keeps only the material inside both.Join to put a point on the end of a shaft. Cut to cut a cone shape into a face.

Try it: put a 60° point on a shaft

  1. Make a 1 in diameter, 3 in tall cylinder at the origin with Cylinder.
  2. Click Cone. Set Diameter to 1 in, Top diameter to 0 and Height to 0.866 in.
  3. Set Z to 3 in, so the base sits on the top of the shaft. Set Operation to Join and click OK.
  4. The shaft now ends in a 60° point, like a dead center.

Tips and common mistakes

  • Do work out the Height from the included angle on the print. The dialog has no angle box; use the formula above.
  • Do use Hole for a countersunk or counterbored hole. It sets the countersink angle and diameter for you.
  • Do type the unit with the number when the print uses another unit. In a millimetre design, typing 0.866 in enters 21.9964 mm.
  • Don’t set Diameter to 0. The base must have a size; put the point at the top with Top diameter 0 and use a negative Height if you need it to point down.

See also

Cylinder, Sphere and Box place the same way. Revolve turns any sketched profile, such as a Morse taper with a tang.

ParameterTypeRequiredDescription
radiusexprYes
diameterexprYes
heightexprYesthe base circle, and the top's distance along the axis; a negative height goes the other way
top_radiusexprNothe top circle; default 0: a full cone ending in an apex
top_diameterexprNothe top circle; default 0: a full cone ending in an apex
base[x,y,z]Nocentre of the base circle: numbers in mm or length expressions; default: the origin
axisX|Y|Z|[x,y,z]Nodefault Z
operationNo
touchingNo
targets[body]No
nameNo
body_nameNo
Automation details

Command ID: solid.cone

Parameters:

radius | diameter, height: expr (the base circle, and the top's distance along the axis; a negative height goes the other way); top_radius? | top_diameter?: expr (the top circle; default 0: a full cone ending in an apex); base?: [x,y,z] (centre of the base circle: numbers in mm or length expressions; default: the origin); axis?: X|Y|Z|[x,y,z] (default Z); operation?, touching?, targets?: [body], name?, body_name?

Rectangular Pattern

Command group: SOLID › CREATE

Repeat a hole, a pocket or a whole body in a row or a grid: a row of tapped holes along a rail, or a grid of vent slots. You pick what to copy, the direction, how many and the spacing.

Before you start

  • Make one of the thing you want to repeat first, such as one hole in the corner of the grid.
  • Choose what to copy with Object Type. Bodies copies whole solid bodies. Features repeats a step from the History, such as a Hole or a cut Extrude, on the same body.
  • A sketch is neither. To repeat sketch curves, use the sketch’s own Rectangular Pattern, or extrude the sketch first.
  • You need a direction for each row: an origin axis, a sketch line or a straight edge of the part.

Options

Choice in the dialogMeaningExample or reason to choose it
TypeRectangular, Circular or Path. The same dialog makes all three patterns.Rectangular for rows and grids.
Object TypeBodies copies whole bodies. Features repeats History steps.Features for holes in one plate.
Objects or FeaturesWhat to repeat. With Features, pick the step in the History.The Hole feature for the first hole.
DirectionThe way the first row runs.The X axis, or the long edge of the plate.
QuantityHow many along the first direction, counting the original.5 holes in the row.
SpacingCentre-to-centre distance between neighbours in the first direction.1 in pitch.
Direction 2The way the second row runs. Leave it empty for a single row.The Y axis for a grid.
Quantity 2How many along the second direction, counting the original. 1 means a single row.3 rows.
Spacing 2Centre-to-centre distance in the second direction.0.75 in.
Alignment and Alignment 2 (Features only)Flip direction runs the copies the other way. Center puts copies on both sides of the original: an odd Quantity splits evenly, an even Quantity puts one extra copy on one side, and Flip moves the extra copy to the other side. Quantity 4 with Center gives copies at 0, +1, −1 and +2 spacings.Flip when the row runs off the end of the part.

Try it: a 5 × 3 grid of holes

  1. Make a 6 in × 4 in × 0.5 in plate. Sketch a rectangle from the origin: type 6 in, press Tab to jump to the other side, type 4 in. Then Extrude it 0.5 in.
  2. Add one 0.25 in through hole with Hole, 1 in in from each of the two edges at the origin corner.
  3. Click Rectangular Pattern. Set Object Type to Features and click the Hole step in the History.
  4. Click Direction, then the X axis (or the long edge of the plate). Set Quantity to 5 and Spacing to 1 in.
  5. Click Direction 2, then the Y axis. Set Quantity 2 to 3 and Spacing 2 to 1 in.
  6. Click OK. The plate has 15 holes on a 1 in grid.

Tips and common mistakes

  • Do count the original in Quantity. Five holes in a row is Quantity 5, not 4.
  • Do read Spacing as hole-to-hole pitch, the way the print dimensions it, not the total length of the row.
  • Do pattern the Hole feature rather than the body when the holes are all in one part. Change the hole size once and every copy follows.
  • Don’t forget to check Alignment if the copies land off the part. Flip direction or Center left on from an earlier pattern moves the whole grid.
  • Don’t leave Quantity 2 at 1 and expect a grid. With Quantity 2 at 1 you get a single row even if Direction 2 is picked.

See also

Circular Pattern repeats around an axis, such as a bolt circle. Rectangular Pattern repeats curves inside a sketch. Hole makes the first hole to pattern.

ParameterTypeRequiredDescription
features[names]Yes
bodies[names]Yes
components[names]Yes
dir1[x,y,z]Yes
count1Yes
spacing1Yes
dir2No
count2No
spacing2No
alignmentleft|center|rightNo
alignment2left|center|rightNodefault right; counts include the original
reversedboolNo
reversed2boolNoFlip
Automation details

Command ID: solid.pattern.rectangular

Parameters:

features: [names] | bodies: [names] | components: [names]; dir1: [x,y,z]; count1; spacing1; dir2?, count2?, spacing2?; alignment?, alignment2?: left|center|right (default right; counts include the original); reversed?, reversed2?: bool (Flip)

Circular Pattern

Command group: SOLID › CREATE

Repeat a hole, a boss or a whole body around an axis: the bolt circle on a flange, or spokes on a hub. You pick what to copy, the axis, how many and over what angle.

Before you start

  • Make one of the thing you want to repeat first, such as one hole on the bolt circle. On a round face there are no straight edges to locate a hole from, so sketch a point where the hole goes and use Hole with Placement set to Multiple.
  • Choose what to copy with Object Type. Bodies copies whole solid bodies. Features repeats a step from the History, such as a Hole or a cut Extrude, on the same body.
  • A sketch is neither. To repeat sketch curves, use the sketch’s own Circular Pattern, or extrude the sketch first.
  • You need an axis to turn about: an origin axis, a sketch line, a straight or circular edge, or a cylinder face such as the bore of the part.

Options

Choice in the dialogMeaningExample or reason to choose it
TypeRectangular, Circular or Path. The same dialog makes all three patterns.Circular for a bolt circle.
Object TypeBodies copies whole bodies. Features repeats History steps.Features for holes in one part. Bodies for separate parts, such as loose spokes.
Objects or FeaturesWhat to repeat. With Features, pick the step in the History.The Hole feature for one bolt hole.
AxisThe line the copies turn about. Clicking a round edge or a cylinder face uses its centre line.The Z axis, or the face of the centre bore.
QuantityHow many in total, counting the original.6 for a 6-hole bolt circle.
Total angleThe angle the pattern covers. At 360 deg the copies are spaced evenly all the way round. Under 360 deg, the first and last copies sit at the two ends of the angle.360 deg for a full bolt circle. 90 deg with Quantity 4 puts holes at 0, 30, 60 and 90 deg.
Alignment (Features only)Flip direction turns the pattern the other way. Center puts copies on both sides of the original: an odd Quantity splits evenly, an even Quantity puts one extra copy on one side, and Flip moves the extra copy to the other side. Quantity 4 over 90 deg with Center gives −30, 0, 30 and 60 deg.Flip a partial pattern that went the wrong way around.

Try it: a 6-hole bolt circle

  1. Make a flange: sketch a 4 in circle on the XY plane at the origin and Extrude it 0.5 in.
  2. Sketch a point on the top face, 1.5 in from the centre on the X axis (a 3 in bolt circle). Click Hole, set Placement to Multiple and pick the point. Set Diameter to 0.281 in and Extents to All, then click OK. The Hole must be finished so it shows in the History.
  3. Click Circular Pattern. Set Object Type to Features and click the Hole step in the History.
  4. Click Axis, then click the Z axis.
  5. Set Quantity to 6 and Total angle to 360 deg. Click OK. There are 6 holes, 60° apart.
  6. To make it 8 holes, edit the pattern and change Quantity to 8.

Tips and common mistakes

  • Do pattern the Hole feature rather than the body when the holes are all in one part. Change the hole size once and every copy follows.
  • Do count the original in Quantity. A 6-hole bolt circle is Quantity 6, not 5.
  • Don’t use 360 deg for a partial pattern you want to end on a copy. Under 360 deg the angle is from the first copy to the last; at 360 deg the last would land on the first, so the copies are spaced evenly instead.
  • Don’t pick the edge of the hole you are patterning as the Axis. That turns the hole about its own centre, so every copy lands on the original. Pick the Z axis or the centre bore.
  • Don’t pick the sketch you drew the hole from. Pick the Hole step, or extrude the sketch first.

See also

Rectangular Pattern repeats in rows and columns. Circular Pattern repeats curves inside a sketch. Hole makes the first hole to pattern.

ParameterTypeRequiredDescription
features[names]Yes
bodies[names]Yes
components[names]Yes
axisX|Y|Z | construction axis name| {origin, dir}| {edge|face: [x,y,z]}Yesfollowed when edited; origin coordinates may be length expressions; the pattern follows them; a straight or circular edge, or a cylinder, cone or torus face: about its axis, followed as the model changes
countYes
angleNodefault 360 deg; partial arcs include both ends
alignmentleft|center|rightNodefault right; original stays fixed
reversedboolNoFlip; selects the extra side for even centered counts
Automation details

Command ID: solid.pattern.circular

Parameters:

features: [names] | bodies: [names] | components: [names]; axis: X|Y|Z | construction axis name (followed when edited) | {origin, dir} (origin coordinates may be length expressions; the pattern follows them) | {edge|face: [x,y,z]} (a straight or circular edge, or a cylinder, cone or torus face: about its axis, followed as the model changes); count; angle? (default 360 deg; partial arcs include both ends); alignment?: left|center|right (default right; original stays fixed); reversed?: bool (Flip; selects the extra side for even centered counts)

Pattern on Path

Command group: SOLID › CREATE

Read the solid features guide.

ParameterTypeRequiredDescription
features[names]Yes
bodies[names]Yes
path_sketchsketchYes
path[curve ids in order]Yes
countYes
spacingYesbetween instances
distanceYesfirst to last
orientationidentical|pathNo
flipboolNo
Automation details

Command ID: solid.pattern.path

Parameters:

features: [names] | bodies: [names]; path_sketch: sketch; path: [curve ids in order]; count; spacing (between instances) | distance (first to last); orientation?: identical|path; flip?: bool

Mirror

Command group: SOLID › CREATE

Copy bodies or features to the other side of a plane: model half of a symmetric part and mirror it, or make the left-hand version of a right-hand bracket.

Before you start

  • Make the half (or the feature) you want to copy first.
  • Choose what to copy with Object Type. Bodies copies whole solid bodies. Features repeats a step from the History, such as a Hole or a cut Extrude, on the other side of the same body.
  • A sketch is neither. To mirror sketch curves, use the sketch’s own Mirror, or extrude the sketch first.
  • You need a plane to mirror about: an origin plane (XY, XZ or YZ), a construction plane, or a flat face of the part.

Options

Choice in the dialogMeaningExample or reason to choose it
Object TypeBodies copies whole bodies. Features repeats History steps.Features to put a second pocket on the other side of the centre line. Bodies to mirror half a part.
Objects or FeaturesWhat to mirror. The box is named Objects or Features to match Object Type. With Features, pick the steps in the History; you can pick more than one.The half body, or two Hole steps at once.
Mirror PlaneThe plane the copy is reflected across.The YZ plane when the part is centred on the origin.
Operation (Bodies only)New Body keeps the copy as its own body. Join merges it with the original into one body.Join when you modelled half a part and want one solid. New Body for a left-hand and right-hand pair.

Try it: model half, mirror the rest

  1. Sketch a 2 in × 1.5 in rectangle on the XY plane with one 1.5 in short edge on the Y axis, so the 2 in side runs along X, and Extrude it 0.5 in. This is the right half of a 4 in wide block.
  2. Click Mirror. Leave Object Type on Bodies and click the block.
  3. Click Mirror Plane, then click the YZ plane.
  4. Set Operation to Join. Click OK. The block is now 4 in wide and one body.
  5. Add a hole on one side, then Mirror again with Object Type set to Features and the Hole step picked. The hole appears at the same spot on the other side.

Tips and common mistakes

  • Do draw symmetric parts centred on an origin plane. Then that plane is the mirror plane, and the halves line up.
  • Do use Join when the halves touch and should be one part. With New Body you get two bodies sitting face to face.
  • Do mirror the feature, not the body, when you just need the same hole or pocket on the other side. Change the original and the mirrored one follows.
  • Don’t expect a mirrored copy to be the same part. A mirrored right-hand bracket is a left-hand bracket, and a mirrored right-hand thread is left-hand.

See also

Mirror mirrors curves inside a sketch. Rectangular Pattern and Circular Pattern repeat features more than once.

ParameterTypeRequiredDescription
features[names]Yes
bodies[names]Yescombine?: join with the original
planeXY|XZ|YZ|construction plane name or id|{origin, normal}|{origin, x_dir, y_dir}Yesorigin coordinates may be length expressions; the mirror follows them, or a construction plane
Automation details

Command ID: solid.mirror

Parameters:

features: [names] | bodies: [names] (combine?: join with the original); plane: XY|XZ|YZ|construction plane name or id|{origin, normal}|{origin, x_dir, y_dir} (origin coordinates may be length expressions; the mirror follows them, or a construction plane)

Hole

Command group: SOLID › CREATE

Default keyboard shortcut: H

Put a hole in a solid body: a simple hole, a counterbore, a countersink or a tapped hole. You set the location, size, depth and drill point, the same things a print calls out.

Before you start

  • You need a solid body with a face to drill into. The hole goes straight into the face. A single hole can go on a flat or curved face; locating it from edges needs a flat face.
  • Locate the hole from edges of that face, the same way a print dimensions a hole from two edges. Pick an edge as Reference 1 and type its distance, then do the same for Reference 2. Both edges must be straight and must not be parallel to each other: pick one side edge and one front or back edge, not two opposite sides.
  • To drill several holes at once, sketch a point for each hole on the face first. Then open Hole, set Placement to Multiple and select the points you want. Only the points you select get a hole.

Options

Choice in the dialogMeaningExample or reason to choose it
Personal PresetsSaves the dialog settings under a name you choose (Save new), so you can reuse them. Presets are saved in your profile, so they carry over to every design. Rename and Delete manage saved presets.Save a “1/4-20 blind” preset once and pick it for every tapped hole of that size.
PlacementSingle drills one hole on the face you pick. Multiple drills a hole at each sketch point you select.Single for one hole located from edges. Multiple for a bolt pattern laid out in a sketch.
FaceThe face the hole is drilled into.Click the top face of the part.
Reference 1 and Distance 1An edge of the face, and how far the hole center is from it.The left edge, 0.75 in.
Reference 2 and Distance 2A second edge, and how far the hole center is from it.The front edge, 0.75 in.
ExtentsHow the depth is set. Distance drills to the Depth you type.Use Distance for a blind hole.
Hole TypeSimple is a straight hole. Counterbore and Countersink add a bore or a cone at the top.Counterbore for a socket head cap screw. Countersink for a flat head screw: 82° for inch screws, 90° for metric.
Hole Tap TypeSimple is a plain drilled hole with a Diameter you type. Tapped drills the tap drill size and shows the thread settings.Choose Tapped for any tapped hole.
Profile (Tapped)The thread standard.UNC for coarse inch threads, UNF for fine inch threads.
Size (Tapped)The thread size, from a list. The Designation box shows the callout.1/4-20 UNC.
Tap drill diameter (Tapped)The drill for the tapped hole. It fills in the standard tap drill for the size, the one on a tap drill chart (the app’s messages call it “the tap maker’s drill”), and you can type a different one. The app accepts drills from the smaller of the thread’s minor diameter and the standard tap drill, up to 5% over the standard tap drill, and says the allowed range if you go outside it. For a custom metric size with no entry in the app’s tap drill table, the app fills in major diameter minus pitch, the usual rule of thumb; check a tap drill chart.1/4-20 UNC fills in 0.201 in (a #7 drill); hover the box to see 5.105 mm. Typing 0.203 in (13/64) is accepted; 0.220 in is refused because too little thread would engage.
Full thread depth (Tapped)On: the thread runs the full depth of the hole. Off: a Thread depth box appears.Turn it off for a blind tapped hole.
Thread depth (Tapped)How deep the full thread goes, separate from the drill Depth.Drill 0.625 in deep with 0.50 in of thread, so a standard tap doesn’t bottom out.
Hand (Tapped)Right or Left.Right unless the print calls for LH.
Modeled (Tapped)On: the thread groove is cut into the hole wall. Off: the thread is carried as a callout only.Leave it off unless you need to see the thread or 3D print it. A modeled thread makes the part slower to rebuild.
End (Modeled on)Square stops the thread sharp at the bottom. Fade runs it out over a length, the way a tap’s chamfered end leaves it.Fade for a realistic blind tapped hole.
Drill PointThe bottom of the hole. Angle leaves a drill point.Use Angle for a hole made with a twist drill.
DepthHow deep the hole goes.Measured to the end of the full diameter, not the tip of the drill point.
Tip AngleThe angle of the drill point. Default is 118°.118° for a standard jobber drill. 135° for most carbide and split-point drills.
Diameter (Simple)The drill size for a plain hole.Type the drill size, such as 0.201 in.

Try it: a 1/4-20 tapped hole 0.75 in from two edges

  1. Click New Sketch and choose the XY plane. Draw a 2-point rectangle from the origin, 3 in × 2 in. Click Finish Sketch.
  2. Click Extrude and set the distance to 0.75 in. Click OK.
  3. Click Hole, then click the top face.
  4. Click Reference 1, pick the left edge of the top face and set Distance 1 to 0.75 in. Click Reference 2, pick the front edge and set Distance 2 to 0.75 in.
  5. Set Hole Tap Type to Tapped, Profile to UNC and Size to 1/4-20 UNC. Check that Tap drill diameter shows 0.201 in.
  6. Turn off Full thread depth. Set Depth to 0.625 in and Thread depth to 0.50 in. Click OK.

Tips and common mistakes

  • Do locate holes from edges the way the print dimensions them. When the part changes size, the holes keep their distances from those edges.
  • Do use Tapped and pick the size from the list instead of typing a drill diameter. The hole then carries the thread callout, and the tap drill comes from a standard tap drill chart.
  • Do check the Tap drill diameter before clicking OK. UNC and UNF sizes show it in inches; metric and pipe sizes show millimetres. If your shop runs a different drill, such as 0.203 in for 1/4-20 in steel, type it in. Hovering over the box shows the tap maker’s drill in millimetres and the accepted range, not the drill you typed.
  • Don’t turn on Modeled just to show the hole is tapped. The callout already says it.
  • Do turn off Full thread depth on blind tapped holes and set a Thread depth shorter than the drill Depth, so a standard tap doesn’t bottom out.
  • Do type the unit with the number when the print is in another unit. In a millimetre design, typing 0.75 in enters 19.05 mm.
  • Don’t expect every hole to work on sheet metal. Sheet metal holes must go straight into a flat panel; holes into the edge of the sheet, countersinks and drill points are refused there.

See also

Thread cuts a thread on any round face, including the outside of a shaft. Circular Pattern repeats a hole around a center for a bolt circle. Chamfer breaks the edge of a hole.

ParameterTypeRequiredDescription
position[x,y,z] on a face| sketch + points: [sketch point ids]| sketch aloneYesnumbers, or length expressions the hole follows; one hole each, perpendicular to the sketch, into the material on whichever side of the sketch plane it is; every sketch point: standalone points and circle centres
flipboolNosketch points: drill to the other side
singleboolNowith one sketch point: a Single hole that follows the point, drilled into the face there
direction[x,y,z]Nodefault: into the face, against its outward normal; where faces of several bodies meet there, the face of a body in `targets`; one that leaves the face is mirrored into it; ignored with sketch points unless single
diameterYesa tapped hole, drilled with the tap maker's tabulated drill: ISO metric (a custom metric size: major minus pitch), UNC/UNF, G, Rp, Rc 1/16 to 4, NPT 1/16 to 3; a `diameter` given with it chooses another drill, from the thread's minor diameter to 5% over the tap maker's
threadYesa tapped hole, drilled with the tap maker's tabulated drill: ISO metric (a custom metric size: major minus pitch), UNC/UNF, G, Rp, Rc 1/16 to 4, NPT 1/16 to 3; a `diameter` given with it chooses another drill, from the thread's minor diameter to 5% over the tap maker's
clearance"M6" with fit?: close|normal|looseYesISO 273
head"ISO 4762"| "ISO 7380-1"Nosocket head; button head
head_gaplengthNodefault 0.5 mm; an expression with parameters is kept, so the recess follows them
depthNodefault through all
to[x,y,z]NoTo Object: drill to that face or point
references[{edge: [x,y,z] on a straight edge of the face, distance: expr}]Noat most two; the centre goes that far from each edge's line, in the face's plane, and follows the edges; with a numeric position only
typesimple|drilled|counterbore|countersinkNo
tip_angleNo
cb_diameterlengthNo
cb_depthlengthNo
bridgelengthNocounterbore: two 3D-printing bridge layers this thick below its floor, a slot along X then a square; e.g. 0.2 mm
cs_diameterNo
cs_angleNo
handright|leftNothe tapped thread's hand; default right; needs thread
thread_depthpositive length independent of drill depthNodefault: full useful cylindrical wall; must fit that wall
modeledboolNoneeds thread: the tapped thread cut into the wall, the groove between its teeth, as solid.thread's modelled one; default false: listed only
ease_outexpr|standard|shortNomodelled tapped threads: the inner end, towards the bottom, fades out over this length, ISO 3508's runout, standard 2.5 P, short 1.25 P; without it the groove stops square at thread_depth, or clear of a blind hole's bottom
targets[body]Nothe bodies to drill; holes at the points of a sketch on a body's face default to that body, found along the holes' direction on it alone; explicit targets or participants override, ["*"] every body
old_surface_startboolNosketch points: a point inside a body starts the hole at the body's surface whatever plane the sketch lies on, as designs saved before #11538 drilled; set on those holes, keeps them as they were when edited
Automation details

Command ID: solid.hole

Parameters:

position: [x,y,z] on a face (numbers, or length expressions the hole follows) | sketch + points: [sketch point ids] (one hole each, perpendicular to the sketch, into the material on whichever side of the sketch plane it is) | sketch alone (every sketch point: standalone points and circle centres); flip?: bool (sketch points: drill to the other side); single?: bool (with one sketch point: a Single hole that follows the point, drilled into the face there); direction?: [x,y,z] (default: into the face, against its outward normal; where faces of several bodies meet there, the face of a body in `targets`; one that leaves the face is mirrored into it; ignored with sketch points unless single); diameter | thread (a tapped hole, drilled with the tap maker's tabulated drill: ISO metric (a custom metric size: major minus pitch), UNC/UNF, G, Rp, Rc 1/16 to 4, NPT 1/16 to 3; a `diameter` given with it chooses another drill, from the thread's minor diameter to 5% over the tap maker's) | clearance: "M6" with fit?: close|normal|loose (ISO 273); head?: "ISO 4762" (socket head) | "ISO 7380-1" (button head) (with clearance: a counterbore the screw's head sits in, so type defaults to counterbore; its diameter is the head's dk plus head_gap each side, its depth the head's k plus head_gap; a cb_diameter or cb_depth given keeps its value); head_gap?: length (default 0.5 mm; an expression with parameters is kept, so the recess follows them); depth? (default through all) | to?: [x,y,z] (To Object: drill to that face or point); references?: [{edge: [x,y,z] on a straight edge of the face, distance: expr}] (at most two; the centre goes that far from each edge's line, in the face's plane, and follows the edges; with a numeric position only); type?: simple|drilled|counterbore|countersink; tip_angle?; cb_diameter?, cb_depth?, bridge?: length (counterbore: two 3D-printing bridge layers this thick below its floor, a slot along X then a square; e.g. 0.2 mm); cs_diameter?, cs_angle?; thread?: "M6", "1/4-20 UNC", "G 1/4", "Rp 1/4", "Rc 1/4" or "1/4-18 NPT" (cosmetic unless modeled); hand?: right|left (the tapped thread's hand; default right; needs thread); thread_depth?: positive length independent of drill depth (default: full useful cylindrical wall; must fit that wall); modeled?: bool (needs thread: the tapped thread cut into the wall, the groove between its teeth, as solid.thread's modelled one; default false: listed only); ease_out?: expr|standard|short (modelled tapped threads: the inner end, towards the bottom, fades out over this length, ISO 3508's runout, standard 2.5 P, short 1.25 P; without it the groove stops square at thread_depth, or clear of a blind hole's bottom); targets?: [body] (the bodies to drill; holes at the points of a sketch on a body's face default to that body, found along the holes' direction on it alone; explicit targets or participants override, ["*"] every body); old_surface_start?: bool (sketch points: a point inside a body starts the hole at the body's surface whatever plane the sketch lies on, as designs saved before #11538 drilled; set on those holes, keeps them as they were when edited)

Pipe

Command group: SOLID › CREATE

Run a round tube or rod along a path of sketch curves: bent tubing, a coolant or hydraulic line, a handrail, or a wire. You give the outside diameter and, for a tube, the wall.

Before you start

  • You need a sketch with the path drawn as connected curves: lines and arcs, end to end. The path is the tube’s centerline.
  • Diameter is the outside diameter (OD). Leave Wall empty for a solid rod; type a wall for a tube.
  • Lines meeting at a sharp corner give a mitred joint, like welded pipe. For bent tubing, draw each bend as an arc at the bender’s centerline radius.

Options

Choice in the dialogMeaningExample or reason to choose it
PathThe sketch curves the tube follows.A line, an arc and a line for a 90° bend.
DiameterThe outside diameter of the tube or rod. It starts at 5 in the design’s unit.0.375 in for 3/8 tube.
WallThe wall thickness, measured in from the OD. Empty means solid.0.035 in.
OperationNew Body makes a separate body. Join adds it to a body it touches. Cut takes it out. Intersect keeps only the material inside both.New Body for a tube. Cut for a round channel through a part.

Try it: a bent 3/8 tube

  1. Start a sketch on the XY plane. Draw a 3 in line along X, a tangent arc of 1 in radius turning 90°, and a 2 in line along Y. Finish the sketch.
  2. Click Pipe. Click Path and pick the line, the arc and the second line. Path shows 3 selected.
  3. Set Diameter to 0.375 in and Wall to 0.035 in. Click OK.
  4. The tube is 3/8 in OD with a 0.035 in wall, bent 90° on a 1 in centerline radius.

Tips and common mistakes

  • Do draw the path through the tube’s center, the way a tube bender’s centerline radius and lengths are given.
  • Do keep an arc’s radius bigger than half the OD, or the inside of the bend folds into itself. Real tube needs much more, often 2 to 3 times the OD, so use the radius your bender’s die gives.
  • Do type the unit with the number when the print uses another unit. In a millimetre design, typing 0.375 in enters 9.525 mm.
  • Don’t leave gaps in the path. The curves must meet end to end.

See also

Sweep runs any section shape along a path. Cylinder makes a straight rod without a sketch.

ParameterTypeRequiredDescription
path_sketchsketchYes
path[curve ids in order]Yes
diameterexprYes
wallexprNohollow
operationNo
targetsNo
nameNo
body_nameNo
Automation details

Command ID: solid.pipe

Parameters:

path_sketch: sketch; path: [curve ids in order]; diameter: expr; wall?: expr (hollow); operation?, targets?, name?, body_name?

Bounding Solid

Command group: SOLID › CREATE

Put a box around a part: the size of the raw stock it’s cut from. Margin adds cleanup stock on every side, the way you’d saw a blank oversize before squaring it up.

Before you start

  • You need at least one body.
  • The box is square to the design’s X, Y and Z axes, not to the part. Model the part square to the axes, or the box comes out bigger than the stock you’d really cut.
  • The box is a new body, named Bounding, or Bounding (2) if the design already has a body named Bounding. The part itself is not changed.

Options

Choice in the dialogMeaningExample or reason to choose it
Bodies (all if none)The bodies to put the box around. A body selected when you start is picked for you. Clear it to use every body in the design.Pick one part when the design has several.
MarginStock added on every side of the box. The overall size grows by twice the Margin in each direction. It starts at 2 in the design’s unit; set 0 for the part’s exact size.0.0625 in on each side to leave room to face and square up a sawn blank.

Try it: size the stock for a plate

  1. Make a 3 in × 2 in × 0.75 in block.
  2. Click Bounding Solid. Bodies shows the part if it was selected; otherwise leave it empty. Set Margin to 0.0625 in. The yellow preview shows the box. Click OK.
  3. A new body, Bounding, covers the part. It is 3.125 in × 2.125 in × 0.875 in: the stock to order or saw.
  4. Hide the new body in the Browser to see the part again. It is Bounding, or Bounding (2) if the design already had a body named Bounding.

Tips and common mistakes

  • Do remember the Margin is per side. For 1/8 in of total cleanup on each dimension, set Margin to 0.0625 in.
  • Do round the stock up to a size you can buy. The box gives the smallest block that holds the part; standard bar and plate sizes are usually a little bigger.
  • Do type the unit with the number when the print uses another unit. In a millimetre design, typing 0.0625 in enters 1.5875 mm.
  • Don’t expect a box square to the part when the part sits at an angle to the axes. The box stays square to X, Y and Z.
  • Don’t leave Bounding showing while you work. It covers the part; hide it in the Browser.

See also

Extrude makes the part from a sketch. Offset Face adds stock to a single face instead of the whole part.

ParameterTypeRequiredDescription
bodies[names]Nodefault: all
marginexprNo
nameNo
body_nameNo
Automation details

Command ID: solid.bounding_solid

Parameters:

bodies?: [names] (default: all); margin?: expr; name?, body_name?

Thread

Command group: SOLID › CREATE

Put a thread on a round face: an external thread on a shaft or stud, or an internal thread in a bore. You pick the thread form and size, how far the thread runs, and how its ends are finished.

Before you start

  • You need a body with a cylindrical face: the outside of a shaft for an external thread, or the wall of a bore for an internal one.
  • Size the cylinder first. For an external thread, make the shaft the thread’s major diameter (12 mm for M12). For an internal thread, make the bore the tap drill size.
  • Click the face near the end you want threaded. On a shaft with two free ends, that tells a limited-length thread where to start.
  • For an ordinary tapped hole, including UNC and UNF, use Hole with Hole Tap Type set to Tapped instead. It drills the tap drill and keeps the thread callout on the hole.

Options

Choice in the dialogMeaningExample or reason to choose it
Cylindrical faceThe round face the thread goes on.Click the outside of the shaft near the end to thread.
FormThe thread standard: ISO metric, Trapezoidal (ISO 2904), Acme (ASME B1.5), Buttress (DIN 513 or ASME B1.9), and the pipe threads Pipe G (ISO 228-1), Pipe Rp (ISO 7-1), Pipe taper R, Rc (ISO 7-1) and Pipe taper NPT (ASME B1.20.1). UNC and UNF are not in this list.ISO metric for fasteners. Acme or Trapezoidal for lead screws. Buttress for a screw that takes load one way, like a vise screw. Pipe taper NPT for US tapered pipe threads.
ModeledOn: the thread groove is cut into the part. Off: the thread is carried as a callout only.Leave it off unless you need to see the thread or 3D print it. A modeled thread makes the part slower to rebuild.
Automatic sizeOn by default: picks the standard size that fits the face you selected.Leave it on when the shaft is a standard diameter.
Size and PitchThe thread size from the standard’s table, and for metric threads the pitch.M12 with the standard 1.75 mm pitch, or 1.25 mm for M12 fine.
DesignationThe thread callout. You can type it directly.M12, M12x1.25.
HandRight, Left, or Both. Pipe threads offer Right or Left only.Right unless the print calls for LH.
StartsNumber of thread starts, 1 to 8.1 for fasteners. 2 or more for a fast-moving lead screw: each turn advances the pitch times the number of starts.
Steep Flank (Buttress only)Which end the steep, load-carrying flank faces.Point it toward the load.
ExtentFull runs the whole face. Length runs a set distance. Up to stops at a face or plane you pick.Length for a threaded end on a longer shaft. Up to for a thread that stops at a shoulder.
Start end (optional)Which end of the face the thread is measured from. Shown once Extent is Length or Up to.Pick it when the thread starts at the wrong end.
Start (Ends)None leaves the thread’s free end plain. Chamfer adds a lead-in chamfer and shows Chamfer Angle and Chamfer Depth; left blank, they use the ISO 4753 standard chamfer.Chamfer the start of any thread that has to take a nut or go into a tapped hole by hand.
End (Ends)Square stops the thread sharp. Fade runs it out over a length (ISO 3508 runout, 2.5 pitches standard).Fade for a thread cut with a die or single-point tool that stops short of a shoulder.

Try it: a threaded shaft end

  1. Sketch a 12 mm circle on the XY plane and extrude it 50 mm.
  2. Click Thread, then click the round face of the shaft near its top end.
  3. Check that Form is ISO metric and Automatic size picked M12 with a 1.75 mm pitch.
  4. Set Extent to Length and the length to 20 mm.
  5. Turn on Modeled to see the thread cut into the shaft.
  6. Under Ends, set Start to Chamfer. Leave Chamfer Angle and Chamfer Depth blank for the standard ISO 4753 chamfer.
  7. Click OK. The top 20 mm of the shaft carries an M12 thread with a lead-in chamfer.

Tips and common mistakes

  • Do size the cylinder before threading it: major diameter for an external thread, tap drill size for an internal one.
  • Do click the face near the end you want threaded, or pick Start end. Otherwise a Length thread can start at the wrong end.
  • Do chamfer the start of an external thread. The shop will put one there anyway, and a nut starts on it in the model the same way it does on the part.
  • Do leave room for runout before a shoulder. A thread can’t run full depth right up to a shoulder without a relief groove.
  • Don’t turn on Modeled just to show the part is threaded. The callout already says it.
  • Don’t look for UNC or UNF here. Inch fastener threads are only on Hole tapped holes for now; the inch forms in this list are Acme, ASME buttress and the pipe threads.
  • Don’t mix up NPT and the ISO taper (R, Rc, often called BSPT). Both are tapered pipe threads, but the thread angle and pitch differ, and they don’t seal together.

See also

Hole makes tapped holes with the right tap drill, including UNC and UNF. Chamfer breaks an edge by hand. Revolve makes a turned part to thread.

ParameterTypeRequiredDescription
face[x,y,z] on a cylindrical faceYes
formmetric|trapezoidal|acme|buttress|buttress_asme|bspp|rp|bspt|nptNodefault: the one `designation` names, else metric; square is refused: no current standard defines it
handright|left|bothNodefault right; both: a right- and a left-handed groove crossing
old_auto_sizeboolNoan automatic size keeps the rule of designs saved before a hole was sized by its nominal diameter: a 12 mm hole is M14; set on those threads, keeps them as they were when edited
steep_flankstart|endNobuttress only, default end: the steep flank of the screw's teeth faces the thread's end, the way its axis points
starts1..8Nolead = starts x pitch: that many grooves, each a turn's starts-th further round; a designation that gives the lead gives the starts
ease_outexpr|standard|shortNomodelled threads: the inner end, away from where the thread starts, fades out over this length (shorter than a turn too), its section moving out of the material as it turns; standard 2.5 P, short 1.25 P per ISO 3508; it lies past the full-form length, up to a shoulder's clearance; at a free end, inside the face; without it the groove stops square there, ending in its own profile; the start never fades: it runs off its free end, plain or chamfered (`tip`)
tipnone|start|bothNomodelled external threads: start chamfers the end the thread starts from, which must be free, both each free end the thread reaches, ISO 4753's chamfered end, the last teeth tapering into the cone; chamfer_angle?: angle expr to the axis, default 45 deg; chamfer_depth?: expr, radial from the major diameter, default for a metric thread down to the external minor diameter d3 = d - 1.226869 P (ISO 724), the largest ISO 4753 allows; other forms give it
extentfull|length|up_toNodefault: length when `length` is given, up_to when `up_to` is, else full: the whole face
lengthexprNofrom the end the thread starts at
up_to[x,y,z]| plane name or {origin, normal}Noa point, such as on a shoulder face: the thread runs to its place along the axis; to where the plane crosses the axis
from[x,y,z] near the end of the face the thread starts atNodefault: the face's only free end, the one with no material past it; else the end the `face` point lies within a third of the face of; a pick in the middle of a face whose ends are alike is refused
modeledboolNodefault false
Automation details

Command ID: solid.thread

Parameters:

face: [x,y,z] on a cylindrical face; form?: metric|trapezoidal|acme|buttress|buttress_asme|bspp|rp|bspt|npt (default: the one `designation` names, else metric; square is refused: no current standard defines it); designation?: the size, as its standard writes it: metric "M8", "M8x1", "M16xPh3P1.5" (ISO 68-1, ISO 261, ISO 965-1); trapezoidal "Tr20x4", "Tr40x14(P7)" (ISO 2904, ISO 2903); buttress "S20x4", "S40x14(P7)" (DIN 513, 3°/30°); acme "1/2-10 ACME" (ASME B1.5); buttress_asme "2.5-8 BUTT" (ASME B1.9, 7°/45°, basic profile; no size table); bspp "G 1/2" (ISO 228-1, 55° parallel); rp "Rp 1/2" (ISO 7-1's parallel internal thread, 55°, an R screws into it; internal only); bspt "R 1/2" external, "Rc 1/2" internal (ISO 7-1, 55°, taper 1 in 16); npt "1/2-14 NPT" (ASME B1.20.1, 60°, taper 1 in 16) — a taper thread starts at a free end (the pipe end or the hole mouth) with its standard diameter there, and an external one runs its standard length unless `length` or `up_to` says otherwise (default: the standard size that fits the face, a shaft its major diameter, a hole its nominal diameter (a 12 mm hole is M12) or else the size whose tap drill it is); hand?: right|left|both (default right; both: a right- and a left-handed groove crossing); old_auto_size?: bool (an automatic size keeps the rule of designs saved before a hole was sized by its nominal diameter: a 12 mm hole is M14; set on those threads, keeps them as they were when edited); steep_flank?: start|end (buttress only, default end: the steep flank of the screw's teeth faces the thread's end, the way its axis points); starts?: 1..8 (lead = starts x pitch: that many grooves, each a turn's starts-th further round; a designation that gives the lead gives the starts); ease_out?: expr|standard|short (modelled threads: the inner end, away from where the thread starts, fades out over this length (shorter than a turn too), its section moving out of the material as it turns; standard 2.5 P, short 1.25 P per ISO 3508; it lies past the full-form length, up to a shoulder's clearance; at a free end, inside the face; without it the groove stops square there, ending in its own profile; the start never fades: it runs off its free end, plain or chamfered (`tip`)); tip?: none|start|both (modelled external threads: start chamfers the end the thread starts from, which must be free, both each free end the thread reaches, ISO 4753's chamfered end, the last teeth tapering into the cone; chamfer_angle?: angle expr to the axis, default 45 deg; chamfer_depth?: expr, radial from the major diameter, default for a metric thread down to the external minor diameter d3 = d - 1.226869 P (ISO 724), the largest ISO 4753 allows; other forms give it); extent?: full|length|up_to (default: length when `length` is given, up_to when `up_to` is, else full: the whole face); length?: expr (from the end the thread starts at); up_to?: [x,y,z] (a point, such as on a shoulder face: the thread runs to its place along the axis) | plane name or {origin, normal} (to where the plane crosses the axis); from?: [x,y,z] near the end of the face the thread starts at (default: the face's only free end, the one with no material past it; else the end the `face` point lies within a third of the face of; a pick in the middle of a face whose ends are alike is refused); modeled?: bool (default false); continuous_flanks?: bool (default true; false keeps the historical half-turn faces and groove-before-chamfer construction). Cosmetic: the model is unchanged, model.threads lists it. Modeled: cuts the thread profile into the body: past a free end the groove runs out a pitch into the air (or through a chamfer); at a shoulder, a head or a blind hole's bottom it stops with its section clear of it, never cutting into it; a hole as wide as the thread or wider is narrowed to the thread's minor diameter first, and the reply's warning says so, unless its wall is open along the thread (a cross hole, a cut through the side, a slanted end), which is refused with the drill to use; a shaft thicker than the thread is refused with the size to make it, never turned down

Boundary Fill

Command group: SOLID › CREATE

Read the solid features guide.

ParameterTypeRequiredDescription
tools[bodies]Yes
cells[[x,y,z] a point inside each cell to fill (a cell: inside some tools, outside the rest)]Yes
operationnew|join|cut|intersectNo
remove_toolsboolNo
Automation details

Command ID: solid.boundary_fill

Parameters:

tools: [bodies]; cells: [[x,y,z] a point inside each cell to fill (a cell: inside some tools, outside the rest)]; operation?: new|join|cut|intersect; remove_tools?: bool

Coil

Command group: SOLID › CREATE

Read the solid features guide.

ParameterTypeRequiredDescription
diameterYes
section_sizeYes
sectioncircular|squareNo
section_positioninside|center|outsideNo
base[x,y,z]Nonumbers in mm or length expressions; default: the origin
axisX|Y|Z|[x,y,z]Nodefault Z
start_angleNo
clockwiseboolNo
sketchNoa sketch; its plane is the section plane, so its placement sets the radius and start phase
profilesNodefault: all; one closed profile without holes
operationNo
targetsNo
nameNo
body_nameNo
Automation details

Command ID: solid.coil

Parameters:

diameter; two of revolutions (or turns), height, pitch; section_size; section?: circular|square; section_position?: inside|center|outside; base?: [x,y,z] (numbers in mm or length expressions; default: the origin); axis?: X|Y|Z|[x,y,z] (default Z); start_angle?; clockwise?: bool; sketch? (a sketch; its plane is the section plane, so its placement sets the radius and start phase), profiles? (default: all; one closed profile without holes); operation?, targets?, name?, body_name?

Rib

Command group: SOLID › CREATE

Add a thin wall along an open sketch line, filled out to the faces of the part: a gusset in the corner of an L-bracket, or a stiffening rib on a wall. You draw where the rib’s edge goes, and the rib fills from there to the part.

Before you start

  • You need a body and a sketch with an open line (or chain of lines) for the rib’s outer edge. The line doesn’t have to touch the part, but the rib must have faces to fill to.
  • For a gusset, sketch on a plane through the middle of the bracket, and draw the line across the corner from one leg to the other.
  • With Direction on In plane, the rib fills from the line toward the part inside the sketch plane, and Thickness is across that plane.

Options

Choice in the dialogMeaningExample or reason to choose it
CurvesThe open sketch line or lines for the rib’s edge.A diagonal line across the inside corner of an L-bracket.
DirectionIn plane: the rib fills from the line within the sketch plane, Thickness across the plane. Normal: the rib grows along the sketch’s normal instead, Thickness across the line.In plane for a gusset.
ThicknessHow thick the rib is. It starts at 2 in the design’s unit.0.25 in for a gusset on a 0.25 in bracket.
DepthHow far the rib fills from the line. Empty means to the next face of the part.Leave it empty for a gusset.
FlipFill toward the other side of the line.Turn it on if the preview fills away from the corner.

Try it: put a gusset in an L-bracket

  1. Make an L-bracket: on the XZ plane, sketch an L with 3 in legs and a 0.25 in thickness, then extrude it 2 in symmetric so the XZ plane runs through its middle.
  2. Start a sketch on the XZ plane. Draw a line across the inside corner, from 2 in up the vertical leg to 2 in out along the horizontal leg. Finish the sketch.
  3. Click Rib. Click Curves and pick the line. Leave Direction on In plane and set Thickness to 0.25 in.
  4. Check the preview fills toward the corner; if not, turn on Flip. Click OK. A 0.25 in gusset fills the corner, centered on the bracket.

Tips and common mistakes

  • Do sketch an In plane rib on the plane where you want its middle. Half the thickness goes each way from that plane, so a rib sketched on an outside face hangs half off the part. With Normal, the rib grows out of the sketch plane instead, centered on the line: draw the line where the rib’s middle goes, on a plane at the rib’s edge.
  • Do weigh Rib against a plain extrude. For one gusset on a machined part, a triangle sketched and extruded Symmetric works just as well. Rib pays off when the walls may change size (it fills to the new faces), when the faces it meets are curved or sloped, or when you need many ribs (Web).
  • Do keep a machined rib’s thickness at least as big as the end mill that has to cut around it, and think about how the pocket beside it will be cut. A thin rib between deep pockets chatters.
  • Do type the unit with the number when the print uses another unit. In a millimetre design, typing 0.25 in enters 6.35 mm.
  • Don’t close the sketch line into a loop. A rib needs an open line; use Extrude for a closed shape.

See also

Web makes a grid of ribs from several lines at once. Fillet rounds the rib’s edges.

ParameterTypeRequiredDescription
sketch[open curve ids, in order]Yes
curves[open curve ids, in order]Yes
thicknessYesacross the sketch plane, or across the curves when normal
depthNodefault: to the next face
flipboolNofill to the other side
directionin_plane|normalNogrow across the sketch plane, the default, or along its normal
Automation details

Command ID: solid.rib

Parameters:

sketch, curves: [open curve ids, in order]; thickness (across the sketch plane, or across the curves when normal); depth? (default: to the next face); flip?: bool (fill to the other side); direction?: in_plane|normal (grow across the sketch plane, the default, or along its normal)

Web

Command group: SOLID › CREATE

Add a grid of thin walls from several open sketch lines at once, each filled out to the faces of the part: the ribs of a waffle or isogrid pocket, dividers in a tray, or supports inside a housing. Each line becomes its own wall.

Before you start

  • You need a body and a sketch of open lines, one for each wall. Lines can cross; each one makes its own wall.
  • For walls standing in a pocket or tray, sketch the lines on the top face or a plane above the pocket and set Direction to Normal. The walls then grow down from the sketch to the floor, Thickness across each line.
  • With Direction on In plane, each wall fills inside the sketch plane instead, as Rib does, with half the Thickness each side of the plane.

Options

Choice in the dialogMeaningExample or reason to choose it
CurvesThe open sketch lines, one wall per line.Two crossing lines over a pocket.
DirectionIn plane: each wall fills within the sketch plane, Thickness across the plane. Normal: each wall grows along the sketch’s normal, Thickness across its line.Normal for walls standing in a pocket.
ThicknessHow thick each wall is. It starts at 2 mm.0.080 in for the ribs of a lightened pocket.
DepthHow far each wall fills from the sketch. Empty means to the next face of the part.Leave it empty to run the walls down to the floor.
FlipFill toward the other side of the sketch.Turn it on if the preview grows away from the part.

Try it: divide a tray into four

  1. Make a 4 in × 3 in × 1 in box and use Shell on its top face with a 0.1 in wall, so it’s an open tray.
  2. Start a sketch on the tray’s top rim plane. Draw one line across the middle lengthwise and one across the middle widthwise, each ending at the inside walls. Finish the sketch.
  3. Click Web. Click Curves and pick both lines. Set Direction to Normal and Thickness to 0.1 in. Leave Depth empty.
  4. Check the preview runs down into the tray; if not, turn on Flip. Click OK. The tray now has four pockets.

Tips and common mistakes

  • Do make the walls between machined pockets at least as thick as you can hold without chatter, and check the end mill can reach the corners between them. The corner radius left is the cutter’s radius.
  • Do use one Web for the whole grid. It’s one feature to change when the rib layout changes.
  • Do type the unit with the number when the print uses another unit. In a millimetre design, typing 0.1 in enters 2.54 mm.
  • Don’t close the lines into loops. Web needs open lines; for a pocket outline use Extrude with Cut.

See also

Rib makes a single wall the same way. Shell hollows the part first.

ParameterTypeRequiredDescription
sketch[open curve ids]Yes
curves[open curve ids]Yesone wall each
thicknessYes
depthNodefault: to the next face
flipboolNo
directionin_plane|normalNo
Automation details

Command ID: solid.web

Parameters:

sketch, curves: [open curve ids] (one wall each); thickness; depth? (default: to the next face); flip?: bool; direction?: in_plane|normal

Emboss

Command group: SOLID › CREATE

Raise sketch text or a logo out of a face, or sink it in: an engraved part number, a serial or rev letter, a logo, or raised lettering on a casting pattern. It works on flat faces, and straight-line outlines can wrap around a cylinder.

Before you start

  • You need a body and a sketch with closed profiles to emboss, usually text made with the sketch’s Text tool. Sketch it on or parallel to the face it goes on.
  • Deboss sinks the profiles into the face: that’s engraving. Emboss raises them out of it.
  • Only profiles made of straight lines running along and around the cylinder (orthogonal lines, such as a rectangular label or flat) wrap around it, sketched on a plane tangent to it. Text with curves or diagonals doesn’t wrap; engrave it on a flat face instead. Pick the cylinder’s wall as the Target Face to say which one when there is more than one.

Options

Choice in the dialogMeaningExample or reason to choose it
ProfilesThe sketch profiles to emboss: the letters or the logo outline.The text “PN 1234-A”.
Target Face (optional)The face the profiles go on. Leave it empty to use the face under the sketch.The outside of a shaft, to wrap a rectangular label area around it.
DepthHow deep the profiles sink in, or how high they stand out. It starts at 1 in the design’s unit.0.005 in to 0.010 in for engraving.
TypeEmboss raises the profiles out of the face. Deboss sinks them into it. It starts at Emboss.Deboss for an engraved part number.

Try it: engrave a part number

  1. Make a 3 in × 2 in × 0.75 in block.
  2. Start a sketch on the top face. Start the sketch’s Text tool: it’s in the sketch ribbon’s Create dropdown, between Centerline and Divide Curve, or press S and type Text. Write “PN 1234-A”, 0.25 in high, and finish the sketch.
  3. Click Emboss. Click Profiles and pick the letters. Each letter shape counts as its own profile, so Profiles shows several selected.
  4. Set Type to Deboss and Depth to 0.010 in. Click OK. The part number is engraved 0.010 in deep into the top face.

Tips and common mistakes

  • Do use Deboss for markings on a machined part. Engraving takes one pass with an engraving tool; raised lettering means milling away everything around it.
  • Do keep engraving shallow: 0.005 in to 0.010 in reads clearly and doesn’t take a small engraving tool too deep.
  • Do pick a text height and font your engraving tool can cut. Strokes thinner than the tool’s tip get lost.
  • Do change the Depth from its default. It starts at 1 in the design’s unit, far too deep for a marking.
  • Do type the unit with the number when the print uses another unit. In a millimetre design, typing 0.010 in enters 0.254 mm.

See also

Extrude cuts or raises a profile to a set distance. Hole places drilled and tapped holes.

ParameterTypeRequiredDescription
sketchYes
profilesNo
depthYes
modeemboss|debossNo
targetsNo
faceworld point [x,y,z] selecting a planar or complete exterior cylindrical wallNoinferred when exactly one supported tangent cylinder matches the selected profiles
Automation details

Command ID: solid.emboss

Parameters:

sketch, profiles?; depth; mode?: emboss|deboss; targets?; face?: world point [x,y,z] selecting a planar or complete exterior cylindrical wall (inferred when exactly one supported tangent cylinder matches the selected profiles)

Joint Origin

Command group: SOLID › CREATE

Read the components and joints guide.

ParameterTypeRequiredDescription
nameNo
occurrenceid|nameNodefault: from the pick, or the named face's component
path[occurrence ids from the root]Nothe placement picked on, ending at the occurrence: a part in a sub-assembly placed more than once is then on that copy; default: the occurrence's first placement, or with no occurrence the placement nearest the pick
face[x,y,z] | "<name>"Yescentre of a planar face
circle[x,y,z] | "<name>"Yescentre of a round end: a point on its wall or rim, the end's centre, or the round face's name as model.faces lists it
edge[x,y,z]Yesa point on an edge of a planar face: the edge's middle, z out of the face, y into it; on?: [x,y,z] a point on the face (default: the larger planar face beside the edge); corner?: [x,y,z] near one end: the corner there, x and y along its two edges into the face; found again by name, so it follows the face as it changes size; z?: [x,y,z] the frame's z instead of the face's normal, along the edge when it runs that way, x chosen from z: an edge or corner picked as it is, kept as a point where it can't be found by name
point[x,y,z]Yes
z[x,y,z]No
plane"XY"|"XZ"|"YZ"|"o:<ids>/origin/<item>"|"<name>"Yes
axis"X"|"Y"|"Z"|"o:<ids>/origin/<item>"|"<name>"Yes
bodythe named face's bodyNo
end"<name>"Noa circle's end: the face across its rim there; default the end nearer the pick
Automation details

Command ID: joint.origin

Parameters:

name?, occurrence?: id|name (default: from the pick, or the named face's component), path?: [occurrence ids from the root] (the placement picked on, ending at the occurrence: a part in a sub-assembly placed more than once is then on that copy; default: the occurrence's first placement, or with no occurrence the placement nearest the pick), face: [x,y,z] | "<name>" (centre of a planar face) | circle: [x,y,z] | "<name>" (centre of a round end: a point on its wall or rim, the end's centre, or the round face's name as model.faces lists it) | edge: [x,y,z] (a point on an edge of a planar face: the edge's middle, z out of the face, y into it; on?: [x,y,z] a point on the face (default: the larger planar face beside the edge); corner?: [x,y,z] near one end: the corner there, x and y along its two edges into the face; found again by name, so it follows the face as it changes size; z?: [x,y,z] the frame's z instead of the face's normal, along the edge when it runs that way, x chosen from z: an edge or corner picked as it is, kept as a point where it can't be found by name) | point: [x,y,z], z?: [x,y,z] | plane: "XY"|"XZ"|"YZ"|"o:<ids>/origin/<item>"|"<name>" | axis: "X"|"Y"|"Z"|"o:<ids>/origin/<item>"|"<name>"; a circle's z?: [x,y,z] (a round edge's centre with the way out of it: its end by name when that is the frame it shows, else a point there); body?: the named face's body; end?: "<name>" (a circle's end: the face across its rim there; default the end nearer the pick); x?: [x,y,z] (a direction, world) | "<face name>" | {face: [x,y,z]} (the frame's x: a direction, a plane's normal, or the way to another hole's axis; keeps a round origin's turn; a point's x is a direction, laid square to its z, default: chosen from z alone). Faces are kept by name: the origin follows its face, and a circle its end, when the design changes

SOLID › INSERT

Insert Point Cloud

Command group: SOLID › INSERT

Read the solid features guide.

Automation details

Command ID: pointcloud.insert

Parameters:

path: .ply, .xyz or .pts file (its points join the design as a point cloud: drawn as points, never a body; a PLY's red, green and blue are its colours, its other properties can colour and filter it; its edge and face elements become segments and triangles, a segment with width and height properties drawn as a bead) → cloud, name, points, segments, triangles, source_points, bounds, warnings; name?; units?: mm|cm|m|in|ft (the file's length unit, default mm); max_points?: most points kept (default 5000000, at most 20000000; a larger file is thinned evenly); center?: bool (move the cloud so the middle of its base sits at the origin); point_size?: pixels (0.5…16, default 2); color_by?: file|single|height or a property's name (the file's colours, the default; one colour; a ramp by height, blue low to red high; or by any property of the file, a ramp over its values or a palette for whole numbers); color?: [r, g, b] 0…1 (the single colour; given alone it sets color_by single, null sets color_by file); color_range?: [low, high] the ramp runs between (design Z in mm by height), or null for the automatic range (by height all but the lowest and highest 2 % of the points; by a property the values it takes over what the filters keep); height_range?: the same as color_range; filters?: [{property, range: [low, high]} or {property, values: [whole numbers]} for a property with named values], or null (only what every filter keeps is shown)

Insert Fastener

Command group: SOLID › INSERT

Read the solid features guide.

Automation details

Command ID: parts.fastener

Parameters:

the Insert Part dialog's command (same as parts.insert): family, size, length?, at?|point?+direction? (from the shank's tip toward the head), flip?, name?

Insert STEP

Command group: SOLID › INSERT

Read the import and export guide.

ParameterTypeRequiredDescription
folderboolNodefault false; true: an import that brings more than one item into the history (its Import feature and the rigid joints holding its parts) goes into a new history folder named `Group #N`
nameNothe feature's and the component's
upz|yNodefault z: the file's axes are kept; y: the file has Y up, so the import's component is turned a quarter turn about X, Y becoming Z → stand_up: its occurrence
namesauto|solid|product|instanceNodefault auto: keep meaningful solid names, replace placeholders with the nearest named product, else instance; solid: the solid's own name; product: the part's name; instance: the occurrence's name, else the product's
historyfeatures|solids for a FreeCAD fileNodefault features: its sketches and the Pad, Pocket, Revolution, Groove, Fillet, Chamfer and primitive features that use them are rebuilt as features in the new component, when every one can be and they build what the file holds → features_added, history: "features"; else, or with solids, the file's solids come in as an Import feature, and a warning says why the history was not rebuilt
matrix[12 or 16 numbers]Nowhere the new component's occurrence sits in the active component, after `up`: a rigid row-major 3x4 rotation-and-translation or 4x4 matrix, flat or as rows, as occurrence.move takes it; a scale, shear or mirror is refused before the file is read
toleranceNosee occurrence.move
Automation details

Command ID: file.insert_step

Parameters:

path: STEP, IGES, FreeCAD .FCStd or Rhino .3dm file (its bodies join the design as an Import base feature in a new component named after the file, placed in the active component; an assembly's products become nested components, a repeated part one component with an occurrence per placement, all held as one rigid group until component.unlock → component, component_name, occurrence, subcomponents?, rigid_joints?, group?: the folder); folder?: bool (default false; true: an import that brings more than one item into the history (its Import feature and the rigid joints holding its parts) goes into a new history folder named `Group #N`); name? (the feature's and the component's); up?: z|y (default z: the file's axes are kept; y: the file has Y up, so the import's component is turned a quarter turn about X, Y becoming Z → stand_up: its occurrence); names?: auto|solid|product|instance (default auto: keep meaningful solid names, replace placeholders with the nearest named product, else instance; solid: the solid's own name; product: the part's name; instance: the occurrence's name, else the product's); history?: features|solids for a FreeCAD file (default features: its sketches and the Pad, Pocket, Revolution, Groove, Fillet, Chamfer and primitive features that use them are rebuilt as features in the new component, when every one can be and they build what the file holds → features_added, history: "features"; else, or with solids, the file's solids come in as an Import feature, and a warning says why the history was not rebuilt); matrix?: [12 or 16 numbers] (where the new component's occurrence sits in the active component, after `up`: a rigid row-major 3x4 rotation-and-translation or 4x4 matrix, flat or as rows, as occurrence.move takes it; a scale, shear or mirror is refused before the file is read); tolerance? (see occurrence.move)

Insert Mesh

Command group: SOLID › INSERT

Read the import and export guide.

ParameterTypeRequiredDescription
nameNo
upz|y for a glTF fileNodefault y: glTF has Y up, so the import's component is turned a quarter turn about X, Y becoming Z → stand_up: its occurrence; z: the file's axes are kept
originboolNodefault false: the mesh keeps its file's coordinates; true: once read, scaled and stood up, the import's occurrence is moved so the box around its bodies is centred on X and Y of the active component with its lowest point on Z = 0 → moved_to_origin: [x, y, z], the shift made
crease_angle_degrees0..180No0 clears imported shading normals for the previous flat-shading behavior; positive angles regenerate angle-weighted normals across manifold edges below this crease angle; omitted keeps authored OBJ normals/smoothing groups, or flat shading when absent; geometry stays unchanged
Automation details

Command ID: file.insert_mesh

Parameters:

path: .3mf, .stl, .obj, .ply (with faces), .glb or .gltf file (its meshes join the design as mesh bodies in a new component named after the file → component, component_name, occurrence; a glTF file's node tree becomes nested components held as one rigid group until component.unlock, a mesh placed again another occurrence, its materials the bodies' appearances, and its metres millimetres); name?; up?: z|y for a glTF file (default y: glTF has Y up, so the import's component is turned a quarter turn about X, Y becoming Z → stand_up: its occurrence; z: the file's axes are kept); units?: mm|cm|m|in|ft for an STL or OBJ file, or a PLY file with faces (default mm): the unit its coordinates are in, scaled to millimetres as the file is read (a 3MF or glTF file states its own). The design keeps the millimetre mesh and not the unit, so insert the file again to try another; origin?: bool (default false: the mesh keeps its file's coordinates; true: once read, scaled and stood up, the import's occurrence is moved so the box around its bodies is centred on X and Y of the active component with its lowest point on Z = 0 → moved_to_origin: [x, y, z], the shift made); crease_angle_degrees?: 0..180 (0 clears imported shading normals for the previous flat-shading behavior; positive angles regenerate angle-weighted normals across manifold edges below this crease angle; omitted keeps authored OBJ normals/smoothing groups, or flat shading when absent; geometry stays unchanged)

SOLID › MODIFY

Fillet

Command group: SOLID › MODIFY

Default keyboard shortcut: F

Round the edges of a solid: the “R.03 typ” or “break all edges R.015” callout on a print, or the corner radius left in a pocket by the end mill. You pick the edges and the radius.

Before you start

  • You need a solid body with edges to round.
  • Pick edges one at a time, or click a face to round every edge around it.
  • Round the larger radii first. Do the vertical corners of a block before its top edges, so the small top round runs smoothly around the big corner.
  • An inside corner on a milled part can’t be sharper than the cutter. Make inside radii a little larger than the end mill’s radius, so the tool doesn’t bury itself in the corner.

Options

Choice in the dialogMeaningExample or reason to choose it
EdgesThe edges to round. Click edges one by one, or click a face to take all of its edges.The four vertical corners of a block, or the top face.
RadiusThe radius of the round. A new fillet starts at 2 mm, shown in the design’s unit.0.03 in for a typical edge break.
TypeConstant radius: one radius all along the edge. Chord length: the width across the round stays the same whatever angle the faces meet at. Variable radius: a radius at each end, and more along the edge if you add them. Curvature continuous: a smoother section where it meets flat faces (G2). Spline: the radius follows control radii you set along the edge.Constant radius for almost everything on a machined part. Variable radius for a round that has to grow along an edge.
Corners (Constant radius)Shown when three or more of the picked edges meet at one corner, such as rounding every edge of a block in one Fillet. Tangent makes a rolling-ball corner. Curvature makes a smooth patch set back along each edge, with a Setback box.Leave it on Tangent. That is the corner a ball end mill leaves.
Face limits (Constant radius)What happens when the radius is too big for the faces beside an edge. Exact radius stops and says why. Largest radius rounds every edge with the largest radius that fits, and shows the Applied Radius. Narrowed radius keeps the radius and narrows the round only where the faces run out.Leave it on Exact radius, so a radius that doesn’t fit is caught instead of quietly changed.
Turns (Constant radius)Where two rounds turn around a sharp corner edge, such as the rounds along the base of a boss at its upright edges. Mitre runs the rounds on until they meet. Swept turns them around the corner edge.Mitre unless you want the corner rolled around.
End Radius, Swap Ends, Mid Radius (Variable radius)The radius at the far end of the first edge, which end gets the start radius, and extra radii along the edge (Add Mid Radius). Smooth radius change blends between them instead of running straight.A round that goes from 0.06 in at one end to 0.25 in at the other.
Tangent chainFollows the edge around smooth (tangent) bends, so one pick rounds the whole run.Leave it on for edges that flow into an existing round.
PreviewShows the round on the part while you set it up.Leave it on.

Try it: round the corners and break the top edges of a plate

  1. Make a 3 in × 2 in × 0.75 in block (sketch a rectangle, Extrude 0.75 in).
  2. Click Fillet and click the four vertical corner edges. Set Radius to 0.25 in and click OK.
  3. Click Fillet again and click the top face. Edges shows 8 selected: the four straight edges and the four rounded corners.
  4. Set Radius to 0.03 in and click OK. The top edge has a 0.03 in round all the way around.

Tips and common mistakes

  • Do type the unit with the number when the print uses another unit. In a millimetre design, typing 0.03 in enters 0.762 mm.
  • Do round all the edges of one size in a single Fillet. It’s one feature to change when the print changes.
  • Do round the big corners before the small edge breaks. Done the other way, the small round has to turn a sharp corner and can fail or leave an odd shape.
  • Don’t make a round bigger than the faces beside it allow. With Face limits on Exact radius, the dialog says “The radius is too large for the faces beside these edges” and OK stays grayed out; use a smaller radius, or set Face limits to Largest radius.
  • Don’t model a round you’ll never see just to look finished. Every fillet adds rebuild time and can get in the way of later features.

See also

Chamfer bevels an edge instead of rounding it. Shell hollows a part after the outside edges are rounded.

ParameterTypeRequiredDescription
edges[[x,y,z] point on edge | {body, index}]Yes
radiusexprYes
tangent_chainboolNodefault true: each picked edge's tangent chain is rounded with it; false: exactly the picked edges
bodythe body the edges are onNowithout it, each point is looked up on every placed body, shown or hidden, and a point on edges of 2 bodies is refused, naming them
typeconstant|curvature|chord|variable|splineNoalias g2: curvature-continuous quintic between planar faces along straight edges, one planar end face at each end (including sloped ends); radius sets the equivalent circular setback, not constant curvature; exact fit only; radius is the width across; radius at the end nearest start, radius2 at the other; one end may be 0, where the round closes to the corner; the radius along the edge is a clamped B-spline from the end nearest start: radius is its first control radius, radii the rest, knots its knot vector from 0 to 1; one end may be 0
radius2No
start[x,y,z]No
radii[expr, …]No
knots[number, …]No
mid[[position 0…1 from start, radius expr], …]Novariable radii between the ends
smoothboolNosmooth instead of straight between the radii
cornerrolling|g2Norolling, the default: the corners where three blended edges meet are pieces of the rolling ball's sphere, tangent to each blend; a curvature round's corners are always g2 patches, between its own sections; g2: each corner where three or more rounded edges meet, and every edge there is rounded, is set back (1.5 × the radius along each edge unless `setbacks` names it) and filled with a patch meeting the rounds and the faces round it to the second order (curvature-continuous seams, clear of the points where a round's section ends on a face), constant-radius exact fillets only, refused where it cannot be built
setbacks[{vertex: [x,y,z], distance: expr}, …]Nocorner setbacks: the corner at the vertex nearest `vertex`, where every edge meeting it is rounded, starts its patch `distance` along each of those edges from the vertex, past where the rounds would meet (past the radius at a square corner), so the rounds blend through a larger patch; one distance for all of a corner's edges; constant-radius exact fillets only; with `corner: g2`, the G2 patch's corners
turnmitre|sweptNowhere two rounds turn round a sharp, straight, convex side edge square to both on a plane, such as the rounds along a boss's base at its upright edges: mitre, the default, runs them on to meet in a mitre; swept ends them at the side edge and fills the corner with their section revolved about it, a torus; swept takes type: constant, corner: rolling, no setbacks and fit: exact or reduce, and is refused with the reason where the rounds are unequal or the side edge is concave, curved or tangent
fitexact|reduce|taperNoexact, the default: a radius too large for the geometry around the edges is refused, naming the largest that fits; reduce: where it doesn't fit it is reduced to the largest that does, one radius for edges sharing a face; the smallest used is returned as `radius_applied`, each edge's as `radii_applied: [{edge: [x,y,z], radius}]`, and noted in the feature's warning; taper: along each straight edge between planar faces where it doesn't fit, the radius follows the room there as a smooth spline, down to 0 where a face closes to a point, noted in the feature's warning
Automation details

Command ID: solid.fillet

Parameters:

edges: [[x,y,z] point on edge | {body, index}] ; radius: expr; tangent_chain?: bool (default true: each picked edge's tangent chain is rounded with it; false: exactly the picked edges); body?: the body the edges are on (without it, each point is looked up on every placed body, shown or hidden, and a point on edges of 2 bodies is refused, naming them); type?: constant|curvature (alias g2: curvature-continuous quintic between planar faces along straight edges, one planar end face at each end (including sloped ends); radius sets the equivalent circular setback, not constant curvature; exact fit only)|chord (radius is the width across)|variable (radius at the end nearest start, radius2 at the other; one end may be 0, where the round closes to the corner)|spline (the radius along the edge is a clamped B-spline from the end nearest start: radius is its first control radius, radii the rest, knots its knot vector from 0 to 1; one end may be 0); radius2?; start?: [x,y,z]; radii?: [expr, …]; knots?: [number, …]; mid?: [[position 0…1 from start, radius expr], …] (variable radii between the ends); smooth?: bool (smooth instead of straight between the radii); corner?: rolling|g2 (rolling, the default: the corners where three blended edges meet are pieces of the rolling ball's sphere, tangent to each blend; a curvature round's corners are always g2 patches, between its own sections; g2: each corner where three or more rounded edges meet, and every edge there is rounded, is set back (1.5 × the radius along each edge unless `setbacks` names it) and filled with a patch meeting the rounds and the faces round it to the second order (curvature-continuous seams, clear of the points where a round's section ends on a face), constant-radius exact fillets only, refused where it cannot be built); setbacks?: [{vertex: [x,y,z], distance: expr}, …] (corner setbacks: the corner at the vertex nearest `vertex`, where every edge meeting it is rounded, starts its patch `distance` along each of those edges from the vertex, past where the rounds would meet (past the radius at a square corner), so the rounds blend through a larger patch; one distance for all of a corner's edges; constant-radius exact fillets only; with `corner: g2`, the G2 patch's corners); turn?: mitre|swept (where two rounds turn round a sharp, straight, convex side edge square to both on a plane, such as the rounds along a boss's base at its upright edges: mitre, the default, runs them on to meet in a mitre; swept ends them at the side edge and fills the corner with their section revolved about it, a torus; swept takes type: constant, corner: rolling, no setbacks and fit: exact or reduce, and is refused with the reason where the rounds are unequal or the side edge is concave, curved or tangent); fit?: exact|reduce|taper (exact, the default: a radius too large for the geometry around the edges is refused, naming the largest that fits; reduce: where it doesn't fit it is reduced to the largest that does, one radius for edges sharing a face; the smallest used is returned as `radius_applied`, each edge's as `radii_applied: [{edge: [x,y,z], radius}]`, and noted in the feature's warning; taper: along each straight edge between planar faces where it doesn't fit, the radius follows the room there as a smooth spline, down to 0 where a face closes to a point, noted in the feature's warning)

Chamfer

Command group: SOLID › MODIFY

Cut a flat bevel along edges of a solid: the “break all edges” or “0.03 × 45°” callout on a print. You pick the edges and how the chamfer is sized.

Before you start

  • You need a solid body with edges to chamfer.
  • Pick edges one at a time, or click a face to chamfer every edge around it.
  • Chamfer edges before you add other features that touch them. An edge that already meets a small chamfer or fillet has little room left, and a chamfer there can fail.

Options

Choice in the dialogMeaningExample or reason to choose it
EdgesThe edges to chamfer. Click edges one by one, or click a face to take all of its edges.The four top edges of a block, or the top face.
Chamfer TypeEqual Distance: the same leg on both faces. That is a 45° chamfer only where the faces meet at 90°. Two Distances: a different leg on each face. Distance and Angle: one leg and an angle.Equal Distance for a standard edge break on a square corner. Distance and Angle whenever the print gives an angle, like 0.06 × 30°, and for any chamfer on a sloped face.
Distance (Equal Distance)The leg length on both faces.0.03 in for a typical edge break.
Distance 1 and Distance 2 (Two Distances)The leg on each face.0.06 in by 0.03 in.
Distance 1 and Angle (Distance and Angle)The leg on the first face, and the angle off that face.0.06 in at 45°.
FlipSwaps which face the first distance is measured on. Shown for Two Distances and Distance and Angle.Turn it on if the long leg ends up on the wrong face.
Tangent chainFollows the edge around smooth (tangent) bends, such as a fillet, so one pick chamfers the whole run.Leave it on for edges that flow into a fillet.
PreviewShows the chamfer on the part while you set it up.Leave it on.

Try it: break the top edges of a plate

  1. Make a 3 in × 2 in × 0.75 in block (sketch a rectangle, Extrude 0.75 in).
  2. Click Chamfer and click the four top edges. Edges shows 4 selected.
  3. Set Chamfer Type to Equal Distance and Distance to 0.03 in.
  4. Click OK. All four top edges have a 0.03 × 45° chamfer.
  5. To change it, edit the chamfer and set Chamfer Type to Distance and Angle, Distance 1 to 0.06 in and Angle to 30. Use Flip to swap which face takes the 0.06 in leg.

Tips and common mistakes

  • Do type the unit with the number when the print uses another unit. In a millimetre design, typing 0.03 in enters 0.762 mm.
  • Do chamfer all the edges of one size in a single Chamfer. It’s one feature to change when the print changes.
  • Don’t chamfer an edge right next to an existing small chamfer with a larger size. If the chamfer would run past the neighbouring face, the dialog says so; make the chamfer smaller or chamfer those edges together in one feature.

See also

Hole adds a countersink at the top of a hole. Fillet rounds an edge instead of beveling it.

ParameterTypeRequiredDescription
edges[[x,y,z] | {body, index}]Yes
distanceexprYes
tangent_chainboolNodefault true: each picked edge's tangent chain is cut with it; false: exactly the picked edges
distance2exprNoalong the second face
angleexprNofrom the first face
flipboolNowhich face is first: by default the one facing up most
bodythe body the edges are onNowithout it, each point is looked up on every placed body, shown or hidden, and a point on edges of 2 bodies is refused, naming them
Automation details

Command ID: solid.chamfer

Parameters:

edges: [[x,y,z] | {body, index}] ; distance: expr; tangent_chain?: bool (default true: each picked edge's tangent chain is cut with it; false: exactly the picked edges); distance2?: expr (along the second face) | angle?: expr (from the first face); flip?: bool (which face is first: by default the one facing up most); body?: the body the edges are on (without it, each point is looked up on every placed body, shown or hidden, and a point on edges of 2 bodies is refused, naming them)

Combine

Command group: SOLID › MODIFY

Read the solid features guide.

ParameterTypeRequiredDescription
targetbodyYes
tools[body]Yes
operationjoin|cut|intersectNo
touchingseparate|one_bodyNowhat a join makes of pieces that share no face: separate, the default, makes each piece that touches another only along an edge or at a point a body of its own and keeps pieces that touch nothing in one body; one_body keeps every piece in one body
keep_toolsboolNo
new_componentboolNothe result goes into a new component; component_name?
Automation details

Command ID: solid.combine

Parameters:

target: body; tools: [body]; operation?: join|cut|intersect; touching?: separate|one_body (what a join makes of pieces that share no face: separate, the default, makes each piece that touches another only along an edge or at a point a body of its own and keeps pieces that touch nothing in one body; one_body keeps every piece in one body); keep_tools?: bool; new_component?: bool (the result goes into a new component; component_name?)

Scale

Command group: SOLID › MODIFY

Read the solid features guide.

ParameterTypeRequiredDescription
bodies[names]Yes
factorexprYes
factors[x, y, z] exprsYes
origin[x,y,z]No
Automation details

Command ID: solid.scale

Parameters:

bodies: [names]; factor: expr | factors: [x, y, z] exprs; origin?: [x,y,z]

Delete Face

Command group: SOLID › MODIFY

Read the solid features guide.

ParameterTypeRequiredDescription
faces[[x,y,z] points on the faces, or face names from model.faces]Yes
bodyNo
healboolNodefault true: the neighbouring planes close the gap, as when a rounded edge, hole or boss is taken off; false: the gap stays open and the faces left are a surface body
Automation details

Command ID: face.delete

Parameters:

faces: [[x,y,z] points on the faces, or face names from model.faces]; body?; heal?: bool (default true: the neighbouring planes close the gap, as when a rounded edge, hole or boss is taken off; false: the gap stays open and the faces left are a surface body)

Offset Face

Command group: SOLID › MODIFY

Push faces of a solid in or out by a set distance, and the faces around them follow. Use it to add finish stock, open up a pocket or bore, make a pocket deeper or a wall thicker, without going back into the sketch.

Before you start

  • You need a solid body. The faces can be flat or curved, such as the wall of a bore.
  • A positive Distance pushes the face out of the material and adds stock. A negative Distance pulls it into the material and removes stock.
  • On an inside wall, such as the side of a pocket or the wall of a bore, out of the material means into the opening. A negative Distance opens the pocket or bore up; a positive one closes it in.

Options

Choice in the dialogMeaningExample or reason to choose it
FacesThe faces to move. You can pick more than one.The floor of a pocket, or the wall of a bore.
DistanceHow far each face moves, square to the face. Positive adds material, negative removes it.0.010 in to leave stock on a face for finishing. -0.005 in on a bore wall to open the bore 0.010 in on the diameter.

Try it: deepen and widen a pocket

  1. Make a 3 in × 2 in × 0.75 in block and cut a 1 in × 1 in pocket 0.25 in deep into the top.
  2. Click Offset Face (in the Modify menu) and click the floor of the pocket. Set Distance to -0.125 in. The arrow points down into the part. Click OK. The pocket is now 0.375 in deep.
  3. Click Offset Face again and click one side wall of the pocket. Set Distance to -0.25 in. The arrow points into the material, away from the pocket. Click OK. The pocket is now 1.25 in wide that way.

Tips and common mistakes

  • Do remember a bore moves on the radius. To open a bore 0.010 in on the diameter, offset its wall -0.005 in.
  • Do watch the arrow before clicking OK. It shows which way the face will move.
  • Do type the unit with the number when the print uses another unit. In a millimetre design, typing 0.010 in enters 0.254 mm.
  • Do use Offset Face for a quick size change on a finished model, such as a stock allowance or a fit adjustment. If the size will change again, change the sketch or the feature instead, so the dimension lives where the print puts it.

See also

Extrude sets a depth when the feature is made. Shell sets a wall thickness for a whole hollow part. Hole sets a hole’s size from the start.

ParameterTypeRequiredDescription
faces[[x,y,z] points on faces (planar or curved), or face names from model.faces]Yes
distanceexprYespositive: outward
bodyNo
Automation details

Command ID: solid.offset_face

Parameters:

faces: [[x,y,z] points on faces (planar or curved), or face names from model.faces]; distance: expr (positive: outward); body?

Shell

Command group: SOLID › MODIFY

Hollow out a solid to an even wall thickness, leaving the faces you pick open: a box with an open top, an enclosure, or a cup.

Before you start

  • You need a solid body.
  • Pick the faces to remove. They become the openings. Pick the top face of a block to get an open box.
  • To make a closed hollow part with no openings, click the body instead of a face.
  • Round or chamfer the edges around the opening after you shell, not before. Shell can fail on a round that runs into the opening.

Options

Choice in the dialogMeaningExample or reason to choose it
Faces/BodyThe faces to remove, or the body for a closed hollow part.The top face of a block for an open box.
ThicknessThe wall thickness.0.125 in for a small enclosure.
DirectionInside keeps the outside size and builds the wall inward. Outside keeps the inside size and builds the wall outward. Both splits the wall across the original faces.Inside when the outside size is on the print. Outside when the inside size matters, such as a pocket a part must fit in.
Tangent chain (under Options)Also removes faces that run smoothly into the ones you picked, such as a fillet around the top edge.On by default. Turn it off to remove only the faces you clicked.

Try it: an open-top box

  1. Make a 4 in × 3 in × 2 in block (sketch a rectangle, Extrude 2 in).
  2. Click Shell and click the top face. Faces/Body shows 1 selected.
  3. Set Thickness to 0.125 in and Direction to Inside.
  4. Click OK. The block is an open box, still 4 in × 3 in × 2 in outside, with 0.125 in walls and floor.
  5. To try Outside, edit the shell and set Direction to Outside. The inside is now 4 in × 3 in and the outside grows by the wall.

Tips and common mistakes

  • Do pick Direction from the dimension that matters. Inside holds the outside size, Outside holds the inside size.
  • Do shell first and round the rim after. Shell then doesn’t have to follow the rounds.
  • Don’t click the body when you want an opening. Clicking the body makes a closed hollow part with no way in.

See also

Fillet rounds the rim after you shell. Extrude makes the block to start from.

ParameterTypeRequiredDescription
faces[[x,y,z] points on the faces to remove, or face names from model.faces; none: a closed hollow body]Yes
thicknessexprYes
directioninside|outside|bothNo
tangent_chainboolNoalso open faces joined smoothly to the picked ones
bodyNoneeded with no faces when there are several bodies
Automation details

Command ID: solid.shell

Parameters:

faces: [[x,y,z] points on the faces to remove, or face names from model.faces; none: a closed hollow body]; thickness: expr; direction?: inside|outside|both; tangent_chain?: bool (also open faces joined smoothly to the picked ones); body? (needed with no faces when there are several bodies)

Draft

Command group: SOLID › MODIFY

Read the solid features guide.

ParameterTypeRequiredDescription
faces[[x,y,z] points, or face names from model.faces]Yes
angleexprYes
neutralXY|XZ|YZ|plane name|{origin, normal}Yes
pull[x,y,z]Nodefault: neutral plane normal
bodyNo
Automation details

Command ID: solid.draft

Parameters:

faces: [[x,y,z] points, or face names from model.faces]; angle: expr; neutral: XY|XZ|YZ|plane name|{origin, normal}; pull?: [x,y,z] (default: neutral plane normal); body?

Split Face

Command group: SOLID › MODIFY

Read the solid features guide.

ParameterTypeRequiredDescription
faces[[x,y,z] points on the faces to split, or face names from model.faces]Yes
bodyNo
Automation details

Command ID: solid.split_face

Parameters:

faces: [[x,y,z] points on the faces to split, or face names from model.faces]; body?; and the splitting tool: plane: XY|XZ|YZ|plane name|{origin, normal} — or tool: {body, point} (a face of a body, its surface extended) — or sketch: id or name (its curves, along the sketch's normal; each curve on its own, an open one extended past its ends) and loops?: bool (default false: true joins the curves end to end first, so a closed loop cuts along itself only)

Split Body

Command group: SOLID › MODIFY

Read the solid features guide.

ParameterTypeRequiredDescription
bodynameYes
planeXY|XZ|YZ|plane name|{origin, normal} — or tool: {body, point}Yesa face of a body, its surface extended
linecurve idYesa straight sketch line, along the sketch's normal
Automation details

Command ID: solid.split_body

Parameters:

body: name; plane: XY|XZ|YZ|plane name|{origin, normal} — or tool: {body, point} (a face of a body, its surface extended) — or sketch: id, line: curve id (a straight sketch line, along the sketch's normal)

Press Pull

Command group: SOLID › MODIFY

Default keyboard shortcut: Q

Read the solid features guide.

ParameterTypeRequiredDescription
face[x,y,z] and distance: exprYespositive adds material, negative cuts
edges[[x,y,z]…] and distanceYesa fillet
bodythe body the face or edges are onNofor edges, as for solid.fillet
Automation details

Command ID: solid.press_pull

Parameters:

face: [x,y,z] and distance: expr (positive adds material, negative cuts), or edges: [[x,y,z]…] and distance (a fillet); body?: the body the face or edges are on (for edges, as for solid.fillet)

Move/Copy

Command group: SOLID › MODIFY

Default keyboard shortcut: M

Read the solid features guide.

ParameterTypeRequiredDescription
bodies[names]Yes
translate[x,y,z]Nolength exprs or numbers, applied after rotation
axis[x,y,z]Nodefault: world Z with origin, else the body's component's Z, world Z at the root
angleangle exprNo
origin[x,y,z]Nolength exprs or numbers in world coordinates; default: the body's component's origin, the world origin at the root
tolerancematrix rotation toleranceNodefault 1e-9, at most 1e-3
copyboolNodefault false; true keeps originals and transforms copies
occurrences[occurrence paths, root first]Noonly with copy: copies each component independently and transforms the copies in world space; reply lists them in occurrences
nameNo
body_nameNo
body_namesNo
Automation details

Command ID: solid.move

Parameters:

bodies: [names]; translate?: [x,y,z] (length exprs or numbers, applied after rotation); axis?: [x,y,z] (default: world Z with origin, else the body's component's Z, world Z at the root); angle?: angle expr; origin?: [x,y,z] (length exprs or numbers in world coordinates; default: the body's component's origin, the world origin at the root); matrix?: rigid row-major 3x4 or 4x4, flat or rows (instead of translate/axis/angle/origin); tolerance?: matrix rotation tolerance (default 1e-9, at most 1e-3); copy?: bool (default false; true keeps originals and transforms copies); occurrences?: [occurrence paths, root first] (only with copy: copies each component independently and transforms the copies in world space; reply lists them in occurrences); name?, body_name?, body_names?

Replace Face

Command group: SOLID › MODIFY

Read the solid features guide.

ParameterTypeRequiredDescription
faces[[x,y,z] points on planar faces, or face names from model.faces]Yes
targetplaneYesXY|XZ|YZ|construction plane|{origin, normal}
target_face[x,y,z] on a parallel planar faceYes
bodyNo
Automation details

Command ID: solid.replace_face

Parameters:

faces: [[x,y,z] points on planar faces, or face names from model.faces]; target: plane (XY|XZ|YZ|construction plane|{origin, normal}) | target_face: [x,y,z] on a parallel planar face; body?

Align

Command group: SOLID › MODIFY

Read the solid features guide.

ParameterTypeRequiredDescription
bodies[names]Yes
from[x,y,z] | "origin" | {snap: vertex|edge_mid|face_center|circle_center, at: [x,y,z]}Yes
from_face[x,y,z]Yes
from_planeXY|XZ|YZ|construction or offset plane name or id|{origin, normal}Yes
toYesthe same
to_face[x,y,z]Yes
to_planeYesa plane as from_plane; the point or face goes onto it along its normal, and the plane may move later: the bodies follow
flipboolNo
axes[x, y, z]Nopoint-to-point only: which world axes the offset moves along; default all three, at least one true
angleexprNoa turn about the target's direction after the move
Automation details

Command ID: solid.align

Parameters:

bodies: [names]; from: [x,y,z] | "origin" | {snap: vertex|edge_mid|face_center|circle_center, at: [x,y,z]} | from_face: [x,y,z] | from_plane: XY|XZ|YZ|construction or offset plane name or id|{origin, normal}; to: (the same) | to_face: [x,y,z] | to_plane: (a plane as from_plane; the point or face goes onto it along its normal, and the plane may move later: the bodies follow); from_frame / to_frame: a frame found again whenever the design changes, so the bodies follow it, in place of that side's point, face or plane: a joint origin as joint.create takes one ({face|circle|edge|point: [x,y,z]}, a face's name, a named joint origin) or {sketch, point|curve} (a sketch point, or a circle's, arc's or ellipse's centre, z along the sketch's normal), on the bodies' own component; faces, planes and frames also turn to meet; flip?: bool; axes?: [x, y, z] (point-to-point only: which world axes the offset moves along; default all three, at least one true); angle?: expr (a turn about the target's direction after the move)

Remove

Command group: SOLID › MODIFY

Read the solid features guide.

ParameterTypeRequiredDescription
bodies[names]Yesremoved from here on in the timeline
Automation details

Command ID: solid.remove

Parameters:

bodies: [names] (removed from here on in the timeline)

Compute All

Command group: SOLID › MODIFY

Read the solid features guide.

Automation details

Command ID: timeline.compute_all

No parameter description is provided by the command registry.

Delete

Command group: SOLID › MODIFY

Default keyboard shortcut: Delete

Read the solid features guide.

ParameterTypeRequiredDescription
featureid|nameYes
features[id|name]Yes
groups[timeline folder ids]Yesa folder goes with all its features, joints and nested folders; one undo step; a sketch takes its dependent features with it
Automation details

Command ID: timeline.delete

Parameters:

feature: id|name | features: [id|name] | groups: [timeline folder ids] (a folder goes with all its features, joints and nested folders; one undo step; a sketch takes its dependent features with it) → deleted: [feature ids]; joints?: [joint ids]; groups?: [folder ids deleted]; failing?: [{feature, error}] (later features that worked and now fail)

Change Parameters

Command group: SOLID › MODIFY

Read the parameters and expressions guide.

ParameterTypeRequiredDescription
nameYes
expressionYesnumber or text
unitmm|cm|m|in|ft|deg|rad|""|textNonew parameters default to their value's unit, a length the design's units; a text parameter's expression is its text, quotes round it optional: sketch texts' words are built from it
commentNo
favoriteboolNo
new_namerenameNoreferences follow; an expression `new = value` renames to `new` and sets value, or sets `new` when the expression is just `new`
deletenameYes
changes{name: expression, …}Yesexisting parameters, checked together and rebuilt once, all or nothing
Automation details

Command ID: parameters.change

Parameters:

name, expression (number or text), unit?: mm|cm|m|in|ft|deg|rad|""|text (new parameters default to their value's unit, a length the design's units; a text parameter's expression is its text, quotes round it optional: sketch texts' words are built from it), comment?, favorite?: bool, new_name?: rename (references follow; an expression `new = value` renames to `new` and sets value, or sets `new` when the expression is just `new`); or delete: name; or changes: {name: expression, …} (existing parameters, checked together and rebuilt once, all or nothing) → changed?: {name: value} (with changes), recomputed: features rebuilt, restored: features whose earlier result for the same inputs was reused (e.g. a value set back), switched?: [{feature, name, suppressed}] (the configuration rules that switch a feature's suppression and read a parameter whose value changed run in their order, as on activation; driven parameters and restrictions wait for a configuration edit or activation)

Physical Material

Command group: SOLID › MODIFY

Read the solid features guide.

ParameterTypeRequiredDescription
bodies[names]Yes
materialnameYesSteel, Aluminum, ABS Plastic, …, or one made with material.create; `material.list`
Automation details

Command ID: material.assign

Parameters:

bodies: [names]; material: name (Steel, Aluminum, ABS Plastic, …, or one made with material.create; `material.list`)

Appearance

Command group: SOLID › MODIFY

Default keyboard shortcut: A

Read the solid features guide.

ParameterTypeRequiredDescription
bodies[names]No
faces[[x,y,z] on faces]Nobody?: which body, default the nearest
components[names or ids, or occurrence names]No
appearancelibrary name or stable starter.* IDNoappearance.library
color"#rrggbb" or [r, g, b]Yes0–255
opacityNo0–1, default 1
nameinert copied look labelNorequires color, mutually exclusive with appearance
pbr{metallic:0..1, roughness:0.045..1, dielectric_ior:1..3}Norequires color/appearance and opacity 1
Automation details

Command ID: appearance.assign

Parameters:

bodies?: [names]; faces?: [[x,y,z] on faces] (body?: which body, default the nearest); components?: [names or ids, or occurrence names]; appearance?: library name or stable starter.* ID (appearance.library) | color: "#rrggbb" or [r, g, b] (0–255), opacity? (0–1, default 1); name?: inert copied look label (requires color, mutually exclusive with appearance); pbr?: {metallic:0..1, roughness:0.045..1, dielectric_ior:1..3} (requires color/appearance and opacity 1); a starter ID or registered starter name resolves its recipe before color overrides; omitted pbr retains that response and pbr: null explicitly clears it. Unavailable glass recipes refuse through either selector. Other custom labels with an explicit color retain legacy scalar behavior. Neither appearance nor color (or color: null): clear, so the face follows its body, the body its component, the component its material

SOLID › CONSTRUCT

Offset Plane

Command group: SOLID › CONSTRUCT

Read the solid features guide.

ParameterTypeRequiredDescription
nameNo
Automation details

Command ID: construct.plane.offset

Parameters:

base: XY|XZ|YZ|plane name, or a planar face {face: [x,y,z], body?, index?, path?}; offset: expr, or through: a point ref (vertex, sketch point, construction point) the plane goes through, parallel to base; name?

Plane at Angle

Command group: SOLID › CONSTRUCT

Read the solid features guide.

ParameterTypeRequiredDescription
axisX|Y|Z|construction axis name|{origin, dir} | {edge|face: [x,y,z]}Yeswhere it is now; a straight or circular edge, or a cylinder, cone or torus face: about its axis, followed as the model changes
angleexprYes
direction_onlyboolNodefault false: turn about the axis as a line; true: project the axis direction onto base and turn about the line through pivot, or the base origin
pivotpoint refNoonly with direction_only; vertex, sketch point, construction point
nameNo
Automation details

Command ID: construct.plane.at_angle

Parameters:

base: XY|XZ|YZ|plane name, or a planar face {face: [x,y,z], body?, index?, path?}; axis: X|Y|Z|construction axis name (where it is now)|{origin, dir} | {edge|face: [x,y,z]} (a straight or circular edge, or a cylinder, cone or torus face: about its axis, followed as the model changes); angle: expr; direction_only?: bool (default false: turn about the axis as a line; true: project the axis direction onto base and turn about the line through pivot, or the base origin); pivot?: point ref (only with direction_only; vertex, sketch point, construction point); name?

Plane

Command group: SOLID › CONSTRUCT

Read the solid features guide.

ParameterTypeRequiredDescription
refs1-3 refs in any orderYes
with{the method's other parameters: offset, angle, t, name…}No
Automation details

Command ID: construct.plane

Parameters:

refs: 1-3 refs in any order; with?: {the method's other parameters: offset, angle, t, name…}. Builds with the first of the family's CONSTRUCT commands that takes the refs in its reference parameters (and every key of `with`). Returns method: the command used

Axis

Command group: SOLID › CONSTRUCT

Read the solid features guide.

ParameterTypeRequiredDescription
refs1-3 refs in any orderYes
with{the method's other parameters: offset, angle, t, name…}No
Automation details

Command ID: construct.axis

Parameters:

refs: 1-3 refs in any order; with?: {the method's other parameters: offset, angle, t, name…}. Builds with the first of the family's CONSTRUCT commands that takes the refs in its reference parameters (and every key of `with`). Returns method: the command used

Point

Command group: SOLID › CONSTRUCT

Read the solid features guide.

ParameterTypeRequiredDescription
refs1-3 refs in any orderYes
with{the method's other parameters: offset, angle, t, name…}No
Automation details

Command ID: construct.point

Parameters:

refs: 1-3 refs in any order; with?: {the method's other parameters: offset, angle, t, name…}. Builds with the first of the family's CONSTRUCT commands that takes the refs in its reference parameters (and every key of `with`). Returns method: the command used

Axis Through Cylinder/Cone/Torus

Command group: SOLID › CONSTRUCT

Read the solid features guide.

ParameterTypeRequiredDescription
facerefYesa cylinder, cone or torus face, or a circular edge
nameNo
Automation details

Command ID: construct.axis.through_cylinder

Parameters:

face: ref (a cylinder, cone or torus face, or a circular edge); name?

Axis Perpendicular To Face

Command group: SOLID › CONSTRUCT

Read the solid features guide.

ParameterTypeRequiredDescription
facerefYes
pointrefYesthe axis goes through it, square to the face
nameNo
Automation details

Command ID: construct.axis.normal_to_face

Parameters:

face: ref; point: ref (the axis goes through it, square to the face); name?

Axis Through Two Planes

Command group: SOLID › CONSTRUCT

Read the solid features guide.

ParameterTypeRequiredDescription
aplane or planar face refsYes
bplane or planar face refsYes
nameNo
Automation details

Command ID: construct.axis.two_planes

Parameters:

a, b: plane or planar face refs; name?

Axis Through Two Points

Command group: SOLID › CONSTRUCT

Read the solid features guide.

ParameterTypeRequiredDescription
apoint refsYes
bpoint refsYesvertices, sketch points, points
nameNo
Automation details

Command ID: construct.axis.two_points

Parameters:

a, b: point refs (vertices, sketch points, points); name?

Axis Through Edge

Command group: SOLID › CONSTRUCT

Read the solid features guide.

ParameterTypeRequiredDescription
edgerefYesa straight edge or sketch line
nameNo
Automation details

Command ID: construct.axis.edge

Parameters:

edge: ref (a straight edge or sketch line); name?

Point At Vertex

Command group: SOLID › CONSTRUCT

Read the solid features guide.

ParameterTypeRequiredDescription
pointrefYesvertex, sketch point, point
nameNo
Automation details

Command ID: construct.point.vertex

Parameters:

point: ref (vertex, sketch point, point); name?

Point Through Two Edges

Command group: SOLID › CONSTRUCT

Read the solid features guide.

ParameterTypeRequiredDescription
aedge or line refsYes
bedge or line refsYeswhere they meet, or come closest
nameNo
Automation details

Command ID: construct.point.two_edges

Parameters:

a, b: edge or line refs (where they meet, or come closest); name?

Point Through Three Planes

Command group: SOLID › CONSTRUCT

Read the solid features guide.

ParameterTypeRequiredDescription
aplane or planar face refsYes
bplane or planar face refsYes
cplane or planar face refsYes
nameNo
Automation details

Command ID: construct.point.three_planes

Parameters:

a, b, c: plane or planar face refs; name?

Point At Center Of Circle/Sphere/Torus

Command group: SOLID › CONSTRUCT

Read the solid features guide.

ParameterTypeRequiredDescription
ofrefYescircular edge, sketch circle or arc, sphere or torus face
nameNo
Automation details

Command ID: construct.point.center

Parameters:

of: ref (circular edge, sketch circle or arc, sphere or torus face); name?

Point At Edge And Plane

Command group: SOLID › CONSTRUCT

Read the solid features guide.

ParameterTypeRequiredDescription
edgeedge or line refYes
planeplane or planar face refYes
nameNo
Automation details

Command ID: construct.point.edge_plane

Parameters:

edge: edge or line ref; plane: plane or planar face ref; name?

Point Along Path

Command group: SOLID › CONSTRUCT

Read the solid features guide.

ParameterTypeRequiredDescription
pathedge or sketch curve refYes
nameNo
Automation details

Command ID: construct.point.along_path

Parameters:

path: edge or sketch curve ref; t: expr, 0…1 along it (default 0.5); name?

Midplane

Command group: SOLID › CONSTRUCT

Read the solid features guide.

ParameterTypeRequiredDescription
aplane or planar face refsYes
bplane or planar face refsYeshalfway between; bisecting when they meet at an angle
nameNo
Automation details

Command ID: construct.plane.midplane

Parameters:

a, b: plane or planar face refs (halfway between; bisecting when they meet at an angle); name?

Plane Through Two Edges

Command group: SOLID › CONSTRUCT

Read the solid features guide.

ParameterTypeRequiredDescription
astraight edge or line refs in one planeYes
bstraight edge or line refs in one planeYes
nameNo
Automation details

Command ID: construct.plane.two_edges

Parameters:

a, b: straight edge or line refs in one plane; name?

Plane Through Axis And Point

Command group: SOLID › CONSTRUCT

Read the solid features guide.

ParameterTypeRequiredDescription
pointoff-axis point refYes
nameNo
Automation details

Command ID: construct.plane.axis_point

Parameters:

axis: origin axis, construction axis, straight edge or sketch line ref; point: off-axis point ref; name?

Plane Through Three Points

Command group: SOLID › CONSTRUCT

Read the solid features guide.

ParameterTypeRequiredDescription
apoint refsYes
bpoint refsYes
cpoint refsYes
nameNo
Automation details

Command ID: construct.plane.three_points

Parameters:

a, b, c: point refs; name?

Tangent Plane

Command group: SOLID › CONSTRUCT

Read the solid features guide.

ParameterTypeRequiredDescription
facerefYescylinder, cone, sphere or torus
at[x,y,z] where it touchesNodefault: the face pick
referenceplane or planar face refNothe plane touches where the face turns towards it: parallel to it, on the side of `at`
angleexprNowith reference, default 0 deg: turned that far about the face's axis; not on a sphere
nameNo
Automation details

Command ID: construct.plane.tangent

Parameters:

face: ref (cylinder, cone, sphere or torus); at?: [x,y,z] where it touches (default: the face pick); reference?: plane or planar face ref (the plane touches where the face turns towards it: parallel to it, on the side of `at`); angle?: expr (with reference, default 0 deg: turned that far about the face's axis; not on a sphere); name?

Plane Along Path

Command group: SOLID › CONSTRUCT

Read the solid features guide.

ParameterTypeRequiredDescription
pathedge or sketch curve refYes
Automation details

Command ID: construct.plane.along_path

Parameters:

path: edge or sketch curve ref; t: expr, 0…1 along it (default 0.5); name? The plane is square to the path there

Perpendicular Plane

Command group: SOLID › CONSTRUCT

Read the solid features guide.

ParameterTypeRequiredDescription
planeplane or planar face refYes
Automation details

Command ID: construct.plane.perpendicular

Parameters:

plane: plane or planar face ref; line: straight edge, sketch line or axis ref; name? The plane holds the line and is square to the plane

SOLID › ASSEMBLE

New Component

Command group: SOLID › ASSEMBLE

Read the components and joints guide.

ParameterTypeRequiredDescription
namecomponent id or nameNo
parentcomponent id or nameNodefault: the active one
parent_pathexact parent placementNo
activateboolNodefault true
groundedboolNodefault: true for the design's first part, false otherwise
Automation details

Command ID: component.create

Parameters:

name?, parent?: component id or name (default: the active one); parent_path?: exact parent placement; activate?: bool (default true); grounded?: bool (default: true for the design's first part, false otherwise)

Insert Design

Command group: SOLID › ASSEMBLE

Read the components and joints guide.

ParameterTypeRequiredDescription
patha .sc designYes
Automation details

Command ID: component.insert_design

Parameters:

path: a .sc design → a new component linked to it, holding its solid bodies as placed in that design and a copy of its design properties; name? (default: the design's name); parent?: component id or name (default: the active one); translate?: [x,y,z] in mm from the destination parent's origin (default [0,0,0], also with reuse); reuse?: bool (default false): insert a free occurrence, reusing an existing link to the same file when available

Joint

Command group: SOLID › ASSEMBLE

Default keyboard shortcut: J

Read the components and joints guide.

ParameterTypeRequiredDescription
ajoint originsYes
bjoint originsYes{occurrence, face|circle|edge|point: [x,y,z] in world, or face|circle: "<face name>", plane: "XY"|"o:<ids>/origin/<item>"|"<name>", axis: "Z"|"o:<ids>/origin/<item>"|"<name>"; z? for a point, end?, x? as joint.origin} | origin name
values[angles in deg or expressions, distances]No
limits[[min, max] | null…]No
flipboolNodefault false: the origins are mated, b's z against a's as two faces meet - two plane or axis datums mate aligned, each part keeping its side of the datum, and flip mates them; true: b's z along a's, so two origins given at one point with the same z and x stay as they are; a point origin without x takes its x from z, so give x on both, or use joint.as_built, to keep a turned part where it is
offsetmm or an expressionNo
angledeg or an expressionNoan angle relation's tilt from mated; for other joints the default is none, or a half turn where that turns b less, so faces that already meet stay as they are
nameNo
Automation details

Command ID: joint.create

Parameters:

type: rigid|revolute|slider|cylindrical|pin_slot|planar|ball, or a relation: parallel|perpendicular|angle|point_on_plane|point_on_line|line_on_plane (ties only some directions or points, made where the parts are; several between two parts add up); a, b: joint origins ({occurrence, face|circle|edge|point: [x,y,z] in world, or face|circle: "<face name>", plane: "XY"|"o:<ids>/origin/<item>"|"<name>", axis: "Z"|"o:<ids>/origin/<item>"|"<name>"; z? for a point, end?, x? as joint.origin} | origin name); values?: [angles in deg or expressions, distances]; limits?: [[min, max] | null…]; flip?: bool (default false: the origins are mated, b's z against a's as two faces meet - two plane or axis datums mate aligned, each part keeping its side of the datum, and flip mates them; true: b's z along a's, so two origins given at one point with the same z and x stay as they are; a point origin without x takes its x from z, so give x on both, or use joint.as_built, to keep a turned part where it is); offset?: mm or an expression; angle?: deg or an expression (an angle relation's tilt from mated; for other joints the default is none, or a half turn where that turns b less, so faces that already meet stay as they are); name?; quiet?: bool (leave out dof and fixed). An expression (text) is kept with the joint and worked out again whenever the design changes, so the joint follows the parameters it names; a joint whose expression no longer works out (a parameter deleted) is left out of the solve with the reason in its errors

As-Built Joint

Command group: SOLID › ASSEMBLE

Read the components and joints guide.

ParameterTypeRequiredDescription
typeYes
aoccurrenceYes
boccurrenceYesstay where they are
atjoint originNodefault: b's origin
nameNo
limits[[min, max] | null…]No
quietboolNo
Automation details

Command ID: joint.as_built

Parameters:

type, a: occurrence, b: occurrence (stay where they are); at?: joint origin (default: b's origin) — the joint's frame; one on a or b is kept on its face or hole there, so the joint follows it as the design changes; name?; limits?: [[min, max] | null…]; quiet?: bool

Rigid Group

Command group: SOLID › ASSEMBLE

Read the components and joints guide.

ParameterTypeRequiredDescription
occurrences[ids or names]Yesmove together, as they are
Automation details

Command ID: joint.rigid_group

Parameters:

occurrences: [ids or names] (move together, as they are)

Drive Joints

Command group: SOLID › ASSEMBLE

Read the components and joints guide.

ParameterTypeRequiredDescription
jointid|nameYes
value[angles in deg or expressions, distances]Yesvalues not given keep theirs; a value driven drops the expression joint.create gave it; clamped to the limits; the other joints of a closed loop follow, and the motion stops where the loop can no longer close → stopped_by_loop
values[angles in deg or expressions, distances]Yesvalues not given keep theirs; a value driven drops the expression joint.create gave it; clamped to the limits; the other joints of a closed loop follow, and the motion stops where the loop can no longer close → stopped_by_loop
quietboolNoleave out dof and fixed
Automation details

Command ID: joint.drive

Parameters:

joint: id|name; value | values: [angles in deg or expressions, distances] (values not given keep theirs; a value driven drops the expression joint.create gave it; clamped to the limits; the other joints of a closed loop follow, and the motion stops where the loop can no longer close → stopped_by_loop); quiet?: bool (leave out dof and fixed) → values: [{name, value, unit: deg|mm, limits}] as joint.list gives them; requested: the values asked for, in the same form; held: [{name, requested, value, unit, why}] for each driven value that ended up elsewhere (a motion link sets it, as a screw's travel follows its turn; a limit; contact; a closed loop); moved: whether any occurrence moved; reason: why the joint can move nothing (suppressed: nothing is stored; its origins lost; its chain joined to nothing grounded → unconnected: the occurrence named; or, when nothing moved, the first held value's why); index?: zero-based coordinate to drive with value (the other coordinates follow); continuous?: bool (drag frame, suppress UI echo); stop_at_budget?: bool (default false: a contact check past its sample or work budget fails the drive; true: the drive stops at the last placement checked clear → stopped_by_budget {budget: samples|work|intersections, fraction})

Command group: SOLID › ASSEMBLE

Read the components and joints guide.

ParameterTypeRequiredDescription
ajointsYes
bjointsYes
ratioYesb = ratio · a + offset
offsetin b's unitsNodeg for an angle, mm for a distance, or an expression
iavalue indicesNo
ibvalue indicesNodefault 0
Automation details

Command ID: joint.motion_link

Parameters:

a, b: joints; ratio (b = ratio · a + offset): b's units per a's (mm per deg for an angle driving a distance); offset?: in b's units (deg for an angle, mm for a distance, or an expression); ia?, ib?: value indices (default 0)

Gear

Command group: SOLID › ASSEMBLE

Read the components and joints guide.

ParameterTypeRequiredDescription
internalboolNoone is a ring gear: both turn the same way; by default b turns the other way
reverseboolNo
linkindexNoreplace that motion link
Automation details

Command ID: joint.gear

Parameters:

a, b: joints that turn (revolute), b driven by a; ra, rb: the gears' pitch radii (or tooth counts): mm, an expression, or {circle: [x,y,z], occurrence?}: the round face there, its radius (read again whenever the design changes, so the ratio follows edits); internal?: bool (one is a ring gear: both turn the same way; by default b turns the other way); reverse?: bool; link?: index (replace that motion link) → link, ratio (b's degrees per a's); b keeps where it is, also when the link replaces another (at the current pose)

Rack and Pinion

Command group: SOLID › ASSEMBLE

Read the components and joints guide.

ParameterTypeRequiredDescription
radiusthe pinion's pitch radiusYesmm, an expression, or {circle: [x,y,z], occurrence?}
reverseboolNo
linkindexNoreplace that motion link
Automation details

Command ID: joint.rack_pinion

Parameters:

a, b: a joint that turns (revolute: the pinion) and one that slides (slider: the rack), in either order, b driven by a; radius: the pinion's pitch radius (mm, an expression, or {circle: [x,y,z], occurrence?}): the rack travels radius × the turn in radians; reverse?: bool; link?: index (replace that motion link) → link, ratio (mm per degree, or degrees per mm); b keeps where it is, also when the link replaces another (at the current pose)

Screw

Command group: SOLID › ASSEMBLE

Read the components and joints guide.

ParameterTypeRequiredDescription
pitchtravel per turnYesmm or an expression
reverseboolNoa left-hand thread
linkindexNoreplace that motion link
Automation details

Command ID: joint.screw

Parameters:

joint: a cylindrical joint (its turn drives its travel) | a, b: a joint that turns and one that slides, b driven by a; pitch: travel per turn (mm or an expression); reverse?: bool (a left-hand thread); link?: index (replace that motion link) → link, ratio (mm per degree, or degrees per mm); b keeps where it is, also when the link replaces another (at the current pose)

Cam

Command group: SOLID › ASSEMBLE

Read the components and joints guide.

ParameterTypeRequiredDescription
athe cam's jointYesrevolute
followerknife|roller|flatNodefault knife; flat: a face square to a slider's line
radiusthe roller's radiusNomm, an expression or {circle: [x,y,z], occurrence?}: the roller's round face, which is its tip too
flipboolNoride the cam from the other side than the one the follower is on now
linkindexNoreplace that motion link
Automation details

Command ID: joint.cam

Parameters:

a: the cam's joint (revolute); b: the follower's joint, a slider (a translating follower) or a revolute (an oscillating one),          b driven by a; profile: {sketch: id|name, index?: the profile (default: the largest)} | {edges: [[x,y,z]…] (points on edges of          the cam's bodies forming one closed loop, world), occurrence?}: the cam's outline, in a plane square to its axis, read again          whenever the design changes; follower?: knife|roller|flat (default knife; flat: a face square to a slider's line); radius?: the          roller's radius (mm, an expression or {circle: [x,y,z], occurrence?}: the roller's round face, which is its tip too); tip?: a          joint origin where the follower touches: its knife's point or its roller's centre, such as {circle: [x,y,z]} on the roller          (needed for an oscillating follower; a translating one's line runs through it, by default along its slider's axis); flip?: bool (ride the cam from the other side          than the one the follower is on now); link?: index (replace that motion link) → link, lift: {min, max, rise, unit} (b's travel          over a turn of the cam), reverse (the side it rides from), follower_offset (mm: how far a translating follower's line passes          from the cam's axis); b keeps where it is, also when the link replaces another (at the current pose)

Constrain

Command group: SOLID › ASSEMBLE

Read the solid features guide.

ParameterTypeRequiredDescription
typemate|flush|distance|concentric|insert|parallel|perpendicular|angle|tangent|point_on_axisYes
offsetthe gapNomm or an expression; needed for distance
angledeg or an expressionNoneeded for angle
flipturn b overNo
nameNodefault: the rule and a number, Mate1
Automation details

Command ID: assembly.constraint.create

Parameters:

type: mate|flush|distance|concentric|insert|parallel|perpendicular|angle|tangent|point_on_axis; a, b: references, as joint.create's origins (a face or a datum plane for a plane, a circle or an axis for an axis, a point; b moves); offset?: the gap (mm or an expression; needed for distance); angle?: deg or an expression (needed for angle); flip?: turn b over; name? (default: the rule and a number, Mate1); quiet?. Kept as a joint of the kind the rule needs (mate and flush planar, distance planar or point on plane, concentric cylindrical, insert revolute, tangent line on plane, point on axis point on line), made where the parts are, so b moves no further than the rule needs → joint, name, dof, conflicts, fixed as joint.create

Enable Contact Sets

Command group: SOLID › ASSEMBLE

Read the solid features guide.

ParameterTypeRequiredDescription
enabledboolNodefault true
Automation details

Command ID: contact.enable_sets

Parameters:

enabled?: bool (default true) — driving joints and motion studies stop where bodies in one contact set would pass through each other

Enable All Contact

Command group: SOLID › ASSEMBLE

Read the solid features guide.

Automation details

Command ID: contact.enable_all

Parameters:

every occurrence takes part in contact (one set of all)

Disable Contact

Command group: SOLID › ASSEMBLE

Read the solid features guide.

Automation details

Command ID: contact.disable_all

Parameters:

contact off

New Contact Set

Command group: SOLID › ASSEMBLE

Read the solid features guide.

ParameterTypeRequiredDescription
occurrences[ids or names]Yestwo or more
nameNo
Automation details

Command ID: contact.create

Parameters:

occurrences: [ids or names] (two or more); name?

Motion Study

Command group: SOLID › ASSEMBLE

Read the components and joints guide.

ParameterTypeRequiredDescription
nameNo
studyNoedit an existing one
stepsNotimeline length, default 100
keys[{joint, index?, points: [[step, value], …]}]Yesvalues: degrees for angles, mm for distances, or expressions
Automation details

Command ID: motion.study

Parameters:

name?; study? (edit an existing one); steps? (timeline length, default 100); keys: [{joint, index?, points: [[step, value], …]}] (values: degrees for angles, mm for distances, or expressions)

Motion Trace

Command group: SOLID › ASSEMBLE

Read the components and joints guide.

ParameterTypeRequiredDescription
studyid|nameYes
jointid|name with from and toYesdegrees for angles, mm for distances, or expressions
Automation details

Command ID: motion.trace

Parameters:

occurrence: id|name, or an occurrence path [ids] (an instance inside sub-assemblies); point: [x,y,z] world point on it, where it is placed now; study: id|name, or joint: id|name with from and to (degrees for angles, mm for distances, or expressions) and index? (which value, default 0); samples? (2…500, default the study's steps + 1, or 100) - the point's path as the study plays or the joint is driven, closed loops following → points: [[x,y,z]…] world positions, samples (taken), stopped_at (the sample where the motion stopped short; the trace ends there), stopped_by_loop, stopped_by_contact (the design is not changed)

Exploded View

Command group: SOLID › ASSEMBLE

Read the solid features guide.

ParameterTypeRequiredDescription
nameNo
scaleNoautomatic: each occurrence pushed away from the assembly's centre by scale × its offset, default 1
moves[{occurrence, translate: [x,y,z]}]Noinstead of automatic
Automation details

Command ID: explode.create

Parameters:

name?; scale? (automatic: each occurrence pushed away from the assembly's centre by scale × its offset, default 1); moves?: [{occurrence, translate: [x,y,z]}] (instead of automatic)

SOLID › POSITION

Capture Position

Command group: SOLID › POSITION

Read the components and joints guide.

Automation details

Command ID: component.capture_position

Parameters:

keeps pending occurrence moves and saves every moving joint's values → captured, joints

Revert Position

Command group: SOLID › POSITION

Read the components and joints guide.

ParameterTypeRequiredDescription
occurrences[ids]Nodefault all
Automation details

Command ID: component.revert_position

Parameters:

occurrences?: [ids] (default all); drops selected pending occurrence moves; with no occurrences the joints also go back to the values Capture Position saved (clamped to limits) → reverted, joints

SOLID › INSPECT

Interference

Command group: SOLID › INSPECT

Read the solid features guide.

ParameterTypeRequiredDescription
bodies[names]Nodefault: all, in world placement; a name that is no body is refused
budget_ssecondsNodefault 240, at most 86400: pairs not finished by then are listed in unchecked as "not checked: the time budget … ran out" and the call returns
exclude_pairs[[a, b]]Nopairs left out, such as a pin in its hole: each side a body's world name or an occurrence id|name, matching every body under it; pairs_excluded counts them
Automation details

Command ID: inspect.interference

Parameters:

bodies?: [names] (default: all, in world placement; a name that is no body is refused); budget_s?: seconds (default 240, at most 86400: pairs not finished by then are listed in unchecked as "not checked: the time budget … ran out" and the call returns); exclude_pairs?: [[a, b]] (pairs left out, such as a pin in its hole: each side a body's world name or an occurrence id|name, matching every body under it; pairs_excluded counts them) → interferences: pairs that overlap with the overlap volume, area, bounding box and centre, and a_part, b_part: {component, path: [occurrence names], path_ids} where each body sits (also on unchecked pairs); unchecked: [{a, b, reason}], the pairs that could not be checked, with the reason, and each mesh body (STL, 3MF or OBJ import: not intersected) as {a: name, b: null, reason: "mesh body: not checked"}, so count: 0 is not read as no overlap (checked counts the solid bodies). Bodies are pruned by bounding box first, then by their display meshes (pairs whose meshes stay apart by more than their chord tolerances, neither inside the other, are clear without a boolean), so any number of bodies is fine; the pairs left are intersected exactly, in parallel on desktop, the slowest first; at most 20000 pairs whose boxes overlap are intersected per call (truncated: true and skipped_pairs: n say when more were left out). Also: pairs_candidate (boxes overlap), pairs_cleared (by the meshes), pairs_intersected, pairs_not_checked, pairs_excluded, budget_exceeded, threads (worker threads; 0: on the calling thread), boolean_ms and tessellate_ms (kernel time summed over the pairs), slowest: [{a, b, ms, boolean_ms, tessellate_ms}] and ms

Bill of Materials

Command group: SOLID › INSPECT

Read the solid features guide.

ParameterTypeRequiredDescription
componentid or nameNodefault the design
levelsflat| topNodefault: every part at any depth, counted through its subassemblies; the component's direct children, subassemblies as one row each
group_by["part_number"]| ["definition"]Nodefault; components without a part number get a row per definition
group_childrentrueNoa component with a part number is one row, the parts placed inside it listed under it as children: [{item, part_number, description, revision, each, count, definition}], not as rows of their own
pathwrite a .csv or .json fileNochildren as items 1.1, 1.2…
Automation details

Command ID: assembly.bom

Parameters:

component?: id or name (default the design); levels?: flat (default: every part at any depth, counted through its subassemblies) | top (the component's direct children, subassemblies as one row each); group_by?: ["part_number"] (default; components without a part number get a row per definition) | ["definition"]; group_children?: true (a component with a part number is one row, the parts placed inside it listed under it as children: [{item, part_number, description, revision, each, count, definition}], not as rows of their own); path?: write a .csv or .json file (children as items 1.1, 1.2…); format?: csv|json (default from the path). Each component's bom_type counts: phantom has no row (what it holds counts here), reference counts nowhere, purchased and inseparable are one row (in flat, an inseparable one's purchased parts are listed too, under it with group_children and a part number) → rows: [{item, part_number, description, revision, bom_type, count, definition, components, properties, children?}] sorted by part number, total_occurrences (children included)

Component Colors

Command group: SOLID › INSPECT

Read the solid features guide.

ParameterTypeRequiredDescription
enabledboolNoomitted: toggle
Automation details

Command ID: inspect.colored_components

Parameters:

enabled?: bool (omitted: toggle); temporary component colours, without changing appearances

Measure

Command group: SOLID › INSPECT

Default keyboard shortcut: I

Read the solid features guide.

ParameterTypeRequiredDescription
bodies[names]Nodefault all
names_onlyboolNoeach world body's name and its definition's, nothing measured
Automation details

Command ID: inspect.measure

Parameters:

bodies?: [names] (default all); names_only?: bool (each world body's name and its definition's, nothing measured) → closed per body; volume_mm3, center_of_mass, density_g_cm3 and mass_g only for closed, consistently oriented bodies; area, bbox and counts for all; total.open_bodies_skipped counts bodies excluded from volume/mass totals; or items: [1-2 selections] (face/edge: type, body, and point on it or index from model.faces/model.edges, or both) → length/area/volume; a face's perimeter_mm, with outer_perimeter_mm and inner_perimeter_mm on a flat face with holes; diameter, radius, circumference or arc_length/angle_deg of circles, arcs and cylinders; slot sizes; min (distance_mm, from, to), max, center_distance (centre to centre, or from an axis) and angle

Section Analysis

Command group: SOLID › INSPECT

Read the solid features guide.

ParameterTypeRequiredDescription
planeXY|XZ|YZ | construction plane name or id | {origin, normal}Yes
offsetexpr along the plane's normalNoXY: +Z, XZ: +Y, YZ: +X
flipboolNo
hatchboolNohatch the cut faces, in each body's colour; default true
Automation details

Command ID: inspect.section

Parameters:

plane: XY|XZ|YZ | construction plane name or id | {origin, normal}; offset?: expr along the plane's normal (XY: +Z, XZ: +Y, YZ: +X); at?: expr, the cut's world coordinate on the named plane's axis (XY: Z, XZ: Y, YZ: X), instead of offset; flip?: bool; hatch?: bool (hatch the cut faces, in each body's colour; default true); or clear: true. Returns the cut's origin and normal, and for a named plane its axis and at

SOLID › CONFIGURE

Configure

Command group: SOLID › CONFIGURE

Read the parameters and expressions guide.

ParameterTypeRequiredDescription
params[parameter names]No
features[feature names]No
materials[body names]No
appearances[body names]Nocolumns to add; with no rows yet, a "Default" row takes the current values
Automation details

Command ID: config.configure

Parameters:

params?: [parameter names]; features?: [feature names]; materials?: [body names]; appearances?: [body names] (columns to add; with no rows yet, a "Default" row takes the current values)

Display Configuration Table

Command group: SOLID › CONFIGURE

Read the parameters and expressions guide.

Automation details

Command ID: config.table

Parameters:

→ columns (param:<name> or suppress:<feature>), rows (each with its fixed id), the active row's name and whether the design still matches it

SKETCH › FINISH SKETCH

Finish Sketch

Command group: SKETCH › FINISH SKETCH

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
restore_markerboolNodefault true
Automation details

Command ID: sketch.finish

Parameters:

restore_marker?: bool (default true); false transfers the temporary marker directly to another editor without rebuilding the intervening timeline

Cancel Sketch

Command group: SKETCH › FINISH SKETCH

Read the sketching and constraints guide.

Automation details

Command ID: sketch.cancel

Parameters:

Leaves the sketch without keeping what changed since it was created or opened for editing: the design goes back to how it was then (a new sketch is removed), as one step that Undo brings back. Refused when the undo history no longer leads back to that point (use Undo instead). restore_marker?: bool (default true); false transfers the temporary marker to another editor

Finish with AutoConstrain

Command group: SKETCH › FINISH SKETCH

Read the sketching and constraints guide.

Automation details

Command ID: sketch.finish_auto_constrain

Parameters:

like AutoConstrain, then Finish Sketch

SKETCH › CREATE

Line

Command group: SKETCH › CREATE

Default keyboard shortcut: L

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
points[[x,y] | "l1.end" | {at, ref}…]Yesx and y each a number or an expression over the design's parameters, "W / 2": such a point is fixed where it comes to and follows the parameters, while text naming no parameter, such as "5", is just that number, in the design's length unit (a JSON number is mm), and does not itself fix the point; likewise wherever a point argument also takes a point id
closedboolNo
constructionboolNo
ids[..]No
inferboolNoadd horizontal/vertical
connectboolNodefault true: a new point landing exactly on an existing point, the origin too, is made coincident with it
Automation details

Command ID: sketch.line

Parameters:

points: [[x,y] | "l1.end" | {at, ref}…] (x and y each a number or an expression over the design's parameters, "W / 2": such a point is fixed where it comes to and follows the parameters, while text naming no parameter, such as "5", is just that number, in the design's length unit (a JSON number is mm), and does not itself fix the point; likewise wherever a point argument also takes a point id), closed?: bool, construction?: bool, ids?: [..], infer?: bool (add horizontal/vertical); connect?: bool (default true: a new point landing exactly on an existing point, the origin too, is made coincident with it). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

2-Point Rectangle

Command group: SKETCH › CREATE

Default keyboard shortcut: R

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
p0opposite corners [x,y]Yes
p1opposite corners [x,y]Yesa coordinate may be an expression over the design's parameters: the corner is fixed and follows them; text naming no parameter, such as "5", is just that number, in the design's length unit (a JSON number is mm), and does not itself fix the corner
connectboolNodefault true: a new point landing exactly on an existing point, the origin too, is made coincident with it
Automation details

Command ID: sketch.rectangle.two_point

Parameters:

p0, p1: opposite corners [x,y] (a coordinate may be an expression over the design's parameters: the corner is fixed and follows them; text naming no parameter, such as "5", is just that number, in the design's length unit (a JSON number is mm), and does not itself fix the corner); construction?. Returns sides {bottom, right, top, left} (line ids) and corners {bottom_left, bottom_right, top_right, top_left} (point ids); connect?: bool (default true: a new point landing exactly on an existing point, the origin too, is made coincident with it). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

3-Point Rectangle

Command group: SKETCH › CREATE

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
p0first edgeYes
p1first edgeYes
p2point on the opposite edgeYes
constructionboolNo
connectboolNodefault true: a new point landing exactly on an existing point, the origin too, is made coincident with it
Automation details

Command ID: sketch.rectangle.three_point

Parameters:

p0, p1: first edge; p2: point on the opposite edge; construction?: bool; connect?: bool (default true: a new point landing exactly on an existing point, the origin too, is made coincident with it). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Rounded Rectangle

Command group: SKETCH › CREATE

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
p0opposite corners [x,y]Yes
p1opposite corners [x,y]Yesa coordinate may be an expression over the design's parameters: the corner is fixed and follows them, as in `sketch.rectangle.two_point`; text naming no parameter, such as "5", is just that number, in the design's length unit (a JSON number is mm), and does not itself fix the corner
radiusexprYesthe corner radius, less than half the shorter side
connectboolNodefault true: a new point landing exactly on an existing point, the origin too, is made coincident with it
Automation details

Command ID: sketch.rectangle.rounded

Parameters:

p0, p1: opposite corners [x,y] (a coordinate may be an expression over the design's parameters: the corner is fixed and follows them, as in `sketch.rectangle.two_point`; text naming no parameter, such as "5", is just that number, in the design's length unit (a JSON number is mm), and does not itself fix the corner); radius: expr (the corner radius, less than half the shorter side); construction?. A 2-point rectangle whose four corners are filleted alike: one radius dimension (`param`), the other arcs equal to it. Returns sides {bottom, right, top, left} (line ids) and arcs (the corner arcs: bottom-left, bottom-right, top-right, top-left), diagonal (construction, between the bottom-left and top-right arc centres) and centre (the point held at its midpoint); connect?: bool (default true: a new point landing exactly on an existing point, the origin too, is made coincident with it). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Center Rectangle

Command group: SKETCH › CREATE

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
center[x,y]Yes
corner[x,y]Yesa coordinate may be an expression over the design's parameters: the point is fixed and follows them; text naming no parameter, such as "5", is just that number, in the design's length unit (a JSON number is mm), and does not itself fix the point
constructionboolNo
connectboolNodefault true: a new point landing exactly on an existing point, the origin too, is made coincident with it
Automation details

Command ID: sketch.rectangle.center

Parameters:

center, corner: [x,y] (a coordinate may be an expression over the design's parameters: the point is fixed and follows them; text naming no parameter, such as "5", is just that number, in the design's length unit (a JSON number is mm), and does not itself fix the point); construction?: bool. Returns sides {bottom, right, top, left} (line ids) and corners {bottom_left, bottom_right, top_right, top_left} (point ids); connect?: bool (default true: a new point landing exactly on an existing point, the origin too, is made coincident with it). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Center Diameter Circle

Command group: SKETCH › CREATE

Default keyboard shortcut: C

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
center[x,y] or point refYesa coordinate may be an expression over the design's parameters: the centre is fixed and follows them; text naming no parameter, such as "5", is just that number, in the design's length unit (a JSON number is mm), and does not itself fix the centre
radiusnumber or expressionYesan expression naming a parameter is a dimension driven by it; with `through` a point ref, that and the point both fix the radius
diameternumber or expressionYesan expression naming a parameter is a dimension driven by it; with `through` a point ref, that and the point both fix the radius
throughpoint refNoa point the circle passes through, kept on it: where the tool's second click snapped
constructionboolNo
connectboolNodefault true: a new point landing exactly on an existing point, the origin too, is made coincident with it
Automation details

Command ID: sketch.circle.center

Parameters:

center: [x,y] or point ref (a coordinate may be an expression over the design's parameters: the centre is fixed and follows them; text naming no parameter, such as "5", is just that number, in the design's length unit (a JSON number is mm), and does not itself fix the centre), radius | diameter: number or expression (an expression naming a parameter is a dimension driven by it; with `through` a point ref, that and the point both fix the radius); through?: point ref (a point the circle passes through, kept on it: where the tool's second click snapped); construction?: bool; connect?: bool (default true: a new point landing exactly on an existing point, the origin too, is made coincident with it). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

2-Point Circle

Command group: SKETCH › CREATE

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
p0diameter endsYes
p1diameter endsYes
constructionboolNo
connectboolNodefault true: a new point landing exactly on an existing point, the origin too, is made coincident with it
Automation details

Command ID: sketch.circle.two_point

Parameters:

p0, p1: diameter ends; construction?: bool; connect?: bool (default true: a new point landing exactly on an existing point, the origin too, is made coincident with it). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

3-Point Circle

Command group: SKETCH › CREATE

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
p0[x,y] or point refYes
p1[x,y] or point refYes
p2[x,y] or point refYesa referenced point stays on the circle
constructionboolNo
connectboolNodefault true: a new point landing exactly on an existing point, the origin too, is made coincident with it
Automation details

Command ID: sketch.circle.three_point

Parameters:

p0, p1, p2: [x,y] or point ref (a referenced point stays on the circle); construction?: bool; connect?: bool (default true: a new point landing exactly on an existing point, the origin too, is made coincident with it). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

3-Point Arc

Command group: SKETCH › CREATE

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
start[x,y] or point refYes
end[x,y] or point refYes
through[x,y] or point refYesa referenced through point stays on the arc
constructionboolNo
connectboolNodefault true: a new point landing exactly on an existing point, the origin too, is made coincident with it
Automation details

Command ID: sketch.arc.three_point

Parameters:

start, end, through: [x,y] or point ref (a referenced through point stays on the arc); construction?: bool; connect?: bool (default true: a new point landing exactly on an existing point, the origin too, is made coincident with it). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Center Point Arc

Command group: SKETCH › CREATE

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
centerYes
startYes
endYescounter-clockwise from start; clockwise?: bool for clockwise
centerYes
startYes
sweepsigned degreesYes
constructionboolNo
connectboolNodefault true: a new point landing exactly on an existing point, the origin too, is made coincident with it
Automation details

Command ID: sketch.arc.center_point

Parameters:

center, start, end (counter-clockwise from start; clockwise?: bool for clockwise) | center, start, sweep: signed degrees; construction?: bool; connect?: bool (default true: a new point landing exactly on an existing point, the origin too, is made coincident with it). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Inscribed Polygon

Command group: SKETCH › CREATE

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
centerYes
radiusYesto vertices
sidesYes
angledegNo
connectboolNodefault true: a new point landing exactly on an existing point, the origin too, is made coincident with it
Automation details

Command ID: sketch.polygon.inscribed

Parameters:

center, radius (to vertices), sides, angle?: deg; connect?: bool (default true: a new point landing exactly on an existing point, the origin too, is made coincident with it). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Circumscribed Polygon

Command group: SKETCH › CREATE

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
centerYes
radiusYesto edge midpoints
sidesYes
angledegNo
connectboolNodefault true: a new point landing exactly on an existing point, the origin too, is made coincident with it
Automation details

Command ID: sketch.polygon.circumscribed

Parameters:

center, radius (to edge midpoints), sides, angle?: deg; connect?: bool (default true: a new point landing exactly on an existing point, the origin too, is made coincident with it). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Edge Polygon

Command group: SKETCH › CREATE

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
p0Yes
p1one edgeYes
sidesYes
connectboolNodefault true: a new point landing exactly on an existing point, the origin too, is made coincident with it
Automation details

Command ID: sketch.polygon.edge

Parameters:

p0, p1: one edge, sides; connect?: bool (default true: a new point landing exactly on an existing point, the origin too, is made coincident with it). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Center to Center Slot

Command group: SKETCH › CREATE

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
p0Yes
p1arc centresYes
widthYes
connectboolNodefault true: a new point landing exactly on an existing point, the origin too, is made coincident with it
Automation details

Command ID: sketch.slot.center_to_center

Parameters:

p0, p1: arc centres, width; returns centre_line (construction, between the arc centres) and middle (the point held at its midpoint); connect?: bool (default true: a new point landing exactly on an existing point, the origin too, is made coincident with it). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Overall Slot

Command group: SKETCH › CREATE

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
p0Yes
p1overall endsYes
widthYes
connectboolNodefault true: a new point landing exactly on an existing point, the origin too, is made coincident with it
Automation details

Command ID: sketch.slot.overall

Parameters:

p0, p1: overall ends, width; returns centre_line (construction, between the arc centres) and middle (the point held at its midpoint); connect?: bool (default true: a new point landing exactly on an existing point, the origin too, is made coincident with it). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Point

Command group: SKETCH › CREATE

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
idNo
connectboolNodefault true: a new point landing exactly on an existing point, the origin too, is made coincident with it
Automation details

Command ID: sketch.point

Parameters:

point: [x,y] (each a number or an expression over the design's parameters: the point is then fixed where it comes to and follows them; text naming no parameter, such as "5", is just that number, in the design's length unit (a JSON number is mm), and does not itself fix the point) or a snap: "mid:<line>" (held at the line's midpoint), "on:<curve>:x,y" (on the curve near x,y), "x:<a>+<b>:x,y" (at the crossing of two curves near x,y), {vertex: [x,y,z]} or {on_edge: [x,y,z]} of the model; id?; connect?: bool (default true: a new point landing exactly on an existing point, the origin too, is made coincident with it). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Sketch Dimension

Command group: SKETCH › CREATE

Default keyboard shortcut: D

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
half_axesboolNodefault true; false: with auto, the minor radius wherever text_at is, as before half-axes, which the Dimension tool sends with Draw ellipses with their axes off
Automation details

Command ID: sketch.dimension

Parameters:

entities: [refs] (a line midpoint is "mid:line_id"; a model edge or vertex, {edge: [x,y,z], body?} or {vertex: [x,y,z], body?}, is projected into the sketch first and measured there: a point against the projected edge measures the distance to it), type?: auto|distance|horizontal|distance_x|vertical|distance_y|length|radius|diameter|major|minor|angle, value?: number or expression (default: current; a bare number is in the design's units, e.g. 2 means 2 in in an inch design; give a unit such as "50 mm" to override), or `name = expression` (e.g. "width = 40"; also `#name = expression`), which adds the user parameter `name` with that expression and dimensions with `name` (refused when the name is taken or reserved; the reply's `defined` names it); text_at?: [x, y]. Two points or a line off the axes: with auto, text_at across from them (between the perpendiculars through them, or a tenth of their distance past) is the distance between them; pulled further past an end, up or down from it horizontal, sideways vertical. Two midpoints: placing text above or below their bounding box measures horizontally, beside it vertically, anywhere else (inside it or past its corners) the distance between them. Two lines: the angle between them (acute, or 180° − it when the value is nearer that) unless text_at picks a sector. A line and a circle or arc: the distance from the line to its edge (the gap, or how far the edge reaches past a line that cuts it; 0 is tangent); dimension the centre point for the distance to the centre. A point and a circle or arc: the distance from the point to its edge (or how far within it a point inside lies). Two circles or arcs: the gap between their edges (or how far they overlap when they cross, or the narrowest gap between them when one lies inside the other), or the centre-to-centre distance when they are the two caps of a slot. An ellipse: a half-axis, the distance from the centre to an axis end (major or minor; with auto, text_at pulled across the major axis gives the major half-axis, pulled out past an end of it the minor one, the minor without text_at; previous_axis?: major|minor, the half-axis an earlier placement chose, kept while text_at is near the boundary between them; the reply's axis names the one chosen; with auto, when a driving dimension already holds one half-axis (or its whole axis), the other one, wherever text_at is; without its major axis line the major half-axis goes to the ellipse's own major end, without its minor axis line the minor half-axis is the minor radius); any other type, radius among them, the minor radius; an ellipse's axis line with auto: its length, the whole axis, wherever text_at is; half_axes?: bool (default true; false: with auto, the minor radius wherever text_at is, as before half-axes, which the Dimension tool sends with Draw ellipses with their axes off). An arc and one of its end points: the arc's length (type arc_length does it from the arc alone). A dimension the sketch already determines is refused (naming what determines it); if_redundant?: "driven" places it as a driven (reference) dimension instead. scale_sketch?: bool (the dimension tool sends the Sketch preference, on by default): when this is the sketch's first length dimension and nothing in the sketch fixes a size (no other driving length dimension, nothing fixed or projected), the whole sketch scales to the value instead of distorting: about the origin when the drawing is tied to it (joined to the origin, or constrained to a sketch axis), otherwise about the middle of the drawing, so it stays where it was drawn (#11043); scale_about_origin?: bool (default false; the dimension tool sends the Sketch preference) scales about the origin always, as before #11043. Returns measured in unit (mm or deg), and scaled: the factor when the sketch was scaled

Normal/Construction

Command group: SKETCH › CREATE

Default keyboard shortcut: X

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
curves[ids]Yes
valueboolNodefault: construction unless all of them already are
Automation details

Command ID: sketch.construction

Parameters:

curves: [ids], value?: bool (default: construction unless all of them already are)

Midpoint Line

Command group: SKETCH › CREATE

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
end[x,y]Yes
Automation details

Command ID: sketch.line.midpoint

Parameters:

mid: [x,y], point ref or snap ("mid:<line>", "on:<curve>:x,y", model vertex/edge); end: [x,y]; construction?. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Tangent Arc

Command group: SKETCH › CREATE

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
startend point of a line or arcYes"l1.end"
end[x,y] or an existing point's idYes"l2.start": the arc ends on that point
curvethe curve to be tangent toNodefault: the one ending at start
Automation details

Command ID: sketch.arc.tangent

Parameters:

start: end point of a line or arc ("l1.end"), end: [x,y] or an existing point's id ("l2.start": the arc ends on that point); curve?: the curve to be tangent to (default: the one ending at start). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

2-Tangent Circle

Command group: SKETCH › CREATE

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
curves[two lines/circles/arcs]Yes
radiusexprYes
diameterexprYes
near[x,y]Yeswhich of the solutions
Automation details

Command ID: sketch.circle.two_tangent

Parameters:

curves: [two lines/circles/arcs], radius | diameter: expr, near: [x,y] (which of the solutions). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

3-Tangent Circle

Command group: SKETCH › CREATE

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
curves[three lines/circles/arcs]Yes
near[x,y]Yeswhich of the solutions
Automation details

Command ID: sketch.circle.three_tangent

Parameters:

curves: [three lines/circles/arcs], near: [x,y] (which of the solutions). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Center Point Slot

Command group: SKETCH › CREATE

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
centerslot centreYes
endone arc centreYes
widthYes
Automation details

Command ID: sketch.slot.center_point

Parameters:

center: slot centre, end: one arc centre, width; returns centre_line and middle. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Three Point Arc Slot

Command group: SKETCH › CREATE

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
startYes
throughYes
endpoints on the centre arcYes
widthYes
Automation details

Command ID: sketch.slot.arc_three_point

Parameters:

start, through, end: points on the centre arc, width; returns centre_arc and middle. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Center Point Arc Slot

Command group: SKETCH › CREATE

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
centerYes
startcentre arc startYes
enddirection of the centre arc endYescounter-clockwise
sweepdegreesYes0 < sweep < 360
widthYes
Automation details

Command ID: sketch.slot.arc_center

Parameters:

center, start: centre arc start, end: direction of the centre arc end (counter-clockwise) or sweep: degrees (0 < sweep < 360), width; returns centre_arc and middle. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Ellipse

Command group: SKETCH › CREATE

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
axesboolNodefault true; false: the ellipse alone, as the Ellipse tool draws it with Draw ellipses with their axes off
Automation details

Command ID: sketch.ellipse

Parameters:

center, major: end of the major axis [x,y], minor: a point the ellipse passes through on the minor side [x,y] | minor_radius. Also adds its major and minor axes as construction lines through the centre (their ends: the major end and the point opposite it, and the two ends of the minor axis on the ellipse), held by midpoint, perpendicular and point-on-curve constraints, so a half-axis is the distance from the centre to an axis end and an axis line's length is a full axis; axes?: bool (default true; false: the ellipse alone, as the Ellipse tool draws it with Draw ellipses with their axes off). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Fit Point Spline

Command group: SKETCH › CREATE

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
points[[x,y] or point refs…]Yes2…500
Automation details

Command ID: sketch.spline.fit_point

Parameters:

points: [[x,y] or point refs…] (2…500): the spline passes through them; each fit point has an editable tangent handle whose direction and length control curvature, periodic?: boolean (default false); periodic cubic fit closes smoothly through at least 3 distinct samples. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Control Point Spline

Command group: SKETCH › CREATE

Read the sketching and constraints guide.

Automation details

Command ID: sketch.spline.control_point

Parameters:

points: control points (2…500), cubic. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Control Point Spline (Degree 5)

Command group: SKETCH › CREATE

Read the sketching and constraints guide.

Automation details

Command ID: sketch.spline.control_point_5

Parameters:

points: control points (2…500), degree 5. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Conic Curve

Command group: SKETCH › CREATE

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
start[x,y] or point refsYes
end[x,y] or point refsYes
apex[x,y] or point refsYes
rho0…1Nodefault 0.5: parabola
Automation details

Command ID: sketch.conic

Parameters:

start, end, apex: [x,y] or point refs; rho?: 0…1 (default 0.5: parabola). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Centerline

Command group: SKETCH › CREATE

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
curves[line ids]Yes
valueboolNodefault toggles
Automation details

Command ID: sketch.centerline

Parameters:

curves: [line ids], value?: bool (default toggles): centerline line type (not in profiles; an axis)

Text

Command group: SKETCH › CREATE

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
atbaseline start [x,y]Yes
heightcapital height mmNodefault 5
angledegNo
Automation details

Command ID: sketch.text

Parameters:

text: string (\n breaks lines), or text_expression: the words built from text parameters (parameters.add with unit text), number parameters and quoted texts joined by + ("Label + '-L'", "'W' + Width"; a number parameter shows its value in the design's units, "85 mm", an angle in degrees, "30°"; number(Width) is the number alone and number(Width, 2) has 2 decimals): the text keeps it, its words follow the parameters (a parameter edit or a configuration row relabels it) and `exprs` returns it as `text`; at: baseline start [x,y], height?: capital height mm (default 5), angle?: deg; at's x and y, height and angle each take a number or an expression over the design's parameters as a string ("plate_width / 2", "label_size"): the text keeps the expression, follows the parameters, and returns it in `exprs`; an expression naming no parameter is the number it comes to, a bare number in one is in the design's units, font?: an installed font's family or full name, or a .ttf/.otf file path (~ and %VAR% expand; default: the built-in Liberation Sans), bold?, italic?: bool (an installed family's bold or italic face; italic without one is slanted), align?: left|center|right (the lines against `at`, or along the path), vertical_align?: baseline|top|middle|bottom (the block of lines against `at` or the path: the first line's baseline (default), its capitals' tops, halfway between those and the last line's baseline, or the bottom of the last line's descenders), letter_spacing?: extra mm between letters, line_spacing?: times the font's line distance (default 1), path?: a curve id of the sketch the baseline follows ("": none; `at` and `angle` then do not place the text; the curve becomes construction, a guide the letters stand on), reverse?: bool (the other way along the path). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Construction Line

Command group: SKETCH › CREATE

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
throughsecond pointNo
curvesketch referencesNo
curve2sketch referencesNo
angledegrees from sketch XNo
offsetpositive lengthNoparallel: distance from the reference line; horizontal: distance from sketch X; vertical: distance from sketch Y; the side follows at
solutionzero-based solution indexNo
Automation details

Command ID: sketch.construction_line

Parameters:

Construction line, free. at: point; through?: second point; curve?, curve2?: sketch references; angle?: degrees from sketch X; offset?: positive length (parallel: distance from the reference line; horizontal: distance from sketch X; vertical: distance from sketch Y; the side follows at) and a driving dimension at that value; near?: solution selector, and the side a line with a length runs towards; solution?: zero-based solution index; length?: a segment of this length from `at`, held by a length dimension, instead of an infinite line (horizontal, vertical, angle, parallel, perpendicular); construction?: false makes that segment a normal line. Tangents take circles/arcs, or an open spline/conic endpoint for tangent_at.. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Horizontal Construction Line

Command group: SKETCH › CREATE

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
throughsecond pointNo
curvesketch referencesNo
curve2sketch referencesNo
angledegrees from sketch XNo
offsetpositive lengthNoparallel: distance from the reference line; horizontal: distance from sketch X; vertical: distance from sketch Y; the side follows at
solutionzero-based solution indexNo
Automation details

Command ID: sketch.construction_line.horizontal

Parameters:

Construction line, horizontal. at: point; through?: second point; curve?, curve2?: sketch references; angle?: degrees from sketch X; offset?: positive length (parallel: distance from the reference line; horizontal: distance from sketch X; vertical: distance from sketch Y; the side follows at) and a driving dimension at that value; near?: solution selector, and the side a line with a length runs towards; solution?: zero-based solution index; length?: a segment of this length from `at`, held by a length dimension, instead of an infinite line (horizontal, vertical, angle, parallel, perpendicular); construction?: false makes that segment a normal line. Tangents take circles/arcs, or an open spline/conic endpoint for tangent_at.. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Vertical Construction Line

Command group: SKETCH › CREATE

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
throughsecond pointNo
curvesketch referencesNo
curve2sketch referencesNo
angledegrees from sketch XNo
offsetpositive lengthNoparallel: distance from the reference line; horizontal: distance from sketch X; vertical: distance from sketch Y; the side follows at
solutionzero-based solution indexNo
Automation details

Command ID: sketch.construction_line.vertical

Parameters:

Construction line, vertical. at: point; through?: second point; curve?, curve2?: sketch references; angle?: degrees from sketch X; offset?: positive length (parallel: distance from the reference line; horizontal: distance from sketch X; vertical: distance from sketch Y; the side follows at) and a driving dimension at that value; near?: solution selector, and the side a line with a length runs towards; solution?: zero-based solution index; length?: a segment of this length from `at`, held by a length dimension, instead of an infinite line (horizontal, vertical, angle, parallel, perpendicular); construction?: false makes that segment a normal line. Tangents take circles/arcs, or an open spline/conic endpoint for tangent_at.. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Construction Line at Angle

Command group: SKETCH › CREATE

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
throughsecond pointNo
curvesketch referencesNo
curve2sketch referencesNo
angledegrees from sketch XNo
offsetpositive lengthNoparallel: distance from the reference line; horizontal: distance from sketch X; vertical: distance from sketch Y; the side follows at
solutionzero-based solution indexNo
Automation details

Command ID: sketch.construction_line.angle

Parameters:

Construction line, angle. at: point; through?: second point; curve?, curve2?: sketch references; angle?: degrees from sketch X; offset?: positive length (parallel: distance from the reference line; horizontal: distance from sketch X; vertical: distance from sketch Y; the side follows at) and a driving dimension at that value; near?: solution selector, and the side a line with a length runs towards; solution?: zero-based solution index; length?: a segment of this length from `at`, held by a length dimension, instead of an infinite line (horizontal, vertical, angle, parallel, perpendicular); construction?: false makes that segment a normal line. Tangents take circles/arcs, or an open spline/conic endpoint for tangent_at.. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Parallel Construction Line

Command group: SKETCH › CREATE

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
throughsecond pointNo
curvesketch referencesNo
curve2sketch referencesNo
angledegrees from sketch XNo
offsetpositive lengthNoparallel: distance from the reference line; horizontal: distance from sketch X; vertical: distance from sketch Y; the side follows at
solutionzero-based solution indexNo
Automation details

Command ID: sketch.construction_line.parallel

Parameters:

Construction line, parallel. at: point; through?: second point; curve?, curve2?: sketch references; angle?: degrees from sketch X; offset?: positive length (parallel: distance from the reference line; horizontal: distance from sketch X; vertical: distance from sketch Y; the side follows at) and a driving dimension at that value; near?: solution selector, and the side a line with a length runs towards; solution?: zero-based solution index; length?: a segment of this length from `at`, held by a length dimension, instead of an infinite line (horizontal, vertical, angle, parallel, perpendicular); construction?: false makes that segment a normal line. Tangents take circles/arcs, or an open spline/conic endpoint for tangent_at.. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Perpendicular Construction Line

Command group: SKETCH › CREATE

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
throughsecond pointNo
curvesketch referencesNo
curve2sketch referencesNo
angledegrees from sketch XNo
offsetpositive lengthNoparallel: distance from the reference line; horizontal: distance from sketch X; vertical: distance from sketch Y; the side follows at
solutionzero-based solution indexNo
Automation details

Command ID: sketch.construction_line.perpendicular

Parameters:

Construction line, perpendicular. at: point; through?: second point; curve?, curve2?: sketch references; angle?: degrees from sketch X; offset?: positive length (parallel: distance from the reference line; horizontal: distance from sketch X; vertical: distance from sketch Y; the side follows at) and a driving dimension at that value; near?: solution selector, and the side a line with a length runs towards; solution?: zero-based solution index; length?: a segment of this length from `at`, held by a length dimension, instead of an infinite line (horizontal, vertical, angle, parallel, perpendicular); construction?: false makes that segment a normal line. Tangents take circles/arcs, or an open spline/conic endpoint for tangent_at.. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Tangent through External Point

Command group: SKETCH › CREATE

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
throughsecond pointNo
curvesketch referencesNo
curve2sketch referencesNo
angledegrees from sketch XNo
offsetpositive lengthNoparallel: distance from the reference line; horizontal: distance from sketch X; vertical: distance from sketch Y; the side follows at
solutionzero-based solution indexNo
Automation details

Command ID: sketch.construction_line.tangent_external

Parameters:

Construction line, tangent_external. at: point; through?: second point; curve?, curve2?: sketch references; angle?: degrees from sketch X; offset?: positive length (parallel: distance from the reference line; horizontal: distance from sketch X; vertical: distance from sketch Y; the side follows at) and a driving dimension at that value; near?: solution selector, and the side a line with a length runs towards; solution?: zero-based solution index; length?: a segment of this length from `at`, held by a length dimension, instead of an infinite line (horizontal, vertical, angle, parallel, perpendicular); construction?: false makes that segment a normal line. Tangents take circles/arcs, or an open spline/conic endpoint for tangent_at.. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Tangent at Curve Point

Command group: SKETCH › CREATE

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
throughsecond pointNo
curvesketch referencesNo
curve2sketch referencesNo
angledegrees from sketch XNo
offsetpositive lengthNoparallel: distance from the reference line; horizontal: distance from sketch X; vertical: distance from sketch Y; the side follows at
solutionzero-based solution indexNo
Automation details

Command ID: sketch.construction_line.tangent_at

Parameters:

Construction line, tangent_at. at: point; through?: second point; curve?, curve2?: sketch references; angle?: degrees from sketch X; offset?: positive length (parallel: distance from the reference line; horizontal: distance from sketch X; vertical: distance from sketch Y; the side follows at) and a driving dimension at that value; near?: solution selector, and the side a line with a length runs towards; solution?: zero-based solution index; length?: a segment of this length from `at`, held by a length dimension, instead of an infinite line (horizontal, vertical, angle, parallel, perpendicular); construction?: false makes that segment a normal line. Tangents take circles/arcs, or an open spline/conic endpoint for tangent_at.. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Common Tangent

Command group: SKETCH › CREATE

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
throughsecond pointNo
curvesketch referencesNo
curve2sketch referencesNo
angledegrees from sketch XNo
offsetpositive lengthNoparallel: distance from the reference line; horizontal: distance from sketch X; vertical: distance from sketch Y; the side follows at
solutionzero-based solution indexNo
Automation details

Command ID: sketch.construction_line.tangent_two

Parameters:

Construction line, tangent_two. at: point; through?: second point; curve?, curve2?: sketch references; angle?: degrees from sketch X; offset?: positive length (parallel: distance from the reference line; horizontal: distance from sketch X; vertical: distance from sketch Y; the side follows at) and a driving dimension at that value; near?: solution selector, and the side a line with a length runs towards; solution?: zero-based solution index; length?: a segment of this length from `at`, held by a length dimension, instead of an infinite line (horizontal, vertical, angle, parallel, perpendicular); construction?: false makes that segment a normal line. Tangents take circles/arcs, or an open spline/conic endpoint for tangent_at.. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Angle Bisector

Command group: SKETCH › CREATE

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
throughsecond pointNo
curvesketch referencesNo
curve2sketch referencesNo
angledegrees from sketch XNo
offsetpositive lengthNoparallel: distance from the reference line; horizontal: distance from sketch X; vertical: distance from sketch Y; the side follows at
solutionzero-based solution indexNo
Automation details

Command ID: sketch.construction_line.bisector

Parameters:

Construction line, bisector. at: point; through?: second point; curve?, curve2?: sketch references; angle?: degrees from sketch X; offset?: positive length (parallel: distance from the reference line; horizontal: distance from sketch X; vertical: distance from sketch Y; the side follows at) and a driving dimension at that value; near?: solution selector, and the side a line with a length runs towards; solution?: zero-based solution index; length?: a segment of this length from `at`, held by a length dimension, instead of an infinite line (horizontal, vertical, angle, parallel, perpendicular); construction?: false makes that segment a normal line. Tangents take circles/arcs, or an open spline/conic endpoint for tangent_at.. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Divide Curve

Command group: SKETCH › CREATE

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
curveidYes
curves[ids]Yeseach divided on its own
Automation details

Command ID: sketch.point.divide

Parameters:

curve: id | curves: [ids] (each divided on its own); segments: whole number from 2 to 500. Places points that divide each curve into `segments` pieces of equal length along it (arc length): segments − 1 points on an open curve (line, arc, spline, conic), `segments` points on a closed one (a circle from its point on sketch +X, an ellipse from its major-axis end, a closed spline from its first point). Each point is constrained to its share of the curve's length, so it stays on the curve and keeps equal spacing as the curve changes; the curve is not changed. Infinite lines are refused. Returns points: [ids], per curve in order, and constraints: [ids]. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Equation Curve

Command group: SKETCH › CREATE

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
xxYest
yyYest
explicittrue and y: fYesx
aloneYesno x
startthe range of tYes
endthe range of tYesor of x
tolerancemmNodefault 0.01
constructionboolNo
variable_unit"number"Nodefault
Automation details

Command ID: sketch.equation_curve

Parameters:

x: x(t), y: y(t): formulas of t in the design's expression language and parameters ("r * (cos(t) + t * sin(t))"); or explicit: true and y: f(x) alone (no x); start, end: the range of t (or of x): a number or an expression ("2 * pi", "360 deg" is 2·pi, a length is in the design's unit); tolerance?: mm (default 0.01): how far the curve may stray from the formulas (0.0001 to 10); construction?: bool; variable_unit?: "number" (default) or "range". The variable is a plain number: sin(t) reads it in radians; a result without a unit is in the design's length unit. With variable_unit "range" it is a length, so a ^ 2 / (4 * t) is a length when a is one: start and end must both be lengths, or one a length and the other a plain number, which is then in the design's unit. Makes a fit point spline through fixed points, as many as the tolerance needs (up to 500), closed smoothly when it ends where it starts; the points are placed again from the formulas whenever the parameters change. Returns curve, points (how many), closed, deviation (mm). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Sketch Constrainer

Command group: SKETCH › CREATE

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
entities[one or two sketch entity ids]: applies the constraint the geometry nearly hasYeshorizontal/vertical, parallel, perpendicular, collinear, coincident, concentric, equal, tangent
Automation details

Command ID: sketch.constrainer

Parameters:

entities: [one or two sketch entity ids]: applies the constraint the geometry nearly has (horizontal/vertical, parallel, perpendicular, collinear, coincident, concentric, equal, tangent)

Project

Command group: SKETCH › CREATE

Default keyboard shortcut: P

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
refs[{edge: [x,y,z], body?} | {face: [x,y,z], body?} (its edge loops) | {body: name} (silhouette) | {vertex: [x,y,z], body?} | {sketch, curve} | {sketch, point} | {point: name} (a construction point) | "origin" | {axis: X|Y|Z|name} | {plane: XY|XZ|YZ|name}]Yes
projection_linkboolNodefault true
constructionboolNodefault the session's preference, off: the created curves are construction geometry
Automation details

Command ID: sketch.project

Parameters:

refs: [{edge: [x,y,z], body?} | {face: [x,y,z], body?} (its edge loops) | {body: name} (silhouette) | {vertex: [x,y,z], body?} | {sketch, curve} | {sketch, point} | {point: name} (a construction point) | "origin" | {axis: X|Y|Z|name} | {plane: XY|XZ|YZ|name}]; projection_link?: bool (default true); construction?: bool (default the session's preference, off: the created curves are construction geometry). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Intersect

Command group: SKETCH › CREATE

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
refs[{body}, {face: [x,y,z], body?}, {edge: [x,y,z], body?}, {sketch, curve}]: section curvesYesor points
projection_linkboolNodefault true
constructionboolNodefault the session's preference, off: the created curves are construction geometry
Automation details

Command ID: sketch.intersect

Parameters:

refs: [{body}, {face: [x,y,z], body?}, {edge: [x,y,z], body?}, {sketch, curve}]: section curves (or points) with the sketch plane; projection_link?: bool (default true); construction?: bool (default the session's preference, off: the created curves are construction geometry). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Include 3D Geometry

Command group: SKETCH › CREATE

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
refslike ProjectYesedges, vertices, sketch curves and points
constructionboolNodefault the session's preference, off: the created curves are construction geometry
Automation details

Command ID: sketch.include_3d

Parameters:

refs: like Project (edges, vertices, sketch curves and points); curves out of the sketch plane are flattened onto it; construction?: bool (default the session's preference, off: the created curves are construction geometry). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Project To Surface

Command group: SKETCH › CREATE

Read the sketching and constraints guide.

Automation details

Command ID: sketch.project_to_surface

Parameters:

curves: [{sketch, curve}…] (curves of other sketches), face: [x,y,z] point on the target face, body?: projected along the active sketch's normal onto the face, as linked 3D curves. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Intersection Curve

Command group: SKETCH › CREATE

Read the sketching and constraints guide.

Automation details

Command ID: sketch.intersection_curve

Parameters:

a, b: {body} | {face: [x,y,z], body?} | {sketch, curve} (a curve of another sketch, standing in for its extrusion along that sketch's normal): where they meet, as linked 3D curves. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Spun Profile

Command group: SKETCH › CREATE

Read the sketching and constraints guide.

Automation details

Command ID: sketch.spun_profile

Parameters:

body: name, axis: a line of the active sketch (curve id) | X|Y|Z: the body's outline revolved about the axis, in the sketch plane. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Isoparametric Curve

Command group: SKETCH › CREATE

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
face[x,y,z] point on the faceYesthe curve passes through it
bodyNo
directionu|v|[x,y,z]Nodefault u
Automation details

Command ID: sketch.isoparametric_curve

Parameters:

face: [x,y,z] point on the face (the curve passes through it), body?, direction?: u|v|[x,y,z] (default u): linked 3D curve. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Fit Curves to Mesh Section

Command group: SKETCH › CREATE

Read the sketching and constraints guide.

Automation details

Command ID: sketch.fit_curves_to_section

Parameters:

body: name (a mesh or solid body): its section with the sketch plane, fitted with lines and arcs, as normal (unlinked) geometry. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

SKETCH › CONSTRAINTS

Horizontal/Vertical

Command group: SKETCH › CONSTRAINTS

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
lineidYes
points[a, b]Yespoint ids or {midpoint: line_id}; accepted midpoints reuse or create a point with a Midpoint constraint
modehorizontal|verticalNo
Automation details

Command ID: sketch.constraint.horizontal_vertical

Parameters:

line: id | points: [a, b] (point ids or {midpoint: line_id}; accepted midpoints reuse or create a point with a Midpoint constraint); mode?: horizontal|vertical

Coincident

Command group: SKETCH › CONSTRAINTS

Read the sketching and constraints guide.

Automation details

Command ID: sketch.constraint.coincident

Parameters:

a, b: points (or a line's middle as {midpoint: line}, which becomes a point tied there), a point and a curve or sketch axis (x / y: the sketch's own axes, unless a curve has that id; an axis becomes a linked construction line on it, reported in created), or two distinct lines (joins their closest endpoints), or two circles/arcs (the second lies on the first: concentric and equal, `constraints` lists both); any of them may be a model edge {edge: [x,y,z], body?} or vertex {vertex: [x,y,z], body?}, projected into the sketch first (reported in created)

Tangent

Command group: SKETCH › CONSTRAINTS

Read the sketching and constraints guide.

Automation details

Command ID: sketch.constraint.tangent

Parameters:

a, b: curves or sketch axes (x / y: the sketch's own axes, unless a curve has that id; an axis becomes a linked construction line on it, reported in created), or the end point of a line or an arc: that end then lies on the other curve (or on the other end point), and its curve is tangent there, in one step (`constraints` lists both); any of them may be a model edge {edge: [x,y,z], body?} or vertex {vertex: [x,y,z], body?}, projected into the sketch first (reported in created)

Equal

Command group: SKETCH › CONSTRAINTS

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
alines or circles/arcsYes
blines or circles/arcsYes
Automation details

Command ID: sketch.constraint.equal

Parameters:

a, b: lines or circles/arcs; or curves: [2 to 200 lines or circles/arcs], each made equal to the first, in one step; any of them may be a model edge {edge: [x,y,z], body?} or vertex {vertex: [x,y,z], body?}, projected into the sketch first (reported in created)

Parallel

Command group: SKETCH › CONSTRAINTS

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
alines or sketch axesYes
blines or sketch axesYesx / y: the sketch's own axes, unless a curve has that id; an axis becomes a linked construction line on it, reported in created
Automation details

Command ID: sketch.constraint.parallel

Parameters:

a, b: lines or sketch axes (x / y: the sketch's own axes, unless a curve has that id; an axis becomes a linked construction line on it, reported in created); any of them may be a model edge {edge: [x,y,z], body?} or vertex {vertex: [x,y,z], body?}, projected into the sketch first (reported in created)

Perpendicular

Command group: SKETCH › CONSTRAINTS

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
alines or sketch axesYes
blines or sketch axesYesx / y: the sketch's own axes, unless a curve has that id; an axis becomes a linked construction line on it, reported in created
Automation details

Command ID: sketch.constraint.perpendicular

Parameters:

a, b: lines or sketch axes (x / y: the sketch's own axes, unless a curve has that id; an axis becomes a linked construction line on it, reported in created); any of them may be a model edge {edge: [x,y,z], body?} or vertex {vertex: [x,y,z], body?}, projected into the sketch first (reported in created)

Fix/Unfix

Command group: SKETCH › CONSTRAINTS

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
entitypoint or curveYes
entities[points and curves]Yes
fixedboolNodefault: fix them all unless all are fixed, then unfix
Automation details

Command ID: sketch.constraint.fix

Parameters:

entity: point or curve | entities: [points and curves]; fixed?: bool (default: fix them all unless all are fixed, then unfix)

MidPoint

Command group: SKETCH › CONSTRAINTS

Read the sketching and constraints guide.

Automation details

Command ID: sketch.constraint.midpoint

Parameters:

point, line | a, b in pick order: a point and a line (either way round), or two lines whose midpoints meet (the first stays, the second moves); any of them may be a model edge {edge: [x,y,z], body?} or vertex {vertex: [x,y,z], body?}, projected into the sketch first (reported in created)

Concentric

Command group: SKETCH › CONSTRAINTS

Read the sketching and constraints guide.

Automation details

Command ID: sketch.constraint.concentric

Parameters:

a, b: two circles/arcs, or a circle/arc and a sketch point (including origin), in either order; the point and the curve's centre become coincident; any of them may be a model edge {edge: [x,y,z], body?} or vertex {vertex: [x,y,z], body?}, projected into the sketch first (reported in created)

Collinear

Command group: SKETCH › CONSTRAINTS

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
alines or sketch axesYes
blines or sketch axesYesx / y: the sketch's own axes, unless a curve has that id; an axis becomes a linked construction line on it, reported in created
Automation details

Command ID: sketch.constraint.collinear

Parameters:

a, b: lines or sketch axes (x / y: the sketch's own axes, unless a curve has that id; an axis becomes a linked construction line on it, reported in created); any of them may be a model edge {edge: [x,y,z], body?} or vertex {vertex: [x,y,z], body?}, projected into the sketch first (reported in created)

Symmetry

Command group: SKETCH › CONSTRAINTS

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
atwo points or two curvesYes
btwo points or two curvesYeslines mirror their ends, circles/arcs their centres
Automation details

Command ID: sketch.constraint.symmetry

Parameters:

a, b: two points or two curves (lines mirror their ends, circles/arcs their centres); line: symmetry line id, or x / y for the sketch's own axes (a curve with that id wins; an axis becomes a linked construction line on it, reported in created); or point (or a point id as line, "origin" too): symmetric about that point, each pair joined by a construction line it is the midpoint of; any of them may be a model edge {edge: [x,y,z], body?} or vertex {vertex: [x,y,z], body?}, projected into the sketch first (reported in created)

Curvature

Command group: SKETCH › CONSTRAINTS

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
atwo curves sharing an end pointYes
btwo curves sharing an end pointYescurvature continuous, G2
Automation details

Command ID: sketch.constraint.curvature

Parameters:

a, b: two curves sharing an end point (curvature continuous, G2)

Polygon

Command group: SKETCH › CONSTRAINTS

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
lines[line ids] of a closed chain: equal sides with every corner on one circleYesa regular polygon
Automation details

Command ID: sketch.constraint.polygon

Parameters:

lines: [line ids] of a closed chain: equal sides with every corner on one circle (a regular polygon)

AutoConstrain

Command group: SKETCH › CONSTRAINTS

Read the sketching and constraints guide.

Automation details

Command ID: sketch.auto_constrain

Parameters:

tolerance?: mm (default 0.01), angle_tolerance?: deg (default 1), dimensions?: bool (default true): infer constraints, then dimension until fully constrained

AutoConstrain from Datum

Command group: SKETCH › CONSTRAINTS

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
datumpoint refYesdefault origin
Automation details

Command ID: sketch.auto_constrain_from_datum

Parameters:

datum: point ref (default origin); like AutoConstrain with positions measured from the datum

SKETCH › MODIFY

Blend Curve

Command group: SKETCH › MODIFY

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
aYes
bend points of two curvesYes"l1.end", "a2.start"
continuityg1|g2|g3|g4Nodefault g1: a tangent spline; g2…g4 match the reference curves' curvature, curvature rate and 4th derivative as well, as one Bézier of degree 5, 7 or 9
Automation details

Command ID: sketch.blend_curve

Parameters:

a, b: end points of two curves ("l1.end", "a2.start"), continuity?: g1|g2|g3|g4 (default g1: a tangent spline; g2…g4 match the reference curves' curvature, curvature rate and 4th derivative as well, as one Bézier of degree 5, 7 or 9). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Fillet

Command group: SKETCH › MODIFY

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
pointcorner shared by two curvesYes"l1.end"
points[corners]| aYesall rounded alike: one radius, the rest equal; a corner is a point reference or {a, b}, the two curves meeting there
btwo lines or arcs meeting at a cornerYestwo lines that share no end are first brought to where they meet: the nearer end of a line that stops short moves there, and a line that runs past is cut there, losing its shorter piece
radiusexprYes
Automation details

Command ID: sketch.fillet

Parameters:

point: corner shared by two curves ("l1.end") | points: [corners] (all rounded alike: one radius, the rest equal; a corner is a point reference or {a, b}, the two curves meeting there) | a, b: two lines or arcs meeting at a corner (two lines that share no end are first brought to where they meet: the nearer end of a line that stops short moves there, and a line that runs past is cut there, losing its shorter piece); radius: expr. A corner that dimensions or constraints measure to stays as a virtual sharp corner on both curves' extensions (`virtual_corners`; the moved ones in `moved_to_virtual_corner`). An ellipse's axis line is never a side of a corner (also for the chamfers). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Equal Distance Chamfer

Command group: SKETCH › MODIFY

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
point[corners]| aYesevery corner cut alike; a corner is a point reference or {a, b}
points[corners]| aYesevery corner cut alike; a corner is a point reference or {a, b}
ba corner of two linesYestwo that share no end are first brought to where they meet, as in sketch.fillet
Automation details

Command ID: sketch.chamfer.equal_distance

Parameters:

point | points: [corners] (every corner cut alike; a corner is a point reference or {a, b}) | a, b: a corner of two lines (two that share no end are first brought to where they meet, as in sketch.fillet); distance. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Distance and Angle Chamfer

Command group: SKETCH › MODIFY

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
point[corners]| aYesevery corner cut alike; a corner is a point reference or {a, b}, and a point's `a` is the first of its two lines in curve order
points[corners]| aYesevery corner cut alike; a corner is a point reference or {a, b}, and a point's `a` is the first of its two lines in curve order
ba corner of two linesYestwo that share no end are first brought to where they meet, as in sketch.fillet
distancealong aYes
angledeg from aYes
Automation details

Command ID: sketch.chamfer.distance_angle

Parameters:

point | points: [corners] (every corner cut alike; a corner is a point reference or {a, b}, and a point's `a` is the first of its two lines in curve order) | a, b: a corner of two lines (two that share no end are first brought to where they meet, as in sketch.fillet); distance: along a, angle: deg from a. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Two Distance Chamfer

Command group: SKETCH › MODIFY

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
point[corners]| aYesevery corner cut alike; a corner is a point reference or {a, b}, and a point's `a` is the first of its two lines in curve order
points[corners]| aYesevery corner cut alike; a corner is a point reference or {a, b}, and a point's `a` is the first of its two lines in curve order
ba corner of two linesYestwo that share no end are first brought to where they meet, as in sketch.fillet
distancealong aYes
distance2along bYes
Automation details

Command ID: sketch.chamfer.two_distance

Parameters:

point | points: [corners] (every corner cut alike; a corner is a point reference or {a, b}, and a point's `a` is the first of its two lines in curve order) | a, b: a corner of two lines (two that share no end are first brought to where they meet, as in sketch.fillet); distance: along a, distance2: along b. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Offset

Command group: SKETCH › MODIFY

Default keyboard shortcut: O

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
flipbool reverses the primary sideNo
Automation details

Command ID: sketch.offset

Parameters:

curves: [ids], distance: number in mm or length expression (creates an editable dimension; positive = left of the chain, or toward `side`), side?: [x,y], chain?: bool (default true: the connected chain, including ends that only coincide). A corner is trimmed back to the crossing when the offset runs past it, and extended until the offset curves intersect when the corner falls short; direction?: one_side|symmetric (default one_side) or symmetric?: bool (the same distance each way; no `side`, whose job it takes over), distance2?: positive length expression (Two Sides: an independent distance the other way, as for `solid.extrude`). Each side is its own offset association, listed in `sides`; flip?: bool reverses the primary side; `param`/`param2` name the driving dimensions; instances?: whole number or unitless expression, 1…100 (default 1): each instance after the first offsets the instance before it by the same distance again, so instance n stands n distances from the curves picked. Every instance of a side is driven by that side's own dimension, and each entry of `sides` names its `instance`; an instance the shape cannot take refuses the whole command. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Trim

Command group: SKETCH › MODIFY

Default keyboard shortcut: T

Read the sketching and constraints guide.

Automation details

Command ID: sketch.trim

Parameters:

curve: id, at: [x,y] (the piece to remove: between the nearest intersections around it). Trimming a line source or its single-line offset releases linked extents (offset_released: curve ids), retaining their driving normal-distance dimensions and parallel relations. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Extend

Command group: SKETCH › MODIFY

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
curveidYes
at[x,y] near the end to extendYesa line runs on to the nearest curve, or the previewed reach when nothing is in the way; an arc grows along its circle and closes into a whole circle when nothing stops it
curves[id, id]Yestwo lines or arcs of this sketch, grown until they meet and joined there
at[[x,y],[x,y]]Noone point per curve: the end to grow; default the end nearest the meeting point
Automation details

Command ID: sketch.extend

Parameters:

curve: id, at: [x,y] near the end to extend (a line runs on to the nearest curve, or the previewed reach when nothing is in the way; an arc grows along its circle and closes into a whole circle when nothing stops it) | curves: [id, id] (two lines or arcs of this sketch, grown until they meet and joined there), at?: [[x,y],[x,y]] (one point per curve: the end to grow; default the end nearest the meeting point). `curve` and `curves` exclude each other; the two-curve form returns moved: the meeting point's id

Break

Command group: SKETCH › MODIFY

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
curveidYes
at[x,y]Yessplit at intersections; at a point where the curve ends and another curve meets, the curve gets its own end there and the two are no longer joined
points[[x,y]…]Yes1…1000 positions projected onto the curve; duplicates are merged
Automation details

Command ID: sketch.break

Parameters:

curve: id, at: [x,y] (split at intersections; at a point where the curve ends and another curve meets, the curve gets its own end there and the two are no longer joined) | points: [[x,y]…] (1…1000 positions projected onto the curve; duplicates are merged). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Sketch Scale

Command group: SKETCH › MODIFY

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
entities[curve/point ids]Yes
base[x,y] or point refYes
factornumberYes
Automation details

Command ID: sketch.scale

Parameters:

entities: [curve/point ids], base: [x,y] or point ref, factor: number; the driving length and radius dimensions wholly inside the selection scale too (a formula becomes `(formula) * factor`)

Transform

Command group: SKETCH › MODIFY

Default keyboard shortcut: G

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
entities[curve/point ids]Yes
factornumberNoscale
angledegreesNoturn
center[x,y] or point refNowhat scales and turns stay put; default [0,0]
translate[dx,dy]Noapplied last
Automation details

Command ID: sketch.transform

Parameters:

entities: [curve/point ids], factor?: number (scale), angle?: degrees (turn), center?: [x,y] or point ref (what scales and turns stay put; default [0,0]), translate?: [dx,dy] (applied last); scales, turns and moves in one step

Move/Copy Sketch Entities

Command group: SKETCH › MODIFY

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
entities[ids]Yes
translate[dx,dy]No
angledegNo
center[x,y]No
copyboolNo
rotate_constraintsboolNodefault true; false keeps constraints unchanged
Automation details

Command ID: sketch.move

Parameters:

entities: [ids], translate?: [dx,dy], angle?: deg, center?: [x,y], copy?: bool, rotate_constraints?: bool (default true; false keeps constraints unchanged); a rotation turns the constraints tied to the sketch axes when all their geometry moves (horizontal and vertical relations and dimensions swap at odd quarter turns and stay at half turns, line angles turn with the line) and removes the horizontal and vertical ones that cannot hold at other angles; picking a text outline or linked point transforms its whole text source once, and copies remain editable text; copy also accepts drawn 3D curves, preserving exact arcs and height above the sketch plane; a turned rectangle keeps its shape: parallel and perpendicular relations replace the horizontal and vertical ones lost; returns `released`: the constraints tying the moved geometry to what stays that the move let go, as {id, kind}, and `added`: the relations put in their place, as {id, kind}. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

SKETCH › INSERT

Insert DXF

Command group: SKETCH › INSERT

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
pathASCII DXFYesblocks expanded
textASCII DXFYesblocks expanded
align"origin"| "center"Nodefault: `at` is where the file's origin goes; `at` is where the drawing's middle goes and it turns and scales about it
tolerancemm within which ends joinNodefault 0.0001
fixedboolNoa Fix constraint on every point the drawing adds, as sketch.constraint.fix puts one; a circle's or ellipse's radius stays free; default false
planeNowhen no sketch is active, a new sketch on it; default XY
Automation details

Command ID: sketch.insert_dxf

Parameters:

path | text: ASCII DXF (blocks expanded); at?: [x,y] offset, scale? | width? | height?: mm, the size of the drawn geometry's bounds before rotation, aspect kept (win over scale); angle?: degrees, about the drawing's origin (or its centre); align?: "origin" (default: `at` is where the file's origin goes) | "center" (`at` is where the drawing's middle goes and it turns and scales about it); tolerance?: mm within which ends join (default 0.0001); fixed?: bool (a Fix constraint on every point the drawing adds, as sketch.constraint.fix puts one; a circle's or ellipse's radius stays free; default false); plane?: (when no sketch is active, a new sketch on it; default XY). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Insert SVG

Command group: SKETCH › INSERT

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
pathSVGYes
textSVGYes
size_from"geometry"| "viewbox"Nodefault: width and height are the bounds of the drawn geometry; the SVG sheet: its viewBox, else its width and height; a drawing smaller than its sheet then comes out smaller than width or height say
align"origin"| "center"Nodefault: `at` is where the file's origin goes; `at` is where the drawing's (or sheet's) middle goes and it turns and scales about it
tolerancemm within which ends joinNodefault 0.0001
fixedboolNoevery point it adds gets a Fix constraint, so the drawing stays where it is put
planeNowhen no sketch is active, a new sketch on it; default XY
Automation details

Command ID: sketch.insert_svg

Parameters:

path | text: SVG; at?: [x,y] offset, scale? | width? | height?: mm, the drawing's size before rotation, aspect kept (win over scale); size_from?: "geometry" (default: width and height are the bounds of the drawn geometry) | "viewbox" (the SVG sheet: its viewBox, else its width and height; a drawing smaller than its sheet then comes out smaller than width or height say); angle?: degrees, about the drawing's origin (or its centre); align?: "origin" (default: `at` is where the file's origin goes) | "center" (`at` is where the drawing's (or sheet's) middle goes and it turns and scales about it); tolerance?: mm within which ends join (default 0.0001); fixed?: bool (every point it adds gets a Fix constraint, so the drawing stays where it is put); plane?: (when no sketch is active, a new sketch on it; default XY). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Canvas

Command group: SKETCH › INSERT

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
pathYesbase64 PNG or JPEG
dataYesbase64 PNG or JPEG
planeXY|XZ|YZ|{face: [x,y,z]}|…Nodefault XY
center[x,y] on the planeNo
at[x,y,z]No
widthmmNodefault 100
angledegNo
opacity0…1Nodefault 0.5
flipNo
nameNo
Automation details

Command ID: canvas.insert

Parameters:

path | data (base64 PNG or JPEG); plane?: XY|XZ|YZ|{face: [x,y,z]}|… (default XY); center?: [x,y] on the plane, or at?: [x,y,z]; width?: mm (default 100); angle?: deg; opacity?: 0…1 (default 0.5); flip?; name?

Decal

Command group: SKETCH › INSERT

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
pathYes
dataYes
widthmmNo
angledegNo
opacityNodefault 1
Automation details

Command ID: canvas.decal

Parameters:

path | data; face: [x,y,z] on a planar face (the image is centred there), or the picked face as sketch.create's plane takes it ({face, body, index, path?}); width?: mm; angle?: deg; opacity?: (default 1)

Edit Canvas

Command group: SKETCH › INSERT

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
canvasid or nameYes
centerNo
widthNo
angledegNo
opacityNo
flipNo
visibleNo
nameNo
lockedNoa locked canvas refuses center, width, angle, flip, Calibrate and Delete
Automation details

Command ID: canvas.edit

Parameters:

canvas: id or name; center?, width?, angle?: deg, opacity?, flip?, visible?, name?, locked? (a locked canvas refuses center, width, angle, flip, Calibrate and Delete)

SKETCH › INSPECT

Curvature Comb Analysis

Command group: SKETCH › INSPECT

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
curves[sketch curve ids]Nosketch?: id|name, default active
edges[[x,y,z] points on body edges: the edge whose exact curve passes nearest]No
faces[[x,y,z] points on faces] with count?: isoparametric curves each wayNodefault 4; combs along the face's iso grid
scalecomb length per unit curvatureNodefault auto: the longest tooth a fifth of the combs' size
Automation details

Command ID: inspect.curvature_comb

Parameters:

curves?: [sketch curve ids] (sketch?: id|name, default active) | edges?: [[x,y,z] points on body edges: the edge whose exact curve passes nearest] | faces?: [[x,y,z] points on faces] with count?: isoparametric curves each way (default 4; combs along the face's iso grid); density?: teeth per curve, 2…2000 (default 40; on an edge or an iso curve exactly that many, evenly spaced along its parameter); scale?: comb length per unit curvature (default auto: the longest tooth a fifth of the combs' size); length?: a multiplier on the automatic scale, 0.01…100 (default 1; ignored with `scale`); add?: true keeps the combs shown and adds these, every comb again at one scale (with nothing picked it draws those shown again with this call's options, or turns the analysis on with none); clear?: true turns the analysis off. Every comb of a call shares one scale, so tooth lengths compare across curves; the viewport draws them. The curvature is exact: from each sketch curve, edge curve (line, circle, ellipse, B-spline, NURBS, intersection curve) or face surface's own derivatives, never from the display mesh; a face's teeth are the surface's normal curvature along each iso curve, along the surface normal. → combs: [{curve, teeth: [[foot, tip]], envelope: [tip], max_curvature, min_radius}] (those shown, with `add`), scale

Minimum Radius Analysis

Command group: SKETCH › INSPECT

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
curves[sketch curve ids]Nosketch?
edges[[x,y,z]…]No
faces[[x,y,z]…]Nodefault: every face of every body
Automation details

Command ID: inspect.minimum_radius

Parameters:

curves?: [sketch curve ids] (sketch?) | edges?: [[x,y,z]…] | faces?: [[x,y,z]…] (default: every face of every body); the smallest radius of curvature of each and where it is

Isocurve Analysis

Command group: SKETCH › INSPECT

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
faces[[x,y,z]…] points on facesYes1…100
addtrue keeps the faces already shownNo
cleartrue turns the analysis off.No
Automation details

Command ID: inspect.isocurve

Parameters:

faces: [[x,y,z]…] points on faces (1…100); count?: whole number of curves each way, 1…100 (default 8); add?: true keeps the faces already shown; clear?: true turns the analysis off. → each face's isoparametric curves, u = const and v = const of its surface, evenly spaced across it and cut to its trims, as points (`curves`) and per face (`faces`); the viewport draws them

Center of Mass

Command group: SKETCH › INSPECT

Read the sketching and constraints guide.

Automation details

Command ID: inspect.center_of_mass

Parameters:

bodies?: [names] (default all): the centre of mass (uniform density) of each and of all together, from the exact mass properties `inspect.measure` takes → center, volume, bodies: [{body, center, volume}], skipped: [{body, reason}] (bodies that enclose no volume or could not be measured)

Zebra Analysis

Command group: SKETCH › INSPECT

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
stripesnumber across the viewNodefault 12
cleartrue turns the analysis offNo
Automation details

Command ID: inspect.zebra

Parameters:

stripes?: number across the view (default 12); clear?: true turns the analysis off

Draft Analysis

Command group: SKETCH › INSPECT

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
pull[x,y,z] directionNodefault Z
angledegreesNodefault 1
cleartrue turns the analysis offNo
Automation details

Command ID: inspect.draft

Parameters:

pull?: [x,y,z] direction (default Z); angle?: degrees (default 1); clear?: true turns the analysis off

Environment Map Analysis

Command group: SKETCH › INSPECT

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
cleartrue turns the analysis offNo
Automation details

Command ID: inspect.environment_map

Parameters:

clear?: true turns the analysis off

Accessibility Analysis

Command group: SKETCH › INSPECT

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
direction[x,y,z] the tool comes fromNodefault Z, from above
cleartrue turns the analysis offNo
Automation details

Command ID: inspect.accessibility

Parameters:

direction?: [x,y,z] the tool comes from (default Z, from above); clear?: true turns the analysis off

Curvature Map Analysis

Command group: SKETCH › INSPECT

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
radiusmm shown redNodefault: a tenth of the model's size
cleartrue turns the analysis offNo
Automation details

Command ID: inspect.curvature_map

Parameters:

radius?: mm shown red (default: a tenth of the model's size); clear?: true turns the analysis off

Inspect Sketch

Command group: SKETCH › INSPECT

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
sketchid|nameNodefault active
Automation details

Command ID: sketch.inspect

Parameters:

sketch?: id|name (default active)

SKETCH › PATTERN

Mirror

Command group: SKETCH › PATTERN

Read the sketching and constraints guide.

Automation details

Command ID: sketch.mirror

Parameters:

entities: [curve/point ids], line: straight curve id, x / y for a sketch axis, or {axis: X|Y|Z} for an origin axis (the mirror line is the axis's projection into the sketch plane; an axis perpendicular to the plane is refused) (symmetry constraints are added). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Circular Pattern

Command group: SKETCH › PATTERN

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
alignmentleft|center|rightNodefault right
reversedboolNodefault false
Automation details

Command ID: sketch.pattern.circular

Parameters:

entities: [ids], center: [x,y] or point ref, count: 2…500 (number or unitless expression, retained through edits), angle?: total deg, number or expression (default 360); alignment?: left|center|right (default right); reversed?: bool (default false). Quantity includes the original. Center balances copies around it; reversed flips the direction and extra side for even totals. Partial arcs span angle with angle/(count-1) spacing; full turns use angle/count. Adds associative pattern constraints; a coordinate center creates a constrainable point. Returns pattern and center ids. pattern?: existing pattern id to edit (omitted values retained; surviving instance ids retained). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Rectangular Pattern

Command group: SKETCH › PATTERN

Read the sketching and constraints guide.

ParameterTypeRequiredDescription
entities[ids]Yes
countYes
spacingYes
count2No
spacing2No
spacingslength numbers or expressionsYes
directionsketch line idsNo
direction2sketch line idsNolive references
dir[x,y] vectorsNo
dir2[x,y] vectorsNodefaults: sketch x, perpendicular to direction 1
alignmentNo
alignment2left|center|rightNodefault right
reversed2boolNodefault false
Automation details

Command ID: sketch.pattern.rectangular

Parameters:

entities: [ids], count, spacing; count2?, spacing2?; counts: positive integer numbers or expressions, at most 500 instances total; spacings: length numbers or expressions; direction?, direction2?: sketch line ids (live references); dir?, dir2?: [x,y] vectors (defaults: sketch x, perpendicular to direction 1); alignment?, alignment2?: left|center|right (default right). Signed spacings reverse each direction. reversed?, reversed2?: bool (default false); reverses placement and the extra side of even centered totals. Quantities include the original. Adds associative pattern constraints. Returns pattern id. pattern?: existing pattern id to edit (omitted values retained; surviving instance ids retained). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

SURFACE › INSPECT

Continuity Analysis

Command group: SURFACE › INSPECT

Read the solid features guide.

ParameterTypeRequiredDescription
bodynameNodefault: the visible bodies
edges[x,y,z] points or {"index": i}Nodefault: every edge of the body; a pick must match one body
gap_tolerance_mmNodefault 0.001
angle_tolerance_degNodefault 1
curvature_tolerance_per_mmNodefault 0.01
curvature_change_tolerance_per_mm2Nodefault 0.001
curvature_change_rate_tolerance_per_mm3Nodefault 0.0001
addtrue keeps the seams already shaded and adds theseNomeasuring an edge again replaces it
cleartrue turns the analysis off.No
Automation details

Command ID: inspect.continuity

Parameters:

body?: name (default: the visible bodies); edges?: [x,y,z] points or {"index": i} (default: every edge of the body; a pick must match one body); samples?: 3…257 stations along each edge, ends included (default 33); gap_tolerance_mm? (default 0.001), angle_tolerance_deg? (default 1), curvature_tolerance_per_mm? (default 0.01), curvature_change_tolerance_per_mm2? (default 0.001), curvature_change_rate_tolerance_per_mm3? (default 0.0001); add?: true keeps the seams already shaded and adds these (measuring an edge again replaces it); clear?: true turns the analysis off. → each edge's worst gap (G0), normal angle (G1), curvature jump (G2), jump of the curvature's change across the seam dκ/ds (G3) and of that change's rate d²κ/ds² (G4), from the faces' exact derivatives, as {value, at: [x,y,z], edge_fraction}; its order (g0…g4: the highest every station meets within the tolerances, null where the faces do not meet), its verdict (smooth: g2 or better, curvature_jump, sharp or gap) and a summary; the seams are shaded in the viewport

SURFACE › CREATE

Boundary Surface

Command group: SURFACE › CREATE

Read the solid features guide.

ParameterTypeRequiredDescription
curves[2 to 4 sketch curve ids in loop order]Yeseach one ends where the next begins and the last where the first does; two: the patch spans between them and leaves each boundary along the tangent across it; three or four: the flat patch of the loop, three collapsing the side opposite the first curve to the corner there
continuityg0|g1|g2|g3 | [one per curve]Nodefault g1: how much of the surface across each boundary the patch repeats — a sketch curve's own plane, so g1 and up hold exactly and g0 lets the middle bow
crownnumber -0.9…3Nodefault 0: how full the middle is, without touching a boundary; two curves only
operationnew|joinNodefault new; join sews the surface into the body of its first edge curve, a solid when it closes the gap
body_nameNoa surface body
Automation details

Command ID: surface.boundary

Parameters:

Either a loop of one sketch's curves — sketch?: id|name (default: the sketch being edited); curves: [2 to 4 sketch curve ids in loop order] (each one ends where the next begins and the last where the first does; two: the patch spans between them and leaves each boundary along the tangent across it; three or four: the flat patch of the loop, three collapsing the side opposite the first curve to the corner there); continuity?: g0|g1|g2|g3 | [one per curve] (default g1: how much of the surface across each boundary the patch repeats — a sketch curve's own plane, so g1 and up hold exactly and g0 lets the middle bow); crown?: number -0.9…3 (default 0: how full the middle is, without touching a boundary; two curves only); samples?: 8…512 stations per boundary (default 25) — or a curve network — u: [curve] (two or more one way: the surface lofts through them), v?: [curve] (crossing every u-curve once: two each way give a Coons patch, more a Gordon surface through every curve; a curve that misses another by more than 0.01 mm is refused, naming the pair), each curve "id" (a curve of `sketch`) | {sketch?: id|name, curves: [ids meeting end to end]} (planar or 3D sketch curves) | {body: name, edges: [[x,y,z]] (points on a chain of its edges), continuity?: g0|g1|g2 (default g0; g1 the surface leaves tangent to the face beside a surface's open edges, g2 with its curvature too; on curves of one direction only)} | {point: [x,y,z]} (a side collapsed to a point: a three-sided patch); operation?: new|join (default new; join sews the surface into the body of its first edge curve, a solid when it closes the gap). name?, body_name? (a surface body)

Patch

Command group: SURFACE › CREATE

Read the solid features guide.

ParameterTypeRequiredDescription
sketchid|name and profiles?Noplanar patches of sketch regions
profiles_3d[indices]Yesthe sketch's planar 3D-sketch loops, each patched in its own plane, as listed by sketch.inspect
edges[[x,y,z]]Yespoints on a closed loop of a body's edges: one face on them, planar when the loop is, else a smooth B-spline surface
continuityg0|g1|g2|g3|g4Noedges only, default g0: how the patch meets the faces beside the loop — g1 their tangent planes, g2 also their curvature across the edges, g3 also its rate of change, g4 the change of that rate
points[[x,y,z]]Noedges only, up to 64: points the patch passes through
crownnumber −0.9…3Nodefault 0: only a fitted patch, whose middle becomes fuller by this many times the loop's size — the bump vanishes on the loop, so the face still meets the picked edges and the continuity across them; a loop that is planar and g0 is the exact plane whatever the crown; with points, crown needs a g1 or higher edge
nameNo
body_nameNo
Automation details

Command ID: surface.patch

Parameters:

sketch?: id|name and profiles? (planar patches of sketch regions) or profiles_3d: [indices] (the sketch's planar 3D-sketch loops, each patched in its own plane, as listed by sketch.inspect) | edges: [[x,y,z]] (points on a closed loop of a body's edges: one face on them, planar when the loop is, else a smooth B-spline surface) and body?; continuity?: g0|g1|g2|g3|g4 (edges only, default g0: how the patch meets the faces beside the loop — g1 their tangent planes, g2 also their curvature across the edges, g3 also its rate of change, g4 the change of that rate) or [{edge: [x,y,z], g: g0|g1|g2|g3|g4}] (those edges so, the rest g0) or {all, edges}; points?: [[x,y,z]] (edges only, up to 64: points the patch passes through); crown?: number −0.9…3 (default 0: only a fitted patch, whose middle becomes fuller by this many times the loop's size — the bump vanishes on the loop, so the face still meets the picked edges and the continuity across them; a loop that is planar and g0 is the exact plane whatever the crown; with points, crown needs a g1 or higher edge); name?, body_name?

Thicken

Command group: SURFACE › CREATE

Read the solid features guide.

ParameterTypeRequiredDescription
bodies[surface names]Yes
thicknessexprYesnegative: against the normal
symmetricboolNo
operationnew|join|cut|intersectNo
targetsNo
Automation details

Command ID: surface.thicken

Parameters:

bodies: [surface names]; thickness: expr (negative: against the normal); symmetric?: bool; operation?: new|join|cut|intersect; targets?

Surface Extrude

Command group: SURFACE › CREATE

Read the solid features guide.

ParameterTypeRequiredDescription
sketchid|nameNo
curves[curve ids]Noopen or closed chains of curves meeting end to end
distanceexprYes
directionpositive|negative|symmetricNosymmetric: distance is the total, split evenly around the start plane, as solid.extrude
conventiontotal_distance|legacy_per_sideNoas solid.extrude; default total_distance; legacy_per_side: the distance on each side, as older symmetric surface extrudes keep
distance2exprNo
start_offsetexprNo
nameNo
body_nameNoa surface body: walls only, no caps
Automation details

Command ID: surface.extrude

Parameters:

sketch?: id|name; curves?: [curve ids] (open or closed chains of curves meeting end to end) and/or profiles? (their boundaries; the default when no curves are given); distance: expr; direction?: positive|negative|symmetric (symmetric: distance is the total, split evenly around the start plane, as solid.extrude); convention?: total_distance|legacy_per_side (as solid.extrude; default total_distance; legacy_per_side: the distance on each side, as older symmetric surface extrudes keep); distance2?: expr; start_offset?: expr; name?, body_name? (a surface body: walls only, no caps)

Surface Revolve

Command group: SURFACE › CREATE

Read the solid features guide.

ParameterTypeRequiredDescription
sketchid|nameNo
curves[curve ids] and/or profiles?Noas surface.extrude
axissketch line id|x|y|X|Y|Z|{type: world, axis: X|Y|Z}|{sketch, curve}|construction axis name|{origin, dir}| {edge|face: [x,y,z]}Yesa line of another sketch, followed in that sketch; origin: numbers or length expressions, followed when parameters change; a straight or circular edge, or a cylinder, cone or torus face: about its axis, followed as the model changes
angleexprNodefault 360 deg
angle2exprNothe other side
symmetricboolNo
nameNo
body_nameNoa surface body, no caps; a solid where a full turn closes it
Automation details

Command ID: surface.revolve

Parameters:

sketch?: id|name; curves?: [curve ids] and/or profiles? (as surface.extrude); axis: sketch line id|x|y|X|Y|Z|{type: world, axis: X|Y|Z}|{sketch, curve} (a line of another sketch, followed in that sketch)|construction axis name|{origin, dir} (origin: numbers or length expressions, followed when parameters change) | {edge|face: [x,y,z]} (a straight or circular edge, or a cylinder, cone or torus face: about its axis, followed as the model changes); angle?: expr (default 360 deg); angle2?: expr (the other side); symmetric?: bool; name?, body_name? (a surface body, no caps; a solid where a full turn closes it)

Surface Sweep

Command group: SURFACE › CREATE

Read the solid features guide.

ParameterTypeRequiredDescription
sketchid|nameNo
curves[curve ids] and/or profiles?Noas surface.extrude
path_sketchsketchYes
path[curve ids in order]Yesa path with corners, or 3D curves, gives ruled walls mitred at the corners
path_bodybody nameNo
path_edges[points on its edges]Nothe path is a chain of that body's edges, instead of a sketch's curves
orientationNo
twistNo
scaleNo
railsNo
rail_fitNoas solid.sweep
nameNo
body_nameNoa surface body, no caps
Automation details

Command ID: surface.sweep

Parameters:

sketch?: id|name; curves?: [curve ids] and/or profiles? (as surface.extrude); path_sketch: sketch; path: [curve ids in order] (a path with corners, or 3D curves, gives ruled walls mitred at the corners); path_body?: body name; path_edges?: [points on its edges] (the path is a chain of that body's edges, instead of a sketch's curves); orientation?, twist?, scale?, rails?, rail_fit? (as solid.sweep); name?, body_name? (a surface body, no caps)

Surface Loft

Command group: SURFACE › CREATE

Read the solid features guide.

ParameterTypeRequiredDescription
sections[{sketch, curves?: [curve ids], profiles?} | {body, edges: [[x,y,z]]}]Yes2…50 in order; each one chain: a profile without holes, or curves meeting end to end; all open or all closed; a body edge chain takes the face the chain bounds as the surface the end repeats
continuity[g0|g1|g2|g3, g0|g1|g2|g3]Noeach end, default g0: how much of the surface at the first and last section the loft repeats — the tangent plane, the curvature, or how that curvature changes
crown[number, number]Nodefault 0: how full each end leaves, -0.9 flattest to 3 fullest
railsNo
rail_fitNoas solid.loft; sketch sections only, both ends g0
nameNo
body_nameNoa surface body: ruled between two sections, smooth through three or more; no caps
Automation details

Command ID: surface.loft

Parameters:

sections: [{sketch, curves?: [curve ids], profiles?} | {body, edges: [[x,y,z]]}] (2…50 in order; each one chain: a profile without holes, or curves meeting end to end; all open or all closed; a body edge chain takes the face the chain bounds as the surface the end repeats); continuity?: [g0|g1|g2|g3, g0|g1|g2|g3] (each end, default g0: how much of the surface at the first and last section the loft repeats — the tangent plane, the curvature, or how that curvature changes); crown?: [number, number] (default 0: how full each end leaves, -0.9 flattest to 3 fullest); rails?, rail_fit? (as solid.loft; sketch sections only, both ends g0); name?, body_name? (a surface body: ruled between two sections, smooth through three or more; no caps)

Offset Surface

Command group: SURFACE › CREATE

Read the solid features guide.

ParameterTypeRequiredDescription
faces[[x,y,z]]Yespoints on the faces
bodyNo
distanceexprYessigned, along the faces' normals; 0 copies the faces
chainboolNoeach pick's whole smooth chain of faces
toleranceexprNohow far a fitted face may stray, default 0.01 mm
Automation details

Command ID: surface.offset

Parameters:

faces: [[x,y,z]] (points on the faces); body?; distance: expr (signed, along the faces' normals; 0 copies the faces); chain?: bool (each pick's whole smooth chain of faces); tolerance?: expr (how far a fitted face may stray, default 0.01 mm). Planes and cylinders move exactly; other faces are fitted. A new surface body (a solid where its faces close)

Ruled Surface

Command group: SURFACE › CREATE

Read the solid features guide.

ParameterTypeRequiredDescription
bodyYes
edges[[x,y,z]]Yespoints on a chain of its edges, open or closed
distanceexprYeshow far the strip reaches
modenormal|tangent|angle|directionNodefault normal: along the normal of the face beside each edge; tangent: continuing it
angleexprNoangle mode: from `direction`, else from the face's normal, turned away from the face; 90 deg from the normal is tangent
direction[x,y,z]Nodirection mode: the rulings; angle mode: the reference, e.g. a pull direction for a draft
face[x,y,z]Noa point on the face the directions come from; default: the face beside the most picked edges
flipboolNothe other side
cornersmitre|roundNosharp corners of the chain; inside corners are always cut back
Automation details

Command ID: surface.ruled

Parameters:

body; edges: [[x,y,z]] (points on a chain of its edges, open or closed); distance: expr (how far the strip reaches); mode?: normal|tangent|angle|direction (default normal: along the normal of the face beside each edge; tangent: continuing it); angle?: expr (angle mode: from `direction`, else from the face's normal, turned away from the face; 90 deg from the normal is tangent); direction?: [x,y,z] (direction mode: the rulings; angle mode: the reference, e.g. a pull direction for a draft); face?: [x,y,z] (a point on the face the directions come from; default: the face beside the most picked edges); flip?: bool (the other side); corners?: mitre|round (sharp corners of the chain; inside corners are always cut back); far?: [null|[[x,y,z]]] (per chain edge, the interior control points of a free strip's far curve, as the default curve's handles list them; null: the default). A constant ruling, or one keeping its angle to an arc's axis, gives an exact plane, cylinder or cone; where the rulings turn freely along an edge, the strip is the exact ruled surface to a far curve of its own (from corner to corner, fair, near the rulings' ends). A new surface body

Blend Surface

Command group: SURFACE › CREATE

Read the solid features guide.

ParameterTypeRequiredDescription
from{body, edges: [[x,y,z]]}Yes
to{body, edges: [[x,y,z]]}Yespoints on the edges of one chain of each body: two picks on the same edge is that one edge, more chain up
continuity[g0|g1|g2|g3, g0|g1|g2|g3]Noeach end, default g1: how much of the surface at the picked edges the blend repeats — the tangent plane, the curvature, or how that curvature changes
bulge[number, number]Nodefault 0: how full each end leaves, -0.9 flattest to 3 fullest
align[[x,y,z], [x,y,z]]Noa point each end leaves towards, when the faces at the picks do not say
seam[[x,y,z], [x,y,z]]Noa point on each chain its stations start from: two closed chains otherwise pair the way that keeps the blend from twisting; an open chain starts at the end nearer its point; a closed chain blends only to a closed chain
stitchboolNosew the blend onto its two bodies where the boundaries meet, replacing them
nameNo
body_nameNo
Automation details

Command ID: surface.blend

Parameters:

from, to: {body, edges: [[x,y,z]]} (points on the edges of one chain of each body: two picks on the same edge is that one edge, more chain up); continuity?: [g0|g1|g2|g3, g0|g1|g2|g3] (each end, default g1: how much of the surface at the picked edges the blend repeats — the tangent plane, the curvature, or how that curvature changes); bulge?: [number, number] (default 0: how full each end leaves, -0.9 flattest to 3 fullest); align?: [[x,y,z], [x,y,z]] (a point each end leaves towards, when the faces at the picks do not say); seam?: [[x,y,z], [x,y,z]] (a point on each chain its stations start from: two closed chains otherwise pair the way that keeps the blend from twisting; an open chain starts at the end nearer its point; a closed chain blends only to a closed chain); stitch?: bool (sew the blend onto its two bodies where the boundaries meet, replacing them); name?, body_name?

SURFACE › MODIFY

Stitch

Command group: SURFACE › MODIFY

Read the solid features guide.

ParameterTypeRequiredDescription
bodies[names]Yessurfaces sewn into the first; a solid when they close
toleranceexprNodefault: tight
Automation details

Command ID: surface.stitch

Parameters:

bodies: [names] (surfaces sewn into the first; a solid when they close); tolerance?: expr (default: tight)

Extend

Command group: SURFACE › MODIFY

Read the solid features guide.

ParameterTypeRequiredDescription
bodysurface nameYes
edges[[x,y,z]]Yespoints on boundary edges to push out
distanceexprYesunless up_to
modenatural| tangent| curvatureNothe surface itself continued: a B-spline's end spans continued, which keeps planes and surfaces of revolution on themselves; the default; the face continued tangent to its surface across the edge, G1; continued keeping the surface's curvature across the edge too, G2
up_toplane| {body, point}| "bounds"NoXY|XZ|YZ|plane name|{origin, normal}; a face, its surface extended; natural: back to the surface's own bounds, undoing a trim
neighboursboolNodefault true: the edges next to an extended edge are lengthened to meet it; false: the extension's sides run square to the edge
Automation details

Command ID: surface.extend

Parameters:

body: surface name; edges: [[x,y,z]] (points on boundary edges to push out); distance: expr (unless up_to); mode?: natural (the surface itself continued: a B-spline's end spans continued, which keeps planes and surfaces of revolution on themselves; the default) | tangent (the face continued tangent to its surface across the edge, G1) | curvature (continued keeping the surface's curvature across the edge too, G2); up_to?: plane (XY|XZ|YZ|plane name|{origin, normal}) | {body, point} (a face, its surface extended) | "bounds" (natural: back to the surface's own bounds, undoing a trim); neighbours?: bool (default true: the edges next to an extended edge are lengthened to meet it; false: the extension's sides run square to the edge)

Trim

Command group: SURFACE › MODIFY

Read the solid features guide.

ParameterTypeRequiredDescription
bodysurface nameYes
planeXY|XZ|YZ|plane name|{origin, normal} or tool: {body, point}Yesa face, its surface extended
curves{sketch, curves?: [curve ids] (chains of curves meeting end to end; default every curve), direction?: normal|[x,y,z] (swept onto the surface along the sketch's normal, the default, or this direction)}Yes
keep[x,y,z]Yesa point on the side to keep
keep_with[x,y,z] or [[x,y,z], …]Yeswhat to keep of it
extendboolNoa surface that stops short of the other is extended to meet it
stitchboolNothe kept pieces sewn into `body`; `with` is then gone
Automation details

Command ID: surface.trim

Parameters:

body: surface name; plane: XY|XZ|YZ|plane name|{origin, normal} or tool: {body, point} (a face, its surface extended) or curves: {sketch, curves?: [curve ids] (chains of curves meeting end to end; default every curve), direction?: normal|[x,y,z] (swept onto the surface along the sketch's normal, the default, or this direction)}; keep: [x,y,z] (a point on the side to keep) or [[x,y,z], ...] (points on the regions to keep: the pieces that hang together once cut). Mutual trim: with: another surface (one face each; leave out plane, tool and curves), keep_with: [x,y,z] or [[x,y,z], ...] (what to keep of it); each is cut where the other's surface (extended) meets it; extend?: bool (a surface that stops short of the other is extended to meet it); stitch?: bool (the kept pieces sewn into `body`; `with` is then gone)

Untrim

Command group: SURFACE › MODIFY

Read the solid features guide.

ParameterTypeRequiredDescription
face{body, point}Yes
holesall|[[x,y,z]]Nopoints on the inner loops to remove; default: all
outerboolNorestore the outer boundary to the surface's natural bounds: a free-form surface's own, an analytic surface's span of the face
in_placeboolNountrim a surface body's face where it is; a solid's face is always copied out
Automation details

Command ID: surface.untrim

Parameters:

face: {body, point}; holes?: all|[[x,y,z]] (points on the inner loops to remove; default: all); outer?: bool (restore the outer boundary to the surface's natural bounds: a free-form surface's own, an analytic surface's span of the face); in_place?: bool (untrim a surface body's face where it is; a solid's face is always copied out). A surface body; the face keeps its name

Unstitch

Command group: SURFACE › MODIFY

Read the solid features guide.

ParameterTypeRequiredDescription
bodyYes
faces[[x,y,z]]Nopoints on the faces to take out, each its own surface body; the rest stay together under the body's name; default: every face its own surface body
Automation details

Command ID: surface.unstitch

Parameters:

body; faces?: [[x,y,z]] (points on the faces to take out, each its own surface body; the rest stay together under the body's name; default: every face its own surface body). A solid comes apart into surfaces

Reverse Normal

Command group: SURFACE › MODIFY

Read the solid features guide.

ParameterTypeRequiredDescription
bodyYesa surface
faces[[x,y,z]]Nopoints on the faces to turn over; default: all of them
Automation details

Command ID: surface.reverse_normal

Parameters:

body (a surface); faces?: [[x,y,z]] (points on the faces to turn over; default: all of them)

Fair Surface

Command group: SURFACE › MODIFY

Read the solid features guide.

ParameterTypeRequiredDescription
bodysurface nameYes
faces[[x,y,z]] points on the faces to fairNodefault: every face
strengthnumberNo0..1 of the full fairing step, default 1
iterationsnumberNo1..1000 steps, default 1
keep_edgesboolNodefault true: hold the boundary row and the row next to it, so a face keeps meeting its neighbours in position and in tangent
nameNo
Automation details

Command ID: surface.fair

Parameters:

body: surface name; faces?: [[x,y,z]] points on the faces to fair (default: every face); strength?: number (0..1 of the full fairing step, default 1); iterations?: number (1..1000 steps, default 1); keep_edges?: bool (default true: hold the boundary row and the row next to it, so a face keeps meeting its neighbours in position and in tangent); tolerance?: expr mm (how far a control vertex may move, default 0.001 of the body's size). Moves the interior control vertices of each B-spline face to flatten its curvature variation while the boundary stays put: the face keeps its edges and the body its shape within the tolerance, and the warning in the reply carries the energy before and after; name?

Surface Fillet

Command group: SURFACE › MODIFY

Read the solid features guide.

ParameterTypeRequiredDescription
plannedaYes
b{body, point}Yestwo faces of separate bodies that meet or cross; the points pick the side the ball rolls on
radiusexprYes
trimboth|first|noneNo
Automation details

Command ID: surface.fillet

Parameters:

planned: a, b: {body, point} (two faces of separate bodies that meet or cross; the points pick the side the ball rolls on); radius: expr; trim?: both|first|none

Match Surface

Command group: SURFACE › MODIFY

Read the solid features guide.

ParameterTypeRequiredDescription
bodysurface nameYesthe body whose face is rebuilt
faces[[x,y,z]]Nopoints on that face, default: the body's first
targetbody nameYesthe body the seam must meet
edges[[x,y,z]]Yespoints on one edge chain of the target: the rebuilt boundary takes its place
orderg1|g2|g3Nodefault g2: position and tangent plane, the curvature too, or how that curvature changes along the seam
Automation details

Command ID: surface.match

Parameters:

body: surface name (the body whose face is rebuilt); faces?: [[x,y,z]] (points on that face, default: the body's first); target: body name (the body the seam must meet); edges: [[x,y,z]] (points on one edge chain of the target: the rebuilt boundary takes its place); order?: g1|g2|g3 (default g2: position and tangent plane, the curvature too, or how that curvature changes along the seam). The far boundary stays where it was and the body keeps its name

SHEET METAL › CREATE

Flange

Command group: SHEET METAL › CREATE

Create a sheet metal base, bend an existing edge into a wall, or build a contour from an open sketch. The sheet metal rule supplies the material thickness and bend settings so the folded and flat versions describe the same part.

Before you start

Flange typeWhat you need to selectResult
BaseA closed region in a finished sketch.A new flat sheet metal body.
EdgeOne or more supported edges of an existing sheet metal body.Bent walls along those edges. A sketch is not required for this mode.
ContourAn open chain of straight sketch lines.A sheet following that outline, extended by the chosen distance.

Choose or create a Sheet Metal Rule before building the part. To work from an ordinary solid plate, use Convert to Sheet Metal first. An arbitrary solid edge does not become a sheet metal edge just by selecting Flange.

Options

ChoiceMeaning
TypeBase, Edge or Contour. The selection determines which input is appropriate.
RuleSets thickness, bend radius, K-factor and the gaps/reliefs that keep bends manufacturable. Change thickness in the rule, not as a flange height.
HeightSets the edge flange’s height. Check the height reference and preview when matching a drawing dimension.
AngleSets how far the edge bends; 90° makes a right-angle wall.
Per-edge angle and heightLets selected edges in one feature have different dimensions. Empty values use the common setting.
PositionPlaces the flange relative to the selected edge. The preview shows which material remains inside or outside the original outline.
FlipBends toward the other side of the sheet.
Corner Relief Shape and sizeUse the rule’s setting or override relief between edge flanges of this feature. These overrides are not available for base or contour flanges.
DistanceSets the length of a contour flange’s extrusion along the open sketch.

Try it: two sides of a tray

  1. Set a sheet rule with 1.5 mm thickness and 1.5 mm bend radius.
  2. Draw and finish an 80 × 50 mm rectangular sketch.
  3. Choose Flange, select the rectangle’s region and make a Base flange.
  4. Choose Flange again, select an edge and set its height to 20 mm.
  5. Add another 20 mm flange on the opposite edge. Check the bend directions before confirming each feature.
  6. Use Create Flat Pattern to inspect the developed part and bend lines.

Tips and common mistakes

  • Do use one edge flange feature for adjoining walls whose shared corners should close together. Flanges made as separate features keep their own ends.
  • Do inspect warnings and the flat pattern; a relief that cannot fit may be left out with a warning.
  • Don’t confuse height with sheet thickness. The rule owns thickness.
  • Don’t select a sketch point for an edge flange: choose an edge of the sheet. For a base flange, select a closed sketch region instead.

Examples and community resources

ResourceWhat to exploreAuthor and licence
Editable sheet metal demoThe rule, base sketch and two edge flanges used in this illustration.SolidCraft contributors; MIT OR Apache-2.0.

Contribute a tip, demo, model or screenshot, explaining the edge or region that must be selected.

See also

Hem returns an edge back toward the sheet. Fold bends a sheet along a chosen line. Sheet Metal Rules controls thickness and bend settings. Create Flat Pattern shows the developed part.

ParameterTypeRequiredDescription
basesketch + profiles?Yesrule?, flip?
contoursketch + curvesYesopen chain of lines and tangent arcs
angleNodefault 90 deg
radiusNodefault: rule
height_fromouter|inner|tangentNowhere the height is measured from: the outer virtual sharp (default), the inner virtual sharp, or past 90 deg the line square to the flange touching the bend's outside (tangent; up to 90 deg the outer sharp)
positioninside|outside|middle|material_outside|tangentNowhere the bend starts: the outer virtual sharp on the edge (default), the bend starting at the edge (bend_outside is the same), half way, the inner virtual sharp on the edge, or the bend's outer face tangent to the edge's face (below 90 deg the bend's end on that face)
flipNo
bodyNo
corner_relief_shapenotch|round|rectangular|tear and corner_relief?Nosize across
typebase|edge|contourNodefault from the inputs
Automation details

Command ID: sheet.flange

Parameters:

base: sketch + profiles? (rule?, flip?) | contour: sketch + curves (open chain of lines and tangent arcs) + distance (rule?, flip?, reverse?) | edge: edges: [[x,y,z] on top/bottom edges, or {point: [x,y,z], angle?, height?, segments?} for an edge with its own angle and height, or split into 1…64 segments [{from, to, angle?, height?, flip?}] that tile the stretch the sheet still has there (from, to are fractions 0…1 of it, first from 0, last to 1; consecutive segments that match make one bend)] + height (along the flange, to its far edge), angle? (default 90 deg), radius? (default: rule), height_from?: outer|inner|tangent (where the height is measured from: the outer virtual sharp (default), the inner virtual sharp, or past 90 deg the line square to the flange touching the bend's outside (tangent; up to 90 deg the outer sharp)), position?: inside|outside|middle|material_outside|tangent (where the bend starts: the outer virtual sharp on the edge (default), the bend starting at the edge (bend_outside is the same), half way, the inner virtual sharp on the edge, or the bend's outer face tangent to the edge's face (below 90 deg the bend's end on that face)), flip?, body?, corner_relief_shape?: notch|round|rectangular|tear and corner_relief? (size across): the relief at the corners these flanges make (default: the rule's); then?: [{angle?, height, flip?}] further bends (at most 16), each a flange on the far edge of the one before, flip: true turns that bend the other way (edge only); type?: base|edge|contour (default from the inputs); name?, body_name? (the new body). Thickness, K-factor and bend radius come from the rule (sheet.manage_rules); passing them here is refused, and so is the corner relief on a base or contour flange

Path Flange

Command group: SHEET METAL › CREATE

Read the sheet metal guide.

ParameterTypeRequiredDescription
edge[x,y,z] on a top or bottom edge of the sheetYes
radiusNodefault: rule
Automation details

Command ID: sheet.path_flange

Parameters:

edge: [x,y,z] on a top or bottom edge of the sheet; sketch + curves: an open chain of sketch lines drawn on a plane across that edge, from the edge out (each line one flange as long as the line, to the outer face from the virtual sharp; each corner one bend, turning the way the lines turn); radius? (default: rule); body?. One feature, one body

Perforate

Command group: SHEET METAL › CREATE

Read the sheet metal guide.

ParameterTypeRequiredDescription
diameterexprYes
pitchexprYes
marginexprNoclear of edges, cut-outs, bends and the outline; default: the diameter
patternstaggered| squareNo60°, default
bodyNo
Automation details

Command ID: sheet.perforate

Parameters:

diameter, pitch: expr; margin?: expr (clear of edges, cut-outs, bends and the outline; default: the diameter); pattern?: staggered (60°, default) | square; body?; sketch? + profiles? (the outline: closed profiles of a sketch on a face of the sheet; holes only inside them, out of their inner loops) - a perforation field over the whole flat sheet or inside the outline: holes in the flat pattern (perforations), the DXF (layer PERFORATION) and the measured volume, not cut into the B-rep (#732)

Hem

Command group: SHEET METAL › CREATE

Fold a sheet metal edge back through a half turn to form a return. Use a hem when you need a doubled-over edge, rather than an upright wall.

Before you start

  • You need an existing sheet metal body and a supported free sheet edge. Make a base flange or convert a suitable solid plate first.
  • Select the edge, not a sketch line, face or point. Hem does not require a new sketch. A bend along a line inside the sheet uses Fold instead.
  • Leave room for the return so it does not run into another wall or return.

Options

ChoiceMeaning
EdgesThe sheet edges to turn back. Check that the preview changes the intended edge.
LengthThe return’s length, measured to its outer face. It is not the thickness of the sheet.
GapControls the inner radius of the return bend. Leave it empty to use the sheet rule’s hem setting.
FlipTurns the return toward the other side of the sheet.

Try it: a returned edge

  1. Create an 80 × 50 mm base flange in 1.5 mm sheet.
  2. Choose Hem and select a free 80 mm edge of the sheet.
  3. Set Length to 8 mm and Gap to 1.5 mm.
  4. Check which side receives the return; use Flip if necessary, then confirm.
  5. Inspect Create Flat Pattern to see the material needed for the bend.

Tips and common mistakes

  • Do check the bend in the preview: an edge returning toward another feature needs enough clearance.
  • Don’t draw a sketch line to tell Hem where to bend. Its input is an existing sheet edge; use Fold for a bend line through a panel.
  • Don’t use Hem for an ordinary right-angle wall. That is an edge Flange.

Examples and community resources

ResourceWhat to exploreAuthor and licence
Editable hem demoThe sheet rule, base flange and 8 mm return used in this illustration.SolidCraft contributors; MIT OR Apache-2.0.

Contribute a tip, demo, model or screenshot, explaining the selected edge and dimensions.

See also

Flange makes a base sheet or an angled wall. Fold bends along a line in the sheet. Sheet Metal Rules sets the default hem gap. Create Flat Pattern shows the developed part and bend lines.

ParameterTypeRequiredDescription
edges[[x,y,z] on sheet edges]Yes
lengthYesto the outer face
gapNoinner radius, default: rule
flipNo
bodyNo
Automation details

Command ID: sheet.hem

Parameters:

edges: [[x,y,z] on sheet edges]; length (to the outer face); gap? (inner radius, default: rule); flip?; body?

Fold

Command group: SHEET METAL › CREATE

Read the sheet metal guide.

ParameterTypeRequiredDescription
linesketch + curveYesa sketch line on the sheet's base face; followed when the sketch changes
points[[x,y,z], [x,y,z]]Yes
angleNodefault 90 deg
radiusNodefault: rule
positioncenterline|start|end|mouldNowhere the line sits in the bend, default centerline
fixed[x,y,z] on the side that staysNodefault: the larger side
flipNofold the other way
bodyNo
Automation details

Command ID: sheet.fold

Parameters:

line: sketch + curve (a sketch line on the sheet's base face; followed when the sketch changes) or points: [[x,y,z], [x,y,z]]; angle? (default 90 deg); radius? (default: rule); position?: centerline|start|end|mould (where the line sits in the bend, default centerline); fixed?: [x,y,z] on the side that stays (default: the larger side); flip? (fold the other way); body?

Convert to Sheet Metal

Command group: SHEET METAL › CREATE

Read the sheet metal guide.

ParameterTypeRequiredDescription
bodyYes
ruleNo
bends[[x,y,z] on sharp edges]Noa solid or a shelled body: `face` is the fixed face and the flat faces reached from it across these edges become the sheet; a shell keeps its thickness, a solid takes the rule's inward; edges between two of those faces that are no bends are cut open with the rule's gap; a point in the middle of a chamfer between two faces, or on a bend modelled with its radius (a cylinder between two flat faces tangent to it, either skin), makes it one bend of the rule's radius at their virtual sharp; the faces' holes become cut-outs and their engravings, up to half the thickness deep, pockets
Automation details

Command ID: sheet.convert

Parameters:

body; face: [x,y,z] on its large flat face (a plate of even thickness; recesses in one face up to half the thickness deep, such as an engraved label, become pockets of the sheet), or that face's name from model.faces; rule?; bends?: [[x,y,z] on sharp edges] (a solid or a shelled body: `face` is the fixed face and the flat faces reached from it across these edges become the sheet; a shell keeps its thickness, a solid takes the rule's inward; edges between two of those faces that are no bends are cut open with the rule's gap; a point in the middle of a chamfer between two faces, or on a bend modelled with its radius (a cylinder between two flat faces tangent to it, either skin), makes it one bend of the rule's radius at their virtual sharp; the faces' holes become cut-outs and their engravings, up to half the thickness deep, pockets)

Create Flat Pattern

Command group: SHEET METAL › CREATE

Read the sheet metal guide.

ParameterTypeRequiredDescription
bodyNo
Automation details

Command ID: sheet.flat_pattern

Parameters:

body? → flat size, outline loops and cut-outs (flat coordinates, as point lists), cutout_shapes (the same cut-outs exactly, in the same order: {circle: {center, radius}} for a round hole, else {segments: [{line: [a, b]} | {arc: {center, radius, start_deg, sweep_deg}} | {polyline: [...]}]}), pockets (with their own cutout_shapes), bend lines and the bend table

SHEET METAL › MODIFY

Unfold

Command group: SHEET METAL › MODIFY

Read the sheet metal guide.

ParameterTypeRequiredDescription
bodyNodefault: the last sheet body
Automation details

Command ID: sheet.unfold

Parameters:

body? (default: the last sheet body) — all bends

Refold

Command group: SHEET METAL › MODIFY

Read the sheet metal guide.

ParameterTypeRequiredDescription
bodyNodefault: the last unfolded sheet
Automation details

Command ID: sheet.refold

Parameters:

body? (default: the last unfolded sheet)

Sheet Metal Rules

Command group: SHEET METAL › MODIFY

Read the sheet metal guide.

ParameterTypeRequiredDescription
nameYesnew or existing
thicknessNo
k_factorNo
bend_radiusNo
relief_widthNo
relief_depthNo
corner_reliefNo
hem_gapNo
gapNoexpressions; `Thickness` is the rule's; gap: between flanges that meet at a corner, whose ends are closed toward each other at any angle
corner_relief_shapenotch|round|rectangular|tearNowhat is cut where two bends meet at a corner of the base: round and rectangular a relief `corner_relief` across; tear none, each bend's end is cut straight from the corner to its flange's end; default notch
angle_displaybend|includedNohow the DXF bend notes read the bend angles: the bend angle, default, or the included angle left between the bend's sides, 180° less it; the bend table gives both, and a flange's `angle` is always the bend angle
activeboolNo
deleteboolNo
Automation details

Command ID: sheet.manage_rules

Parameters:

name (new or existing); thickness?, k_factor?, bend_radius?, relief_width?, relief_depth?, corner_relief?, hem_gap?, gap? (expressions; `Thickness` is the rule's; gap: between flanges that meet at a corner, whose ends are closed toward each other at any angle); corner_relief_shape?: notch|round|rectangular|tear (what is cut where two bends meet at a corner of the base: round and rectangular a relief `corner_relief` across; tear none, each bend's end is cut straight from the corner to its flange's end; default notch); angle_display?: bend|included (how the DXF bend notes read the bend angles: the bend angle, default, or the included angle left between the bend's sides, 180° less it; the bend table gives both, and a flange's `angle` is always the bend angle); active?: bool; delete?: bool

WELDMENTS › CREATE

Frame Member

Command group: WELDMENTS › CREATE

ParameterTypeRequiredDescription
sketchthe frame sketchYesid or name; 2D or 3D
lines[curve ids]Noone member each; default: every straight line drawn in the sketch: not construction, not projected or otherwise linked, not of no length
anchororigin|center|centroid|top_left|top|top_right|left|right|bottom_left|bottom|bottom_rightNothe profile point that sits on each line, default origin
locate[x,y]Nothe profile point that sits on each line, in mm from the profile's own origin: a bar's or an angle's bottom-left corner, a channel's web back at mid-height, the centre of other profiles; takes precedence over `anchor` and keeps the member attached at that point when the profile size changes
angleangle exprNothe profile turned about the line; default 0: its y as near world up as the line allows, a vertical member's x along world X
mirrorboolNothe profile mirrored across its y axis
nameNo
body_nameNowhat the members are named after, then numbered: `Rail`, `Rail (2)`…; default the size's name, `RSC 203x89`, the profile's description, `Rect Tube 40x40x3`, or the profile sketch's name
Automation details

Command ID: frame.member

Parameters:

sketch: the frame sketch (id or name; 2D or 3D); lines?: [curve ids] (one member each; default: every straight line drawn in the sketch: not construction, not projected or otherwise linked, not of no length); profile: {kind: bar|round_bar|rect_tube|round_tube|angle|channel|i_beam, …its dimensions: each a number in the design's units or a length expression over its parameters ("tube_wall", "2 in"); a channel's flange_slope is an angle (a number of degrees)} (frame.profile.list gives each kind's dimensions and an example; dimensions left out take defaults from the profile's size) | {size: a standard steel size by name, such as "RSC 203x89", "PFC 200x90x30", "RSA 50x50x5", "SHS 100x100x5", "RHS 200x100x6.3" or "CHS 42.4x3.2" (frame.profile.list lists them), alone: it brings its dimensions} | profile_sketch: a sketch whose profile is the section, in its own x and y around its origin, with profiles?: picks as solid.extrude takes them (default: its material, nested loops alternating, so two circles make a tube); anchor?: origin|center|centroid|top_left|top|top_right|left|right|bottom_left|bottom|bottom_right (the profile point that sits on each line, default origin); locate?: [x,y] (the profile point that sits on each line, in mm from the profile's own origin: a bar's or an angle's bottom-left corner, a channel's web back at mid-height, the centre of other profiles; takes precedence over `anchor` and keeps the member attached at that point when the profile size changes); angle?: angle expr (the profile turned about the line; default 0: its y as near world up as the line allows, a vertical member's x along world X); mirror?: bool (the profile mirrored across its y axis); name?; body_name? (what the members are named after, then numbered: `Rail`, `Rail (2)`…; default the size's name, `RSC 203x89`, the profile's description, `Rect Tube 40x40x3`, or the profile sketch's name); names?: {line id: name} (names to keep, as Edit Feature keeps them). One body per line, from its start to its end; each member keeps its name when other lines are added or deleted. Returns the feature and the bodies it made

End Cap

Command group: WELDMENTS › CREATE

ParameterTypeRequiredDescription
thicknesslength in design units or a parameter expressionYes
ratiothe inset as a share of the thicknessNogive offset or ratio, not both; neither: the plate is flush with the outside of the profile
chamfercorner chamfer length along each edge from the cornerNodefault 0; a rounded corner needs at least its rounding
Automation details

Command ID: frame.end_cap

Parameters:

faces: [{face:[x,y,z],body,index,path?}] planar end faces of frame members, picked in world mm (1–256); thickness: length in design units or a parameter expression; offset?: inset of the plate from the outside of the profile, a length; ratio?: the inset as a share of the thickness (give offset or ratio, not both; neither: the plate is flush with the outside of the profile); chamfer?: corner chamfer length along each edge from the corner (default 0; a rounded corner needs at least its rounding); direction?: outward|inward (default outward: past the end; inward: the member is cut back by the thickness and the cap takes its place). One separate body per face, named `<member> End Cap`; the plate is the profile's outer outline on a closed section (tube, pipe, bar), its outside on an open one (angle, channel, beam, slotted extrusion): the edges on the rectangle round the profile with their rounds and chamfers, inward stretches closed along the rectangle. Common name/body_names fields supported.

PLASTIC › CREATE

Boss

Command group: PLASTIC › CREATE

Read the plastic parts guide.

ParameterTypeRequiredDescription
position[x,y,z] on a faceYes
directionNodefault: out of the face
diameterYes
heightYes
hole_diameterNo
hole_depthNodefault: to the face
draftNoangle
filletNoroot radius
ribsNocount
rib_thicknessNo
rib_lengthNoout from the wall
rib_offsetNobelow the top
bodyNo
Automation details

Command ID: plastic.boss

Parameters:

position: [x,y,z] on a face; direction? (default: out of the face); diameter, height; hole_diameter?, hole_depth? (default: to the face); draft? (angle), fillet? (root radius); ribs? (count), rib_thickness?, rib_length? (out from the wall), rib_offset? (below the top); body?

Lip

Command group: PLASTIC › CREATE

Read the plastic parts guide.

ParameterTypeRequiredDescription
face[x,y,z] on the rim face of a shelled bodyYes
widthYes
heightYes
typelip|grooveNo
gapNogroove clearance
sideinside|outsideNowhich rim edge
bodyNo
Automation details

Command ID: plastic.lip

Parameters:

face: [x,y,z] on the rim face of a shelled body; width, height; type?: lip|groove; gap? (groove clearance); side?: inside|outside (which rim edge); body?

Snap Fit

Command group: PLASTIC › CREATE

Read the plastic parts guide.

ParameterTypeRequiredDescription
position[x,y,z] on a faceYes
directionNodefault: out of the face
hook[x,y,z]Yesthe catch's direction
lengthYes
thicknessYes
widthYes
catch_depthYes
catch_lengthYes
base_heightNoextends the base below the face
new_bodyboolNomake a separate body instead of joining
bodyNo
Automation details

Command ID: plastic.snap_fit

Parameters:

position: [x,y,z] on a face; direction? (default: out of the face); hook: [x,y,z] (the catch's direction); length, thickness, width, catch_depth, catch_length; base_height? (extends the base below the face); new_body?: bool (make a separate body instead of joining); body?

Rest

Command group: PLASTIC › CREATE

Read the plastic parts guide.

ParameterTypeRequiredDescription
position[x,y,z] on a faceYesthe rest's centre
directionNodefault: out of the face
widthYesround: the diameter
lengthNoa rectangle, along `along`
along[x,y,z]No
heightYes
draftNoangle; default: the body's plastic rule
thicknessNowall of a hollow rest
bodyNo
Automation details

Command ID: plastic.rest

Parameters:

position: [x,y,z] on a face (the rest's centre); direction? (default: out of the face); width (round: the diameter); length? (a rectangle, along `along`); along?: [x,y,z]; height; draft? (angle; default: the body's plastic rule); thickness? (wall of a hollow rest); body?

PLASTIC › SETUP

Manage Plastic Rules

Command group: PLASTIC › SETUP

Read the plastic parts guide.

ParameterTypeRequiredDescription
nameYeslibrary or new rule; a new one starts from `from`, default ABS (1.5mm)
fromNo
materialNo
thicknessNo
nominal_radiusNo
clearanceNo
knife_edgeNo
reveal_heightNo
thickness_variationNo
draftNo
max_thicknessNo
min_thicknessNo
min_draftNoexpressions; `Thickness` is the rule's
activeboolNorule for bodies without one
deleteboolNoa design rule; a library rule goes back to the library values
Automation details

Command ID: plastic.manage_rules

Parameters:

name (library or new rule; a new one starts from `from`, default ABS (1.5mm)); from?; material?; thickness?, nominal_radius?, clearance?, knife_edge?, reveal_height?, thickness_variation?, draft?, max_thickness?, min_thickness?, min_draft? (expressions; `Thickness` is the rule's); active?: bool (rule for bodies without one); delete?: bool (a design rule; a library rule goes back to the library values)

Assign Plastic Rule

Command group: PLASTIC › SETUP

Read the plastic parts guide.

ParameterTypeRequiredDescription
bodies[names]Yes
rulenameYesempty: no rule
Automation details

Command ID: plastic.assign_rule

Parameters:

bodies: [names]; rule: name (empty: no rule) — plastic features on these bodies take its draft and clearance as defaults

Other commands: sketch

Edit Sketch

ParameterTypeRequiredDescription
sketchid or nameYes
restore_markernonnegative timeline positionNo
nullYesend
Automation details

Command ID: sketch.edit

Parameters:

sketch: id or name; restore_marker?: nonnegative timeline position, null (end) or false. A position or null: temporarily roll to this sketch and restore that position on Finish or Cancel; Finish groups the edit into one Undo step. Omitted: the same with the current position when features come after the sketch, so they rebuild once at Finish rather than after every sketch command. false: leave the timeline in place (later features rebuild after every sketch command)

Draw Dimensioned Sketch Geometry

Automation details

Command ID: sketch.draw.dimensioned

Parameters:

command: sketch creation command, params: object, dimensions: object of role to numeric or expression value, or `name = expression` (also `#name = expression`), which defines the user parameter `name` as sketch.dimension does (the reply's `defined` lists the names) (optional with as_drawn or locate), as_drawn?: bool (also dimension every other role of the shape at its size as drawn; a radius or diameter given takes the place of the other, as an edge polygon's side length does its radius, a circle otherwise takes its diameter, and a size the sketch already determines, such as the angle of a line inferred horizontal, is left out), locate?: bool (then place the shape from the sketch origin until nothing of it is left free: horizontal and vertical distances from the origin to its points, centres first, each kept only where it removes freedom, so a shape anchored to the origin or to placed geometry gets none; a point on an axis goes onto that axis instead of a 0 distance; the reply's located lists them); creates geometry and all driving dimensions atomically in one undo step. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Delete Sketch Entities

ParameterTypeRequiredDescription
entities[curve, point or constraint ids]Yes
Automation details

Command ID: sketch.delete

Parameters:

entities: [curve, point or constraint ids]

Drag Sketch Point

ParameterTypeRequiredDescription
pointrefYes
to[x,y]Yes
Automation details

Command ID: sketch.move_point

Parameters:

point: ref, to: [x,y]

Move Sketch Points

ParameterTypeRequiredDescription
moves[{point: ref | near: [x,y], to: [x,y]}]Yeseach point to its own target; `near`: the sketch point closest to it, refused when none lies within `tolerance` or two are as close
tolerancemmNodefault 0.1
sketchNo
Automation details

Command ID: sketch.move_points

Parameters:

moves: [{point: ref | near: [x,y], to: [x,y]}] (each point to its own target; `near`: the sketch point closest to it, refused when none lies within `tolerance` or two are as close), tolerance?: mm (default 0.1); sketch? → moved: [{point, to, at, reached}] (`at`: where it ended up after the one solve; `reached`: whether that is `to`)

Drag Sketch Geometry

Automation details

Command ID: sketch.drag

Parameters:

entity: point or curve, from: [x,y] where it was grabbed, to: [x,y], live?: bool: points and the material point grabbed on a line move toward `to` within the design constraints. When exact placement is impossible, bounded feasible steps minimize cursor distance. Splines slide by to - from; a circle or arc grabbed on its edge takes the radius to `to`. `with?`: [point or curve ids] moves the rest of a selection with the entity as one translation (circles keep their radii). Long moves retain the solution branch; arcs cannot sweep through their starts. Returns `reached`: progress projected onto the requested move (0..1), `at`: the grabbed position for the next frame, and `retry`: budget-limited progress can continue in another frame. `live: true` shares a work budget across held-solve decomposition and cursor correction; ordinary recovery and the final model evaluation retain their existing limits, not a wall-clock deadline. Later features stay stale until `sketch.drag_end`; `dof` stays from before the drag

End Sketch Drag

Automation details

Command ID: sketch.drag_end

Parameters:

constraint?: {command: a sketch.constraint.* id, params}: ends a drag made of `sketch.drag` `live: true` frames: adds the constraint it let go on (not kept if it over-constrains the sketch), then evaluates the whole model once; returns the constraint's result and the sketch's solved state and degrees of freedom

Move Dimension Text

ParameterTypeRequiredDescription
dimensionconstraint id or parameter nameYes
at[x,y] text centreYessketch coordinates
resettrueYesdefault place
Automation details

Command ID: sketch.dimension_text

Parameters:

dimension: constraint id or parameter name; at: [x,y] text centre (sketch coordinates) | reset: true (default place)

Sketch Palette Options

ParameterTypeRequiredDescription
sketchid|nameNodefault active
Automation details

Command ID: sketch.options

Parameters:

sketch?: id|name (default active); any of show_profile, show_points, show_dimensions, show_constraints, show_projected, show_construction, grid, snap, slice, sketch_3d: bool. Returns them all

Chain

ParameterTypeRequiredDescription
curveid: the curves joined to it end to endYeswhat a double-click selects
Automation details

Command ID: sketch.chain

Parameters:

curve: id: the curves joined to it end to end (what a double-click selects)

Solve Sketch

Automation details

Command ID: sketch.solve

Parameters:

reports the solver status and degrees of freedom

Add Spline Point

Automation details

Command ID: sketch.spline.insert_point

Parameters:

curve: fit point spline id, at: [x,y]: a fit point is inserted where the curve passes nearest to it, between its neighbours (the curve still runs through it and all its old points; constraints on them stay); end?: start|end: `at` becomes the spline's new first or last point instead, and the curve runs on to it (refused when that end is joined to other geometry). Control point splines take no new points yet. Returns point: the new point's id

Remove Spline Point

Automation details

Command ID: sketch.spline.remove_point

Parameters:

curve: spline id, point: one of its points (a point id, or the curve's start or end such as s1.end): the spline runs through (or over) its other points only, and the point goes with the constraints on it unless another curve uses it. Fit point, control point and closed splines. Refused for a closed spline's closing point, for an end joined to other geometry or held by a constraint, when fewer than 2 points would be left (3 on a closed spline), and for a projected, offset or patterned spline. Returns curve, point: the point's id, deleted: whether the point went too

Add Tangent Handles

Automation details

Command ID: sketch.spline.handles

Parameters:

curve: fit point spline id: gives it a tangent handle at each fit point (a free point joined to it by a construction line) where the curve already runs, so its shape does not change until a handle is moved; moving a fit point moves the handles nobody has turned with it. In the app a selected spline shows its handles and the first drag of one adds them. Refused for a spline that has handles already, a control point, projected, offset, patterned or equation spline. Returns curve, handles: the handle point ids in fit point order

Reset Tangent Handles

Automation details

Command ID: sketch.spline.reset_handles

Parameters:

curve: fit point spline id: removes its tangent handles (the handle points, their construction lines and the constraints on them), so the curve runs through its fit points as if the handles had never been moved; selected, it shows them again where they start. Refused for a spline without tangent handles. Returns curve, removed: the removed handle point ids in fit point order

Reverse Spline Direction

ParameterTypeRequiredDescription
curvespline idYesfit point, control point or closed
Automation details

Command ID: sketch.spline.reverse

Parameters:

curve: spline id (fit point, control point or closed): its last point becomes its first and the curve runs the other way along the same path; its points and the constraints on them stay. Refused for a projected spline and for one an offset uses or makes. Returns curve

Infinite Line

ParameterTypeRequiredDescription
curves[line ids]Yes
valueboolNodefault toggles
Automation details

Command ID: sketch.infinite

Parameters:

curves: [line ids], value?: bool (default toggles): infinite line type (#1359): construction geometry drawn across the whole view through the line's two points; off, a normal line

Edit Text

ParameterTypeRequiredDescription
livebool: one frame of an interactive changeNothe sketch is solved, the features after it stay as they were until `sketch.drag_end`
Automation details

Command ID: sketch.edit_text

Parameters:

link: the text's link id (or entity: one of its curves), text? or text_expression? (as in sketch.text; typed words replace the expression), at?, height?, angle?: deg (numbers or expressions as in sketch.text; a number replaces an expression, and so does moving the text), font?: family or file path (empty: the built-in font), bold?, italic?: bool (an installed family's bold or italic face; italic without one is slanted), align?: left|center|right (the lines against `at`, or along the path), vertical_align?: baseline|top|middle|bottom (the block of lines against `at` or the path: the first line's baseline (default), its capitals' tops, halfway between those and the last line's baseline, or the bottom of the last line's descenders), letter_spacing?: extra mm between letters, line_spacing?: times the font's line distance (default 1), path?: a curve id of the sketch the baseline follows ("": none; `at` and `angle` then do not place the text; the curve becomes construction, a guide the letters stand on), reverse?: bool (the other way along the path) (each kept when left out); or edits: [{link or entity, text?, at?, …}]: several texts changed together, the design rebuilt once (all or nothing; returns edits: each one's result); live?: bool: one frame of an interactive change (the sketch is solved, the features after it stay as they were until `sketch.drag_end`). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Edit Equation Curve

ParameterTypeRequiredDescription
curvean equation curve's idYes
Automation details

Command ID: sketch.equation_curve.edit

Parameters:

curve: an equation curve's id; x?, y?, explicit?, start?, end?, tolerance?, variable_unit?: as in sketch.equation_curve, each kept when left out (explicit: true drops x; false needs x). The points are chosen again for the new formulas; the curve's start point (and the end point of an open curve) keeps its id, so geometry joined there stays joined. Returns curve, points, closed, deviation

Toggle Driven Dimension

ParameterTypeRequiredDescription
constraintdimension idYes
drivenboolNodefault toggles
Automation details

Command ID: sketch.toggle_driven

Parameters:

constraint: dimension id, driven?: bool (default toggles); a driving dimension gets a parameter with its current value

Toggle Radius / Diameter

ParameterTypeRequiredDescription
constrainta circle's or an arc's radius or diameter dimension idYes
diameterboolNodefault: the other one
sketchid|nameNodefault: the one being edited
Automation details

Command ID: sketch.toggle_diameter

Parameters:

constraint: a circle's or an arc's radius or diameter dimension id; diameter?: bool (default: the other one); sketch?: id|name (default: the one being edited). The circle stays the same size: its parameter is halved or doubled (a plain value is rewritten, an expression becomes `(e) / 2` or `2 * (e)`); refused when anything else uses that parameter

Constraint Glyphs

ParameterTypeRequiredDescription
sketchidNo
nameevery constraint with where to draw its glyphYessketch coordinates
Automation details

Command ID: sketch.glyphs

Parameters:

sketch?: id|name: every constraint with where to draw its glyph (sketch coordinates)

Snap Inference

ParameterTypeRequiredDescription
at[x,y]Yes
from[x,y]Noprevious click
radiusmmNosnap distance, default 1
Automation details

Command ID: sketch.snap

Parameters:

at: [x,y], from?: [x,y] (previous click), radius?: mm (snap distance, default 1): what a click there would snap to and infer

Drawing Size

ParameterTypeRequiredDescription
pathSVG or DXFYesby `format`: "svg" | "dxf", default by the file name, else SVG
textSVG or DXFYesby `format`: "svg" | "dxf", default by the file name, else SVG
Automation details

Command ID: sketch.drawing_bounds

Parameters:

path | text: SVG or DXF (by `format`: "svg" | "dxf", default by the file name, else SVG); returns min, max, width and height of the drawn geometry in the file's own size, and for an SVG with a viewBox (or width and height) its sheet: {min, max, width, height}

Save As DXF

ParameterTypeRequiredDescription
sketchid|nameNodefault active
pathfile to writeNoelse the DXF text is returned
units"design" | "in" | "inch" | "mm" | "cm" | "m" | "ft"Nodefault mm
layerprofile layer nameNodefault "0"
layers"single" | "roles"Nodefault "single": all on `layer`; "roles": outer on "OUTLINE", holes on "CUT"
joinboolNodefault false: R12 LINE/ARC; true: closed loops as LWPOLYLINE with arc bulges
Automation details

Command ID: sketch.export_dxf

Parameters:

sketch?: id|name (default active); path?: file to write (else the DXF text is returned); units?: "design" | "in" | "inch" | "mm" | "cm" | "m" | "ft" (default mm); layer?: profile layer name (default "0"); layers?: "single" | "roles" (default "single": all on `layer`; "roles": outer on "OUTLINE", holes on "CUT"); join?: bool (default false: R12 LINE/ARC; true: closed loops as LWPOLYLINE with arc bulges)

Loose Ends

ParameterTypeRequiredDescription
sketchid|nameNodefault active
untied[{point, curve, at, touches: {point}|{curve}}] ends that touch another curve only by positionYesthe profile closes now, but nothing keeps it closed when a dimension changes
Automation details

Command ID: sketch.loose_ends

Parameters:

sketch?: id|name (default active): the ends of profile curves (not construction, projected or text) that can make a profile fail. open: [{point, curve, at}] ends that touch no other profile curve (no profile runs through them); untied: [{point, curve, at, touches: {point}|{curve}}] ends that touch another curve only by position (the profile closes now, but nothing keeps it closed when a dimension changes)

Tie Loose Ends

ParameterTypeRequiredDescription
sketchid|nameNodefault active
Automation details

Command ID: sketch.loose_ends.tie

Parameters:

sketch?: id|name (default active); points?: [point ids] (default: every untied end): ties each untied end to what it touches, coincident with another curve's end or on another curve. Returns constraints: [ids added], and the untied ends left

3D Line

Automation details

Command ID: sketch.line3d

Parameters:

points: [[x,y,z]…] world points (2 or more): one 3D line per segment, joined end to end (needs 3D Sketch on). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

3D Spline

ParameterTypeRequiredDescription
points[[x,y,z]…] world fit pointsYes2 or more
Automation details

Command ID: sketch.spline3d

Parameters:

points: [[x,y,z]…] world fit points (2 or more): a smooth 3D curve through them (needs 3D Sketch on). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

3D Arc

Automation details

Command ID: sketch.arc3d

Parameters:

start, through, end: [x,y,z] world points: an exact circular arc through them, from start through through to end (needs 3D Sketch on). Stored exactly (centre, radius, angles), so path consumers can treat it as a true arc. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

3D Point

ParameterTypeRequiredDescription
point[x,y,z] world pointYesneeds 3D Sketch on
Automation details

Command ID: sketch.point3d

Parameters:

point: [x,y,z] world point (needs 3D Sketch on). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Move 3D Point

ParameterTypeRequiredDescription
wiredrawn 3D curve or point idYes
indexfit pointNodefault 0
to[x,y,z]Yes
by[dx,dy,dz]Yes
Automation details

Command ID: sketch.move3d

Parameters:

wire: drawn 3D curve or point id, index?: fit point (default 0), to: [x,y,z] | by: [dx,dy,dz]; points joined to it move too

Break Offset Association

ParameterTypeRequiredDescription
curvederived offset curve idYesretains geometry and makes its points editable
Automation details

Command ID: sketch.offset.break

Parameters:

curve: derived offset curve id (retains geometry and makes its points editable)

Trim Along Path

Automation details

Command ID: sketch.trim.path

Parameters:

points: [[x,y]…] (2…2000 positions: a path drawn across the sketch). Every piece the path crosses is removed as `sketch.trim` removes the piece at that crossing, in the order the path crosses them; a piece crossed again after it is gone is not trimmed twice; eligible single-line offset outputs are trimmed while preserving their distance; projected geometry, infinite lines, other offset outputs and pieces trim refuses are passed over (why in `skipped`). dry_run?: bool (report without changing the design). One undo step → trimmed: [each trim's result], removed: [[[x,y]…]] (each removed piece as a polyline). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

ParameterTypeRequiredDescription
linklink idNoj1
entities[curve or point ids]: linked geometry becomes normal sketch geometryNo
Automation details

Command ID: sketch.break_link

Parameters:

link?: link id (j1) | entities?: [curve or point ids]: linked geometry becomes normal sketch geometry

Auto-Project Edges on Reference

ParameterTypeRequiredDescription
valueboolNodefault toggles
Automation details

Command ID: sketch.auto_project

Parameters:

value?: bool (default toggles): project a face's edges when a sketch is created on it

Redefine Sketch Plane

ParameterTypeRequiredDescription
sketchid or nameYes
planeas sketch.createYes
frameas sketch.createNo
author_pathexact owner occurrence idsNo
Automation details

Command ID: sketch.redefine

Parameters:

sketch: id or name; plane: as sketch.create; frame?: as sketch.create; author_path?: exact owner occurrence ids; explicitly rebinds or freezes the plane while preserving geometry and dimension names

Copy Sketch Entities

ParameterTypeRequiredDescription
entities[curve or point ids]Nodefault: the selected sketch geometry
Automation details

Command ID: sketch.copy

Parameters:

entities?: [curve or point ids] (default: the selected sketch geometry); the constraints and dimensions among them come along

Cut Sketch Entities

Automation details

Command ID: sketch.cut

Parameters:

entities?: [curve or point ids] (default: the selected sketch geometry): copies them as sketch.copy does, then deletes them (one undo step)

Paste Sketch Entities

ParameterTypeRequiredDescription
at[x, y]Nosketch point the copy's centre goes to
translate[x, y]No
sketchtargetNodefault: the active sketch
Automation details

Command ID: sketch.paste

Parameters:

at?: [x, y] (sketch point the copy's centre goes to) | translate?: [x, y]; without either, offset from the original in its own sketch and at the same coordinates in another; sketch?: target (default: the active sketch)

Replace Sketch

ParameterTypeRequiredDescription
sketchthe sketch to replaceNodefault: the active sketch
fromthe sketch whose contents take its placeYesid or name
Automation details

Command ID: sketch.replace

Parameters:

sketch?: the sketch to replace (default: the active sketch); from: the sketch whose contents take its place (id or name); file?: a design file `from` is in (default: this design). The sketch keeps its plane, place in the timeline and the features built on it; its curves, points, constraints, dimensions and texts become a copy of `from`'s, at the same sketch coordinates (one undo step)

Other commands: canvas

Calibrate Canvas

ParameterTypeRequiredDescription
canvasid or nameYes
atwo points on the canvas plane [x,y]Yes
btwo points on the canvas plane [x,y]Yes
distancetheir true distanceYesmm
Automation details

Command ID: canvas.calibrate

Parameters:

canvas: id or name; a, b: two points on the canvas plane [x,y]; distance: their true distance (mm): scales the image about a

Straighten Canvas

ParameterTypeRequiredDescription
canvasid or nameYes
atwo points on the canvas plane [x,y] along a line of the imageYes
btwo points on the canvas plane [x,y] along a line of the imageYes
Automation details

Command ID: canvas.straighten

Parameters:

canvas: id or name; a, b: two points on the canvas plane [x,y] along a line of the image; angle?: deg, the direction that line should have (0 horizontal, the default; 90 vertical): turns the image about a, the shorter way

Delete Canvas

ParameterTypeRequiredDescription
canvasid or nameYes
Automation details

Command ID: canvas.delete

Parameters:

canvas: id or name

Canvas to Sketch

ParameterTypeRequiredDescription
canvasid or nameNodefault: the last canvas
Automation details

Command ID: canvas.to_sketch

Parameters:

canvas?: id or name (default: the last canvas); name?: the new sketch's name. Traces the canvas image (dark drawing on a light background, or the reverse) into a new sketch on the canvas plane, lying exactly on the image: lines, splines and holes

Other commands: pointcloud

Edit Point Cloud

ParameterTypeRequiredDescription
cloudid or nameYes
nameNo
visibleNo
point_sizepixelsNo0.5…16
color_byfile|single|height or a property's nameNothe file's colours, the default; one colour; a ramp by height, blue low to red high; or by any property of the file, a ramp over its values or a palette for whole numbers
color[r, g, b] 0…1Nothe single colour; given alone it sets color_by single, null sets color_by file
height_rangethe same as color_rangeNo
unitsmm|cm|m|in|ftNothe file's length unit: the points are scaled from the unit they came in as
matrix[12 or 16 numbers]Nowhere the cloud sits: a rigid row-major 3x4 or 4x4 matrix, as occurrence.move takes it
toleranceNo
Automation details

Command ID: pointcloud.edit

Parameters:

cloud: id or name; name?; visible?; point_size?: pixels (0.5…16); color_by?: file|single|height or a property's name (the file's colours, the default; one colour; a ramp by height, blue low to red high; or by any property of the file, a ramp over its values or a palette for whole numbers); color?: [r, g, b] 0…1 (the single colour; given alone it sets color_by single, null sets color_by file); color_range?: [low, high] the ramp runs between (design Z in mm by height), or null for the automatic range (by height all but the lowest and highest 2 % of the points; by a property the values it takes over what the filters keep); height_range?: the same as color_range; units?: mm|cm|m|in|ft (the file's length unit: the points are scaled from the unit they came in as); matrix?: [12 or 16 numbers] (where the cloud sits: a rigid row-major 3x4 or 4x4 matrix, as occurrence.move takes it); tolerance?; filters?: [{property, range: [low, high]} or {property, values: [whole numbers]} for a property with named values], or null (only what every filter keeps is shown) → cloud

Reload Point Cloud

ParameterTypeRequiredDescription
cloudid or nameYesits file is read again from where it was inserted; name, placement, units, colouring and the filters on properties it still has are kept; read under the limit it was inserted with, so a file that grew is thinned only past that limit
Automation details

Command ID: pointcloud.reload

Parameters:

cloud: id or name (its file is read again from where it was inserted; name, placement, units, colouring and the filters on properties it still has are kept; read under the limit it was inserted with, so a file that grew is thinned only past that limit) → cloud, points, segments, triangles, warnings

Delete Point Cloud

ParameterTypeRequiredDescription
cloudid or nameYes
Automation details

Command ID: pointcloud.delete

Parameters:

cloud: id or name → deleted

Point Clouds

Automation details

Command ID: pointcloud.list

Parameters:

→ clouds: [{cloud, name, file, source (the file's path, read again by pointcloud.reload), points, segments, triangles, source_points, units, visible, point_size, color_by (file, single, height or a property's name), color, colored, properties: [{name, integer, span, names}], filters, color_range (the ramp's now, exact over what the filters keep), height_range (the ramp's own range or the automatic one by height), height_auto, bounds: [[x,y,z], [x,y,z]] in the design, matrix}]

Other commands: inspect

View Analysis

ParameterTypeRequiredDescription
offtrue turns the view analysis on now off.No
Automation details

Command ID: inspect.analysis

Parameters:

off?: true turns the view analysis on now off. → active: the command id of the view analysis on (inspect.zebra, inspect.draft, inspect.curvature_map, inspect.environment_map, inspect.accessibility, inspect.continuity, inspect.isocurve or inspect.curvature_comb), or null. One is on at a time: turning one on turns the one on before off. Read-only without `off`

Clearance

ParameterTypeRequiredDescription
bodies[names]Nodefault: every solid body, in world placement, but those in `against`; mesh bodies are not measured
against[names]Noonly pairs with one body in `bodies` and the other here
min_volumemm³Nodefault 0.01: smaller overlaps count as contact
clearancemmNodefault 0: report pairs closer than this
nearestnNoeach body of `bodies` gets its n nearest others
skip_within"occurrence"| "component"| nullNopairs of bodies of one occurrence; pairs of bodies inside one placed component, its subcomponents included
tolerancemmNodefault 1e-6: a gap below it is contact, 0
Automation details

Command ID: inspect.clearance

Parameters:

bodies?: [names] (default: every solid body, in world placement, but those in `against`; mesh bodies are not measured); against?: [names] (only pairs with one body in `bodies` and the other here); min_volume?: mm³ (default 0.01: smaller overlaps count as contact); clearance?: mm (default 0: report pairs closer than this); nearest?: n (each body of `bodies` gets its n nearest others); skip_within?: "occurrence" (pairs of bodies of one occurrence) | "component" (pairs of bodies inside one placed component, its subcomponents included) | null; tolerance?: mm (default 1e-6: a gap below it is contact, 0) → overlaps: [{a, b, volume_mm3, area_mm2, bbox, center}]; near: [{a, b, distance_mm, from, to, overlap_mm3?: a contact's shared volume below min_volume, exact?: false when measured on the meshes}], nearest by body (the same for pairs that overlap: 0); nearest: {body: [{body, distance_mm}]}; unchecked: [{a, b, reason}] (pairs whose overlap could not be worked out; each mesh body, STL/3MF/OBJ import, is not measured and is listed as {a: name, b: null, reason: "mesh body: not checked"}, also when named in `bodies` or `against`; the result's `bodies` counts the solid bodies only); pairs_checked; pairs_reused (of those, measured by an earlier call: neither body changed or moved since); pairs_pruned (ruled out by their boxes); ms. Distances are measured on the exact faces and edges, the meshes only rule pairs out

Parameter Sweep

ParameterTypeRequiredDescription
parameternameYes
values[number | expression]| fromYesat most 200
tonumbersYes
stepsNodefault 10, at most 199: steps + 1 values
Automation details

Command ID: inspect.parameter_sweep

Parameters:

parameter: name; values: [number | expression] (at most 200) | from, to: numbers, steps? (default 10, at most 199: steps + 1 values) → for each value, in a copy of the design (the design itself is not changed): {value, ms (the rebuild), ok, failed: [{feature, name, error}], warned: [{feature, name, warning}] (features that build at the current value and fail or warn at this one), joints_lost: [{joint, name, origin?: a|b, why}], joints_flipped: [{joint, name, origin: a|b, degrees}] (an origin's z turned more than 90° on its own part from where it is now), sketches_flipped: [{sketch, name, degrees}] (the sketch plane's normal turned more than 90°), sketches_failed: [{sketch, name, failing, folded}] (a solve that fails, or keeps a triangle's turn or an offset's side only by flipping it)} (or {value, ms, ok: false, error} where the parameter takes no such value); summary: {values, ok, slowest_ms, failed, joints_lost, joints_flipped, sketches_flipped, sketches_failed} (how many values show each)

Compare Parameter Variants

Automation details

Command ID: inspect.parameter_variants

Parameters:

variants: [{name, set: {parameter: number | expression}}] (1 to 12; up to 16 existing parameters each). Rebuild each set together in a separate copy of the open design. Returns baseline and ordered results with volume, mass, body counts and rebuild regressions. No edits are kept.

Compare Bodies

ParameterTypeRequiredDescription
tolerancemmNo0.001 to 10, default 0.01
max_regionsNodefault 50
colortrue | {only_in_a?, only_in_b?: "#rrggbb"}Nocolours the faces that hold a region, red where only a has material and green where only b has: the faces mostly beyond the tolerance, else the one with the largest share; an undo step
Automation details

Command ID: inspect.compare

Parameters:

a, b: body names (default: the two selected bodies, or the design's only two), in world placement; tolerance?: mm (0.001 to 10, default 0.01): surfaces closer than this agree; spacing?: mm (default 0.5): the largest piece of a face sampled as one point, coarser for a body that would need more than max_points? (default 8 million, up to 50 million) samples; max_regions? (default 50); color?: true | {only_in_a?, only_in_b?: "#rrggbb"} (colours the faces that hold a region, red where only a has material and green where only b has: the faces mostly beyond the tolerance, else the one with the largest share; an undo step) → a, b: volume_mm3, area_mm2, center_of_mass, bbox; difference: b − a; deviation: a_to_b, b_to_a (max_mm, mean_mm, at, area_beyond_mm2); regions, largest first: side (only_in_a | only_in_b), volume_mm3, center and bbox (measured_by: "boolean": exact, the pieces of a − b or b − a there, measured as inspect.measure does; "not measured": the cut could not be made, so volume_mm3 and center are null and not_measured says why, never an estimate), area_mm2, max_deviation_mm, at, faces_a, faces_b (face indices, as model.faces lists them); identical; verdict; warnings (a body whose surface does not enclose what it measures, lines left out, a cut that could not be made). The regions are found from the two surfaces, so near-identical bodies compare too

Compare Picture

ParameterTypeRequiredDescription
referencepath to a PNG or JPEG the model should matchYes
crop_px[x, y, w, h] in pixelsNo
crop_fraction[x, y, w, h] as fractions 0..1 of the pictureNoto take one view off a board
overlayfalse leaves the overlay PNG outNo
Automation details

Command ID: inspect.compare_picture

Parameters:

reference: path to a PNG or JPEG the model should match; view?/camera?/bodies?/rows? as inspect.silhouette; crop_px?: [x, y, w, h] in pixels, or crop_fraction?: [x, y, w, h] as fractions 0..1 of the picture (to take one view off a board); part_fills_crop?: false — with true the crop box is the part itself and no background search runs, for a crop that tightly frames the part; fit?: "width" | "height" | "both" (default "width"): one uniform scale, taken from the named dimension ("both" claims both and scales by width, reporting aspect_mismatch when the shapes differ); overlay?: false leaves the overlay PNG out → overlap (0..1), edges: top|bottom|left|right in mm at nine stations (positive where the model reaches past the picture, null where a station misses one of the two), the grid and its mm_per_px, and overlay_png (red model, blue picture) as base64 PNG. The compare is outline only: holes are not compared — both the picture's and the model's holes are filled

Silhouette

ParameterTypeRequiredDescription
viewfront|back|left|right|top|bottom|isoNodefault front
camera{target: [x,y,z], yaw, pitch, distance, fov?}Nomm, radians; fov 0 means parallel
bodies[world body names]Nodefault visible bodies, named hidden bodies included
rowsmask height in pixelsNodefault 240, clamped to 16..1024
Automation details

Command ID: inspect.silhouette

Parameters:

view?: front|back|left|right|top|bottom|iso (default front); or camera?: {target: [x,y,z], yaw, pitch, distance, fov?} (mm, radians; fov 0 means parallel); bodies?: [world body names] (default visible bodies, named hidden bodies included); rows?: mask height in pixels (default 240, clamped to 16..1024) → tightly framed run-length mask, box_mm, mm_per_px, right/up, projection, body and triangle counts

Other commands: solid

Cell Grid

ParameterTypeRequiredDescription
sizeexprYesa cell's inscribed diameter
webexprYesthe wall between cells
depthexprYesor through_all: true
shapehexagon|square|triangle|circle|polygonNodefault hexagon
sidesexprNopolygon cells, 3 to 64; default 8
dot_gridhexagonal|squareNowhere circle and polygon cells sit; default hexagonal
angleexprNothe grid's rotation about the anchor; angle 45 deg with square cells gives a diamond layout
anchor[x,y,z]Noa cell's centre, projected onto the sketch plane; default the sketch origin: grids with the same anchor, angle, size and web line up
clipwhole|snap|freeNocells the field's edge crosses: only whole cells; cells cut along the grid's own lines; cells trimmed to the edge as drawn; default snap
directionpositive|negative|symmetricNodefault positive
sketchid|nameNodefault: active or last sketch; its closed profiles hold the field: a loop inside another is a keep-out
profilesas solid.extrudeNo
targets[body]No
modecutNothe only mode so far
Automation details

Command ID: solid.cell_grid

Parameters:

size: expr (a cell's inscribed diameter); web: expr (the wall between cells); depth: expr (or through_all: true); shape?: hexagon|square|triangle|circle|polygon (default hexagon); sides?: expr (polygon cells, 3 to 64; default 8); dot_grid?: hexagonal|square (where circle and polygon cells sit; default hexagonal); angle?: expr (the grid's rotation about the anchor; angle 45 deg with square cells gives a diamond layout); anchor?: [x,y,z] (a cell's centre, projected onto the sketch plane; default the sketch origin: grids with the same anchor, angle, size and web line up); clip?: whole|snap|free (cells the field's edge crosses: only whole cells; cells cut along the grid's own lines; cells trimmed to the edge as drawn; default snap); direction?: positive|negative|symmetric (default positive); sketch?: id|name (default: active or last sketch; its closed profiles hold the field: a loop inside another is a keep-out); profiles?: as solid.extrude; targets?: [body]; mode?: cut (the only mode so far); name?. Cuts at most 250 cells in one operation; not for sheet metal (use sheet.perforate). The faces cut have no stable names yet

Other commands: mesh

Convert Mesh

ParameterTypeRequiredDescription
bodymesh body nameNodefault: the selected mesh body
nameNo
Automation details

Command ID: mesh.convert

Parameters:

body?: mesh body name (default: the selected mesh body); tolerance?: length expression (default 0.05 mm): rebuild a closed mesh as an editable solid — a constant-section part as planar and cylindrical faces, any other closed mesh as the faceted solid its triangles describe — with a parametric timeline feature in the mesh body's owning component (the active component must be that component or an ancestor; otherwise activate the named component first); the active component is unchanged; name?

Other commands: model

Read Body Mesh

ParameterTypeRequiredDescription
bodyfinite positive chord tolerance in mmYes
tolerancefinite positive chord tolerance in mmNodefault 0.05
Automation details

Command ID: model.mesh

Parameters:

body, tolerance?: finite positive chord tolerance in mm (default 0.05); evaluated component-local mesh, at most 8192 vertices and 16384 triangles; no files or model changes

List Edges

ParameterTypeRequiredDescription
bodynameYes
Automation details

Command ID: model.edges

Parameters:

body: name

List Faces

ParameterTypeRequiredDescription
bodynameYes
Automation details

Command ID: model.faces

Parameters:

body: name → per face: index, name (what face.delete, sheet.convert `face`, solid.shell … take in place of a point), exact, area, centroid (not always on the face), plane_normal, point (a point on the face that picks it), cylinder? ({origin, axis, radius} of a cylindrical face), sheet_face? (a folded sheet's face: `sheet:<body>:<panel>:<side>`, panel base or a flange's tag F<feature>.<k>, side top | bottom | rim.<edge> | bend.inner | bend.outer; taken wherever a face name is, and it holds while sizes change and flanges come and go)

List Threads

Automation details

Command ID: model.threads

No parameter description is provided by the command registry.

List Body Features

ParameterTypeRequiredDescription
bodynameNoa placed copy's name means the body it shows; default every body
Automation details

Command ID: model.body_features

Parameters:

body?: name (a placed copy's name means the body it shows; default every body) → bodies: [{body, features: [{id, name}]}], in timeline order the feature that created each body, every later one that changed it, and those of bodies consumed into it. Read-only

Other commands: plugin

Inspect Plugin

ParameterTypeRequiredDescription
pathplugin package folderYes
Automation details

Command ID: plugin.inspect

Parameters:

path: plugin package folder; returns inert manifest and approval hash covering manifest and component; runs no plugin code

Install Plugin

ParameterTypeRequiredDescription
pathplugin .zipYes
replaceboolNorequired to replace an installed plugin of the same id
dry_runboolNo
Automation details

Command ID: plugin.install

Parameters:

path: plugin .zip, replace?: bool (required to replace an installed plugin of the same id), dry_run?: bool; checks the .zip (one plugin, safe paths, size caps, manifest) and unpacks it into the plugins folder; runs no plugin code

List Plugins

Automation details

Command ID: plugin.list

Parameters:

List inspected and installed plugin manifests, namespaced entries, declared effects and input schemas; no plugin code runs

Run Plugin

ParameterTypeRequiredDescription
pathYes
approvalexact hash from plugin.inspect after reviewing permissionsYes
entryYes
paramsJSONNo
Automation details

Command ID: plugin.run

Parameters:

path, approval: exact hash from plugin.inspect after reviewing permissions, entry, params?: JSON; explicit native Wasm component execution, one undo step; errors discard all edits

Other commands: sheet

List Sheet Metal Rules

Automation details

Command ID: sheet.rules

Parameters:

→ rules with their expressions and values, and the active one

Export Flat Pattern as DXF

ParameterTypeRequiredDescription
bodyNo
pathNo
curvesexact|arcsNodefault exact: an ellipse as ELLIPSE, splines, text outlines and other Béziers as SPLINE; arcs: tangent arcs and lines within 0.001 mm, for machines that read only those
textcut|etchNodefault cut: letters that text cut through the sheet are cut contours on layer TEXT; etch: single contours on layer ETCH and the sheet keeps its material there; text machined to a depth is always on ETCH
bend_notesboolNodefault false: a note beside each bend line on layer BEND_NOTES, `B1 UP 90%%d R1`, its angle the included angle when the body's rule has angle_display: included
note_heightmmNodefault 3
bend_linesboolNodefault true; false leaves BEND_UP and BEND_DOWN out, for a cutter that would cut them
layersroles|singleNodefault roles, the layers below; single: every entity on layer 0
per_bodyboolNoa file for every sheet body, `<path>-<body>.dxf`, or their DXF texts without a path → files: [{body, path | dxf, bends}]; not with `body`
Automation details

Command ID: sheet.export_dxf

Parameters:

body?, path?, curves?: exact|arcs (default exact: an ellipse as ELLIPSE, splines, text outlines and other Béziers as SPLINE; arcs: tangent arcs and lines within 0.001 mm, for machines that read only those), text?: cut|etch (default cut: letters that text cut through the sheet are cut contours on layer TEXT; etch: single contours on layer ETCH and the sheet keeps its material there; text machined to a depth is always on ETCH), bend_notes?: bool (default false: a note beside each bend line on layer BEND_NOTES, `B1 UP 90%%d R1`, its angle the included angle when the body's rule has angle_display: included), note_height?: mm (default 3), bend_lines?: bool (default true; false leaves BEND_UP and BEND_DOWN out, for a cutter that would cut them), layers?: roles|single (default roles, the layers below; single: every entity on layer 0), per_body?: bool (a file for every sheet body, `<path>-<body>.dxf`, or their DXF texts without a path → files: [{body, path | dxf, bends}]; not with `body`) → DXF (AutoCAD 2000, mm): layers OUTLINE (the outer contour), CUT (what is cut out inside it), TEXT or ETCH (letters), BEND_UP and BEND_DOWN (dashed centre lines), BEND_NOTES, POCKET_<BOTTOM|TOP|INTERNAL>_Z<from>_TO_<to>_MM (one per side and depth) and PERFORATION; inner contours come before the outline

Export Bend Table

ParameterTypeRequiredDescription
bodyNo
formatcsv|jsonNodefault csv
pathNo
Automation details

Command ID: sheet.export_bend_table

Parameters:

body?, format?: csv|json (default csv), path? → bend table in mm and degrees, including rule and flat centre-line coordinates; angle_deg is the bend angle and included_angle_deg the angle left between the bend's sides (180° less it); a bend that cut-outs split has a row for each piece

Other commands: plastic

List Plastic Rules

Automation details

Command ID: plastic.rules

Parameters:

→ rules with their expressions and values, the active rule and the rules assigned to bodies

Other commands: frame

Profile Library

Automation details

Command ID: frame.profile.list

Parameters:

family?: RSC|PFC|RSA|SHS|RHS|CHS (list only that family's sizes). Returns kinds: [{kind, label, required, optional, example}]: the built-in profiles, each SolidCraft's own parametric definition; and sizes: [{family, kind, standard, source, names, additional?}]: the standard steel sizes by name, each family with the published table its dimensions come from (frame.profile.properties with {profile: {size}} gives a size's dimensions and section)

Profile Properties

ParameterTypeRequiredDescription
profile{kind: bar|round_bar|rect_tube|round_tube|angle|channel|i_beam, …its dimensions: each a number in the design's units or a length expression over its parameters ("tube_wall", "2 in"); a channel's flange_slope is an angle (a number of degrees)}| {size: a standard steel size by name, such as "RSC 203x89", "PFC 200x90x30", "RSA 50x50x5", "SHS 100x100x5", "RHS 200x100x6.3" or "CHS 42.4x3.2" (frame.profile.list lists them), alone: it brings its dimensions}Yesframe.profile.list gives each kind's dimensions and an example; dimensions left out take defaults from the profile's size
Automation details

Command ID: frame.profile.properties

Parameters:

profile: {kind: bar|round_bar|rect_tube|round_tube|angle|channel|i_beam, …its dimensions: each a number in the design's units or a length expression over its parameters ("tube_wall", "2 in"); a channel's flange_slope is an angle (a number of degrees)} (frame.profile.list gives each kind's dimensions and an example; dimensions left out take defaults from the profile's size) | {size: a standard steel size by name, such as "RSC 203x89", "PFC 200x90x30", "RSA 50x50x5", "SHS 100x100x5", "RHS 200x100x6.3" or "CHS 42.4x3.2" (frame.profile.list lists them), alone: it brings its dimensions}; density?: kg/m³ (adds mass_per_m, kg/m). Returns profile (every default filled in), area (mm²), centroid [x,y] (mm, from the bounding box centre), ixx, iyy, ixy (mm⁴ about the centroid), width, height

Insert Profile

ParameterTypeRequiredDescription
profile{kind: bar|round_bar|rect_tube|round_tube|angle|channel|i_beam, …its dimensions: each a number in the design's units or a length expression over its parameters ("tube_wall", "2 in"); a channel's flange_slope is an angle (a number of degrees)}| {size: a standard steel size by name, such as "RSC 203x89", "PFC 200x90x30", "RSA 50x50x5", "SHS 100x100x5", "RHS 200x100x6.3" or "CHS 42.4x3.2" (frame.profile.list lists them), alone: it brings its dimensions}Yesframe.profile.list gives each kind's dimensions and an example; dimensions left out take defaults from the profile's size
at[x,y]Nosketch mm, default the origin
anchororigin|center|centroid|top_left|top|top_right|left|right|bottom_left|bottom|bottom_rightNothe profile point placed on `at`: its origin, the default and a built-in profile's centre, its bounding box's centre, its centroid, or a corner or edge middle of its bounding box
fixedboolNodefault true: the curves are locked, so solving the sketch keeps the shape
sketchid|nameNodefault: the sketch being edited
Automation details

Command ID: frame.profile.insert

Parameters:

profile: {kind: bar|round_bar|rect_tube|round_tube|angle|channel|i_beam, …its dimensions: each a number in the design's units or a length expression over its parameters ("tube_wall", "2 in"); a channel's flange_slope is an angle (a number of degrees)} (frame.profile.list gives each kind's dimensions and an example; dimensions left out take defaults from the profile's size) | {size: a standard steel size by name, such as "RSC 203x89", "PFC 200x90x30", "RSA 50x50x5", "SHS 100x100x5", "RHS 200x100x6.3" or "CHS 42.4x3.2" (frame.profile.list lists them), alone: it brings its dimensions}; at?: [x,y] (sketch mm, default the origin); anchor?: origin|center|centroid|top_left|top|top_right|left|right|bottom_left|bottom|bottom_right (the profile point placed on `at`: its origin, the default and a built-in profile's centre, its bounding box's centre, its centroid, or a corner or edge middle of its bounding box); angle?: radians, counter-clockwise; fixed?: bool (default true: the curves are locked, so solving the sketch keeps the shape); sketch?: id|name (default: the sketch being edited). Returns the section as frame.profile.properties does. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

Other commands: component

Cycle Component Color

Read the components and joints guide.

ParameterTypeRequiredDescription
componentid or nameYes
Automation details

Command ID: component.cycle_color

Parameters:

component: id or name; saves the next identification colour and turns Component Colors on; shared by every instance, separate from appearances

Hide Component History

Read the components and joints guide.

ParameterTypeRequiredDescription
componentid or nameYes
Automation details

Command ID: component.hide_history

Parameters:

component: id or name; keeps the component's features in the design but leaves them out of the History bar (#10393)

Show Component History

Read the components and joints guide.

ParameterTypeRequiredDescription
componentid or nameYes
Automation details

Command ID: component.show_history

Parameters:

component: id or name; puts a component's features back in the History bar after Hide Component History (#10393)

Create Components from Bodies

Read the components and joints guide.

ParameterTypeRequiredDescription
bodies[names]Yes
Automation details

Command ID: component.from_bodies

Parameters:

bodies: [names] → one new component per body, named after it

Activate Component

Read the components and joints guide.

ParameterTypeRequiredDescription
componentid or nameYes0 or "root" for the design
occurrenceexact placement used to author shared geometryNo
pathexact placement used to author shared geometryNo
Automation details

Command ID: component.activate

Parameters:

component: id or name (0 or "root" for the design); occurrence?, path?: exact placement used to author shared geometry

Edit in Place

Read the components and joints guide.

ParameterTypeRequiredDescription
componentid or name of a linked componentYes
pathits placementNooccurrence ids, default its first
Automation details

Command ID: component.edit_in_place

Parameters:

component: id or name of a linked component; path?: its placement (occurrence ids, default its first) → in the app window: open the design it links in a tab of its own with the assembly drawn around it (component.finish_edit goes back); fails when it is not linked, the assembly has no file yet, something is open or running in it, or the file is missing, on a network location not allowed yet or open in another SolidCraft; asked during the agent's turn it opens once the turn ends (queued: true); elsewhere it only checks

Finish Edit

Read the components and joints guide.

Automation details

Command ID: component.finish_edit

Parameters:

no parameters → in the app window: go back from the linked design edited in place to its assembly, which reads it again; fails while the design has unsaved changes (run file.save first) or something is open in it; asked during the agent's turn it finishes once the turn ends (queued: true); elsewhere nothing changes

List Components

Read the components and joints guide.

ParameterTypeRequiredDescription
compactboolNocounts of bodies, features, sketches and construction planes instead of their names; occurrences leave out identity transforms; children as occurrence ids
bodiesboolNowith compact: body names, not their count
Automation details

Command ID: component.list

Parameters:

compact?: bool (counts of bodies, features, sketches and construction planes instead of their names; occurrences leave out identity transforms; children as occurrence ids); bodies?: bool (with compact: body names, not their count)

Component Physical Properties

Read the components and joints guide.

ParameterTypeRequiredDescription
componentid or nameNodefault active
descendantsboolNodefault true
Automation details

Command ID: component.properties

Parameters:

component?: id or name (default active); descendants?: bool (default true); about?: [x, y, z] (mm). Measures the first placement, or all placements for root, including pending moves; material-aware mass and center of mass in world coordinates; volume, density, mass, center of mass and inertia_g_mm2 (the inertia tensor about the body's center of mass, world axes, from its tessellation) only for closed, consistently oriented bodies; total.open_bodies_skipped counts open bodies excluded from volume/mass totals; total.first_moment_g_mm (mass × center of mass), inertia_g_mm2 about the total center of mass, principal_moments_g_mm2 (smallest first) and principal_axes (unit, right-handed); with about, total.inertia_about {point, inertia_g_mm2}

Rename Component

Read the components and joints guide.

ParameterTypeRequiredDescription
componentYes
nameYes
Automation details

Command ID: component.rename

Parameters:

component, name

Delete Component

Read the components and joints guide.

Automation details

Command ID: component.delete

Parameters:

component: deletes it, its subcomponents, occurrences and features

Unlock Contents

Read the components and joints guide.

Automation details

Command ID: component.unlock

Parameters:

component: removes the rigid joints that hold its contents together (an imported assembly's rigid group), so its parts move and take joints one by one → joints (the ids removed)

Move Bodies to Component

Read the components and joints guide.

ParameterTypeRequiredDescription
bodies[names]Yes
componentYes
component_pathits placement to place them byNodefault: its only one, else the active one; a component placed more than once gets them in every instance
from_paththe placement of their component they move fromNodefault: its only one, else its first, where its bodies have their own names
Automation details

Command ID: component.move_bodies

Parameters:

bodies: [names], component; component_path?: its placement to place them by (default: its only one, else the active one; a component placed more than once gets them in every instance); from_path?: the placement of their component they move from (default: its only one, else its first, where its bodies have their own names)

Paste Bodies

Read the components and joints guide.

ParameterTypeRequiredDescription
bodies[names, as the Browser shows them: a body seen in another placement of its component (Cube (B:2)) is copied from there]Yes
componentwhere the copies goNodefault: the active one
component_pathits placement to place them byNodefault: its only one, else the active one; a component placed more than once gets them in every instance
Automation details

Command ID: component.copy_bodies

Parameters:

bodies: [names, as the Browser shows them: a body seen in another placement of its component (`Cube (B:2)`) is copied from there]; component?: where the copies go (default: the active one); component_path?: its placement to place them by (default: its only one, else the active one; a component placed more than once gets them in every instance). Each copy stays where its body is in the world; the copies' names come back as `bodies`

Paste from Another Design

Read the components and joints guide.

ParameterTypeRequiredDescription
componentwhere it goesNodefault: the active one
Automation details

Command ID: component.paste_from_design

Parameters:

component?: where it goes (default: the active one); component_path?: its placement (default: the active one, else its only one). Pastes what was copied in another design, at the same world coordinates: its bodies as imported bodies (no history) in one Import feature, or its components as independent copies with their history, appearances, joints and motion (as `component.paste_new`); the user parameters they use come along, renamed `w_2` when taken. A component that uses what isn't in it (another body, sketch or parameter) is refused, saying what. Returns `bodies` and `feature`, or the pasted components' `paths`

Move Sketches to Component

Read the components and joints guide.

ParameterTypeRequiredDescription
sketches[ids or names]Yesa number is an id; a string is an exact name, then an id
componentYes
Automation details

Command ID: component.move_sketches

Parameters:

sketches: [ids or names] (a number is an id; a string is an exact name, then an id), component; the sketches keep their own coordinates (component.move_features keeps them where they are in the world), except that a sketch on an exactly attached face stays where it is on the face

Move Sketches to Their Parts

Read the components and joints guide.

ParameterTypeRequiredDescription
sketches[ids or names]Nodefault: every sketch in the design
Automation details

Command ID: component.move_sketches.to_parts

Parameters:

sketches?: [ids or names] (default: every sketch in the design) → moved, stayed, sketches [{sketch, name, component, part}], kept [{sketch, name, code, reason}]. Each sketch whose plane is an exactly attached face of a part's body goes to that part, as component.move_sketches moves it, and stays where it is on the face; the others (on an origin, construction or fixed plane, on a body of the design itself, or already in their part) stay where they are. One undo step

Move Features to Component

Read the components and joints guide.

ParameterTypeRequiredDescription
features[ids or names]Yessketches too
componentwhere they goNodefault: the active one
Automation details

Command ID: component.move_features

Parameters:

features: [ids or names] (sketches too); component?: where they go (default: the active one) → moved, component, bodies. Ids, names and timeline order stay; the geometry stays where it is in the world; the bodies the features made move with them. Refused while the timeline is rolled back, for a component placed more than once, and for a move that would break a feature (nothing changes)

Paste New

Read the components and joints guide.

ParameterTypeRequiredDescription
occurrenceselected source placementNo
pathselected source placementNo
parentcomponent the copy goes inNodefault: the source placement's; not the component or one inside it
in_placeas occurrence.copyNo
parent_pathas occurrence.copyNo
translate[x,y,z]No
Automation details

Command ID: component.paste_new

Parameters:

component: independently copy its nested definition graph and history, materials and styles; occurrence?, path?: selected source placement; parent?: component the copy goes in (default: the source placement's; not the component or one inside it); in_place?, parent_path?: as occurrence.copy; translate?: [x,y,z]. Reassigned external bodies become base geometry; retained external links and omitted boundary joints are reported

Read the components and joints guide.

ParameterTypeRequiredDescription
componentid or nameNodefault: every linked component
Automation details

Command ID: component.update_links

Parameters:

component?: id or name (default: every linked component); components?: a list of ids or names, which goes on past a design it cannot read (that one keeps its bodies, is named in failed and gets a warning) — reads the linked designs again and replaces their bodies where they changed, as one undo step; → updated, unchanged, failed ([{component, name, error}]), warnings (also joints whose faces are gone or that no longer solve)

Read the components and joints guide.

ParameterTypeRequiredDescription
componentid or name of a linked componentYes
Automation details

Command ID: component.relink

Parameters:

component: id or name of a linked component; path: a saved design (.sc or .json) — replaces the source and linked bodies for all its occurrences, preserving identities and placements; occurrence?: one of its placements — when it has others, only that placement comes to show the design, as a placement of the component already linked to it or of a new one under the same parent, keeping its position and joints (component is then the one it shows, previous_component the one it showed); returns path, previous_path, warnings, joint_errors and joint_conflicts

Read the components and joints guide.

ParameterTypeRequiredDescription
componentid or name of a linked component — keeps its bodies as they are and forgets the design fileYes
Automation details

Command ID: component.break_link

Parameters:

component: id or name of a linked component — keeps its bodies as they are and forgets the design file

Extract to File

Read the components and joints guide.

ParameterTypeRequiredDescription
componentid or name of a component that is not linkedYes
paththe .sc design to writeYes
Automation details

Command ID: component.extract

Parameters:

component: id or name of a component that is not linked; path: the .sc design to write → its history, subcomponents and their joints move to that design, and the component becomes linked to it (as component.insert_design makes it); returns path, file, feature, bodies, notices (what stays behind), warnings and copies (components that build the same bodies: see component.replace_with_link)

Find Identical Copies

Read the components and joints guide.

ParameterTypeRequiredDescription
componentid or name of a linked componentYes
Automation details

Command ID: component.copies

Parameters:

component: id or name of a linked component → copies: [{component, name}], the components not linked whose own history builds the same bodies

Read the components and joints guide.

ParameterTypeRequiredDescription
witha linked componentYes
patha .sc designYesits link here, else a new linked component
Automation details

Command ID: component.replace_with_link

Parameters:

component or components: [ids or names] of components that are not linked; with: a linked component, or path: a .sc design (its link here, else a new linked component) → each component's history goes and its placements become placements of the linked component; refused when its bodies are not the same as the link's; returns component, name, replaced and warnings

Read the components and joints guide.

Automation details

Command ID: component.links

Parameters:

→ linked components: component, name, path, file (resolved), found (null when the file is on a network location not allowed, or names a device: it is not looked for), changed (whether the file is no longer what this design last read: Update Links reads it again; null when not found or the link is older than this record), then host (the network location); state (missing, unreadable, not_allowed: a network location not allowed or outside the folders this agent may use) and reason when the file can't be reached

Part Properties

Read the components and joints guide.

ParameterTypeRequiredDescription
componentid or nameYes0 or "root": the design's own properties
Automation details

Command ID: component.metadata

Parameters:

component: id or name (0 or "root": the design's own properties) → component, name, part_number, description, revision, bom_type (components only), metadata (as saved, other tools' keys too), properties: {name: text}

Change Part Properties

Read the components and joints guide.

ParameterTypeRequiredDescription
componentid or nameYesnot the root: use document.metadata.set
part_numbertextNo
descriptiontextNo
revisiontextNoempty or null removes
bom_typenormal|phantom|reference|purchased|inseparableNoempty or null: normal
values{key: value}NoSolidCraft's keys, as document.metadata's properties lists them, bom_type, or a reverse-DNS key holding an object
Automation details

Command ID: component.metadata.set

Parameters:

component: id or name (not the root: use document.metadata.set); part_number?, description?, revision?: text (empty or null removes); bom_type?: normal|phantom|reference|purchased|inseparable (empty or null: normal); values?: {key: value} (SolidCraft's keys, as document.metadata's properties lists them, bom_type, or a reverse-DNS key holding an object); properties?: {name: text, …} (free properties; empty or null removes). All or nothing, one undo step → component, metadata, properties, changed: [keys]

Other commands: occurrence

Occurrence Physical Properties

Read the components and joints guide.

ParameterTypeRequiredDescription
occurrenceid or nameYes
path[occurrence ids]Norequired if nested under multiple instances
descendantsboolNodefault true
Automation details

Command ID: occurrence.properties

Parameters:

occurrence: id or name; path?: [occurrence ids] (required if nested under multiple instances); descendants?: bool (default true); about?: [x, y, z] (mm). Physical properties of this placement in world coordinates, including pending moves; volume, density, mass, center of mass and inertia_g_mm2 only for closed, consistently oriented bodies; total.open_bodies_skipped counts open bodies excluded from volume/mass totals; total inertia as in component.properties

Paste (Instance)

Read the components and joints guide.

ParameterTypeRequiredDescription
componentYesor occurrence
pathvalidates selected occurrenceNo
parentcomponentNodefault: the same parent
translate[x,y,z]No
Automation details

Command ID: occurrence.copy

Parameters:

component (or occurrence): what to place again; path?: validates selected occurrence; parent?: component (default: the same parent); translate?: [x,y,z]; in_place?: bool (default false: the selected transform within the parent; true: where the selected placement is in the world, translate in world axes; a parent placed more than once is placed by parent_path?, else the active placement, and every instance gets the copy). Copies the selected effective transform, including pending moves

Move Occurrence

Read the components and joints guide.

ParameterTypeRequiredDescription
occurrenceYes
pathexact placementNo
spacelocal|worldNodefault local
translate[x,y,z]Nonumbers or length expressions: an occurrence moved by expressions that name parameters keeps them and moves with them when the parameters change, along the axes the move was given in; other moves of it stay; a parameter they use can't be deleted, and a renamed one carries into them
axis[x,y,z]No
angleradians or "30 deg"No
origin[x,y,z]No
to{position: [x,y,z], axis?: [x,y,z], angle?} or to_matrix?: [12 or 16 numbers]Noabsolute placement: where it goes in space, not a move; not with translate/axis/angle/origin/matrix
matrix[12 or 16 numbers]Noinstead of translate/axis/angle/origin: a rigid move as a row-major 3x4 rotation-and-translation or 4x4 matrix, flat or as rows; one that is a rotation only up to rounding is orthonormalised, a scale, shear or mirror is refused
tolerancelargest accepted |RᵀR − I|Nodefault 1e-9, at most 1e-3
captureboolNodefault true; false keeps it pending until Capture Position
move_groundedboolNodefault false: a grounded occurrence refuses; true moves it and it stays grounded
Automation details

Command ID: occurrence.move

Parameters:

occurrence; path?: exact placement; space?: local|world (default local); translate?: [x,y,z] (numbers or length expressions: an occurrence moved by expressions that name parameters keeps them and moves with them when the parameters change, along the axes the move was given in; other moves of it stay; a parameter they use can't be deleted, and a renamed one carries into them); axis?: [x,y,z], angle?: radians or "30 deg", origin?: [x,y,z]; to?: {position: [x,y,z], axis?: [x,y,z], angle?} or to_matrix?: [12 or 16 numbers] (absolute placement: where it goes in space, not a move; not with translate/axis/angle/origin/matrix); matrix?: [12 or 16 numbers] (instead of translate/axis/angle/origin: a rigid move as a row-major 3x4 rotation-and-translation or 4x4 matrix, flat or as rows; one that is a rotation only up to rounding is orthonormalised, a scale, shear or mirror is refused); tolerance?: largest accepted |RᵀR − I| (default 1e-9, at most 1e-3); capture?: bool (default true; false keeps it pending until Capture Position); move_grounded?: bool (default false: a grounded occurrence refuses; true moves it and it stays grounded). Nested occurrence definitions remain shared. Replies {occurrence, transform, pending}; when joints hold it to something fixed and put it back, transform is where it is and the reply adds reverted: true and a warning naming the joint

Drag Occurrence

Read the components and joints guide.

ParameterTypeRequiredDescription
at[x,y,z] world point on itYes
point[x,y,z] where it goesYes
Automation details

Command ID: occurrence.drag

Parameters:

occurrence or path: exact placement; at: [x,y,z] world point on it; point: [x,y,z] where it goes. Moves the placement by point - at (with any chain of joints it is in that is joined to nothing grounded) and captures its position; other pending moves stay pending. Shared definitions move in all placements. Grounded parts and parts joints hold to something fixed are refused (joint.reach moves those)

Align Occurrence

Read the components and joints guide.

ParameterTypeRequiredDescription
occurrenceplacements to move togetherNo
occurrencesplacements to move togetherNo
pathplacements to move togetherNo
pathsplacements to move togetherNo
fromYes
from_faceYes
from_planeYes
toYes
to_faceYes
to_planeYes
flipboolNo
captureboolNodefault true
move_groundedboolNodefault false
Automation details

Command ID: occurrence.align

Parameters:

occurrence?, occurrences?, path?, paths?: placements to move together; from | from_face | from_plane; to | to_face | to_plane; flip?: bool; capture?: bool (default true); move_grounded?: bool (default false). Moves only the chosen occurrence placements in world coordinates without changing shared component geometry; a placement inside a component placed more than once moves in every placement of it, and the reply's `shared` lists those paths

Ground

Read the components and joints guide.

ParameterTypeRequiredDescription
occurrenceYes
groundedboolNodefault toggles
Automation details

Command ID: occurrence.ground

Parameters:

occurrence; grounded?: bool (default toggles)

Move to Component

Read the components and joints guide.

ParameterTypeRequiredDescription
occurrenceYes
pathexact placementNo
parentcomponent id or nameYes0 or "root" for the design
parent_paththe parent's placement to place it byNodefault: its only one, else the active one; every instance of the parent gets it
Automation details

Command ID: occurrence.reparent

Parameters:

occurrence; path?: exact placement; parent: component id or name (0 or "root" for the design); parent_path?: the parent's placement to place it by (default: its only one, else the active one; every instance of the parent gets it). The placement keeps its world position; a move that would break a feature is refused

Other commands: browser

Move to Component

Read the window tour.

Automation details

Command ID: browser.reparent

Parameters:

Moves placements, features and bodies into a component. occurrences?: [ids or paths], features?: [ids or names], bodies?: [names], component?: where they go (default: the active one), component_path?: its placement → component, occurrences, features, bodies (what moved). One undo step, all or nothing: placements move first (taking what they hold), then features (taking the bodies they made), then the bodies still elsewhere (a body transfer, which leaves its features behind)

New Group

Read the window tour.

ParameterTypeRequiredDescription
folderbodies|sketches|constructionNo
componentidNodefault 0
parentgroup idNothe new group nests in it, taking its component and folder; at most 8 levels
nameNo
items[keys]Nothey leave other groups
Automation details

Command ID: browser.group

Parameters:

folder?: bodies|sketches|construction; component?: id (default 0); parent?: group id (the new group nests in it, taking its component and folder; at most 8 levels); name?; items?: [keys] (they leave other groups)

Ungroup

Read the window tour.

ParameterTypeRequiredDescription
groupidYesits items and child groups go up to the group that holds it, or the folder
Automation details

Command ID: browser.ungroup

Parameters:

group: id (its items and child groups go up to the group that holds it, or the folder)

Rename Group

Read the window tour.

ParameterTypeRequiredDescription
groupidYes
nameYes
Automation details

Command ID: browser.rename_group

Parameters:

group: id, name

Move Browser Items

Read the window tour.

ParameterTypeRequiredDescription
folder[keys (strings) and group ids (numbers)]Yes
component[keys (strings) and group ids (numbers)]No
items[keys (strings) and group ids (numbers)]Yes
groupidNointo it; omit for the folder's top level
beforekey or group idNoposition in it; default the end
Automation details

Command ID: browser.move

Parameters:

folder, component?, items: [keys (strings) and group ids (numbers)]; group?: id (into it; omit for the folder's top level); before?: key or group id (position in it; default the end). A group cannot go into itself or past 8 levels

Order Browser Items

Read the window tour.

ParameterTypeRequiredDescription
folderbodies|sketches|construction|componentsYes
componentNo
order[keys (strings) and top-level group ids (numbers)]Yesdisplay order; for `components`, the ids of the components placed in it
Automation details

Command ID: browser.order

Parameters:

folder: bodies|sketches|construction|components, component?, order: [keys (strings) and top-level group ids (numbers)] (display order; for `components`, the ids of the components placed in it)

Show/Hide

Read the window tour.

ParameterTypeRequiredDescription
visibleboolYes
items[sketch id or name | body name | construction name | joint name | O|X|Y|Z|XY|XZ|YZ]No
joints[joint id or name]Notheir glyphs; they still constrain and move parts
features[feature id or name]Nothe bodies they made; path?: [occurrence ids] for only that placement's
folderbodies|sketches|construction|origin|joints|allNoall of it; showing also shows its browser folders and groups, and `all` takes in the occurrence hides too
tree_folders[browser folder or group key: c0/bodies, o:4/sketches, o:4/origin, c0/g7, c0/joints, c0/canvases, c0/point_clouds]Nothe folder or group itself: its items keep their own state; component origin items use o:4/origin/XY etc. in items
paths[[occurrence ids]]Noexact placements and descendants
isolateboolNowith paths or items of bodies, and visible:true: everything else is hidden, other placements as placements
Automation details

Command ID: browser.visibility

Parameters:

visible: bool; items?: [sketch id or name | body name | construction name | joint name | O|X|Y|Z|XY|XZ|YZ]; or joints?: [joint id or name] (their glyphs; they still constrain and move parts); or features?: [feature id or name] (the bodies they made; path?: [occurrence ids] for only that placement's); or folder?: bodies|sketches|construction|origin|joints|all (all of it; showing also shows its browser folders and groups, and `all` takes in the occurrence hides too); or tree_folders?: [browser folder or group key: c0/bodies, o:4/sketches, o:4/origin, c0/g7, c0/joints, c0/canvases, c0/point_clouds] (the folder or group itself: its items keep their own state; component origin items use o:4/origin/XY etc. in items); or paths?: [[occurrence ids]] (exact placements and descendants), isolate?: bool (with paths or items of bodies, and visible:true: everything else is hidden, other placements as placements). Saved with the design and undone like any other change (the camera is not)

Expand All

Read the window tour.

ParameterTypeRequiredDescription
path[occurrence ids]Nothat placement and its descendants
componentidNothat component's own folders
Automation details

Command ID: browser.expand_all

Parameters:

path?: [occurrence ids] (that placement and its descendants); component?: id (that component's own folders); omit both for the whole tree, including folders that start closed (Origin, Named Views, Features). Display only: open/closed rows are not saved in the design and are not an undo step → open, open_by_default, closed_by_default

Collapse All

Read the window tour.

ParameterTypeRequiredDescription
path[occurrence ids]Nothat placement's branch
componentidNo
Automation details

Command ID: browser.collapse_all

Parameters:

path?: [occurrence ids] (that placement's branch); component?: id; omit both for the whole tree: every placement, folder and group at every level. The row it starts from (the design row for the whole tree) stays open, so its first level remains visible. Display only; not an undo step → open, open_by_default, closed_by_default

Other commands: joint

Reach Point

Read the components and joints guide.

ParameterTypeRequiredDescription
occurrenceid|nameYes
point[x,y,z]Yeswhere its point goes, world
at[x,y,z]Noits point, in the world where it is now; default: the centre of its bodies' box
z[x,y,z]Nothe way its axis then points
axis[x,y,z]Nothat axis, in the world now; default: its own z
joints[ids or names]Noonly these move toward the point; the other joints of a closed loop they are in follow
quietboolNo
Automation details

Command ID: joint.reach

Parameters:

occurrence: id|name; point: [x,y,z] (where its point goes, world); at?: [x,y,z] (its point, in the world where it is now; default: the centre of          its bodies' box); z?: [x,y,z] (the way its axis then points); axis?: [x,y,z] (that axis, in the world now; default: its own z); joints?: [ids          or names] (only these move toward the point; the other joints of a closed loop they are in follow); quiet?: bool; continuous?: bool (a frame          of a drag: the answer says continuous, and the app's command line doesn't echo it). Moves the values of the joints          on its chain from where they are to the nearest pose, within their limits, through motion links, keeping closed loops closed as joint.drive does          (contact sets are not checked) → reached; error_mm (and error_deg with z): how close it got; point: where its point is; values: [{joint, name,          values}] of the joints that moved, as joint.list gives them; moved; stopped_by_limit: [{joint, value, limit: min|max}]: the limits it rests on          short of the point; stopped_by_loop: {joint}: the closed loop that stopped it (its chain alone gets closer); reason: why nothing can move

Joint Limits

Read the components and joints guide.

ParameterTypeRequiredDescription
jointYes
indexvalue indexNodefault 0
minNo
maxNoomit both to clear
Automation details

Command ID: joint.limits

Parameters:

joint; index?: value index (default 0); min?, max? (omit both to clear)

Edit Joint

Read the components and joints guide.

ParameterTypeRequiredDescription
jointYes
ajoint origins as joint.create takes themNo
bjoint origins as joint.create takes themNoa side left out keeps its place; the two must stay on different occurrences
typeNo
flipNo
offsetNo
angleNoa number, or an expression the joint then follows
suppressedNo
nameNo
Automation details

Command ID: joint.edit

Parameters:

joint; a?, b?: joint origins as joint.create takes them (a side left out keeps its place; the two must stay on different occurrences); type?, flip?, offset?, angle? (a number, or an expression the joint then follows), suppressed?, name?; the joint re-solves, keeping its limits and expressions (a relation's values start again where its parts are); an edit that places it again (not a rename alone) meets a loop it closes as joint.create does, turning an angle a half turn where only that meets it

Delete Joint

Read the components and joints guide.

ParameterTypeRequiredDescription
jointid|nameYesor origin: name, link: index
Automation details

Command ID: joint.delete

Parameters:

joint: id|name (or origin: name, link: index)

List Joints

Read the components and joints guide.

Automation details

Command ID: joint.list

Parameters:

→ joints (type, occurrences, values, limits; expressions: {offset?, angle?, values?} when they were typed as expressions), origins, links, degrees of freedom, conflicts, floating: [{occurrences, joints}] (joint chains joined to nothing grounded: the solver places none of them, so driving them moves nothing)

Inspect Joint

Read the components and joints guide.

ParameterTypeRequiredDescription
jointid|nameNodefault: every joint
Automation details

Command ID: joint.inspect

Parameters:

joint?: id|name (default: every joint) → joints: [{id, name, type, suppressed, visible, flip, offset (mm), angle (deg), values: as joint.list gives them, dofs, expressions?: {offset?, angle?, values?}, a, b: {occurrence, occurrence_name, component, component_name, path (occurrence names from the top, `/` between), snap (as saved: type, pick, face, end, edge, corner, x, point, z, in the component's coordinates), references: [{role: face|end|x|edge|corner, name, role_in_feature?: (top, side:c1…), feature?: {id, name, type} (the feature that made the face; error when it is gone), sketch?: {id, name, curve, curve_type} (a face swept from a sketch curve), faces?: [an edge's two faces], unverified?: a name saved before exact names}], resolves, frame?: {origin, z, x} in the world, error?: why the origin is lost}, links: [{index, role: driver|driven, …as joint.list}], errors, conflicts}] (reads only: nothing changes)

Solve Joints

Read the components and joints guide.

Automation details

Command ID: joint.solve

Parameters:

re-place the occurrences from the joints → conflicts, errors, dof

Sweep Joint

Read the components and joints guide.

ParameterTypeRequiredDescription
jointid|nameYes
fromits value the sweep runs betweenYes
toits value the sweep runs betweenYesdegrees for angles, mm for distances, or expressions
indexNowhich value, default 0
stepNobetween samples, in the joint's unit; default: fine enough that no part passes through another between two
refinemmNothe first contact is bisected until no part moves more than this between the last clear value and the touching one; default 0.01
occurrences[ids or names]Nochecked against each other; default every occurrence
against[ids or names]Nothe parts that stay: each of `occurrences` only against these
contact_setstrueNothe pairs of the design's contact sets
exclude_pairs[[a, b]]Nooccurrences by id or name: pairs of bodies under a and b are not checked, such as a pin in its hole
budget_ssecondsNoat most 86400; past them the sweep stops with what it found; its work is then not capped. Without it the work caps, 4096 samples, 64 bisection steps and 500 million triangle tests, stop it the same way
report_gapsboolNothe nearest gap at every sample, default false
applyboolNoleave the design where the sweep stopped; default false: the design is not changed
Automation details

Command ID: joint.sweep

Parameters:

joint: id|name; from, to: its value the sweep runs between (degrees for angles, mm for distances, or expressions); index? (which value, default 0); step? (between samples, in the joint's unit; default: fine enough that no part passes through another between two); refine?: mm (the first contact is bisected until no part moves more than this between the last clear value and the touching one; default 0.01); occurrences?: [ids or names] (checked against each other; default every occurrence), with against?: [ids or names] (the parts that stay: each of `occurrences` only against these), or contact_sets?: true (the pairs of the design's contact sets); exclude_pairs?: [[a, b]] (occurrences by id or name: pairs of bodies under a and b are not checked, such as a pin in its hole); budget_s?: seconds (at most 86400; past them the sweep stops with what it found; its work is then not capped. Without it the work caps, 4096 samples, 64 bisection steps and 500 million triangle tests, stop it the same way); report_gaps?: bool (the nearest gap at every sample, default false); apply?: bool (leave the design where the sweep stopped; default false: the design is not changed) → stopped_at (the last value clear of contact, or where a loop or limit stopped it; null: it reached `to`), stopped_by: {a, b, at: [x,y,z] (where they touch, world), a_part, b_part: {component, path: [occurrence names], path_ids} (where each touching body sits)} | "loop" | "limit" | "budget" (stopped_at: the last value checked clear) | null, samples: [{value, min_gap_mm?, pair?, parts?}], min_gap? (report_gaps: {part: {mm, at_value, to}}, the nearest each part came to another), unit, gap_method ("mesh": distances between the tessellations), gap_tolerance_mm (how far those can be off: 0 when every face is flat), pairs (checked), touching_at_start: [[a, b]] (pairs already touching at `from`, such as parts joined face to face or a door on its frame: these stop it only once they pass into each other deeper than they start), touching_parts (where their bodies sit), budget_s, budget_exceeded, budget_ran_out: "time" | "samples" | "bisections" (a contact reported less refined than `refine`) | "intersections" | "work" | null, applied. The parts are tested at the samples and through the bisection; the motion is on a copy (closed loops follow, contact sets do not stop it)

Find Joint Limits

Read the components and joints guide.

ParameterTypeRequiredDescription
jointid|nameYes
indexNoone value; default every value
refineNo
stepNo
occurrencesNo
againstNo
contact_setsNo
exclude_pairsNoas joint.sweep
Automation details

Command ID: joint.find_limits

Parameters:

joint: id|name; index? (one value; default every value); refine?, step?, occurrences?, against?, contact_sets?, exclude_pairs? (as joint.sweep) → values: [{index, dof, unit, value (now), min, max, min_by, max_by, range}], each value swept both ways from where it is, its own limits ignored, closed loops following; each side stops at the first contact (bisected to `refine`), where a loop cannot close, or at another limit (another value's, or another joint's through a motion link): min_by/max_by {a, b, at} | "loop" | "limit" | "budget" (its work ran out: the last value checked clear) | "turn" (an angle with no contact within a turn: a turn from the other side, or ±180° from now when nothing is met at all) | "range" (a distance with no contact within the parts' reach: min/max null); touching_at_start, overlapping_at_start: [{a, b, depth_mm}] (already passing into each other: they stop it only going deeper), refine_mm, gap_method, gap_tolerance_mm, pairs (0: no two parts to check, so nothing is swept). The design is not changed

Other commands: assembly

Edit Constraint

ParameterTypeRequiredDescription
constraintid|nameYesa joint made with assembly.constraint.create
typea rule as assembly.constraint.create takes itNothe references it has must fit it; the gap carries over
offsetthe gapNomm or an expression
anglean angle rule's angleNo
fliptrue turns b overNofrom the new rule's way after a type change, else from how it faces now
nameNo
suppressedNo
Automation details

Command ID: assembly.constraint.edit

Parameters:

constraint: id|name (a joint made with assembly.constraint.create); type?: a rule as assembly.constraint.create takes it (the references it has must fit it; the gap carries over); offset?: the gap (mm or an expression); angle?: an angle rule's angle; flip?: true turns b over (from the new rule's way after a type change, else from how it faces now); name?; suppressed?; quiet?. Changed as joint.edit changes a joint: its values start again where its parts are → conflicts, errors, dof, fixed as joint.edit

Mass Properties

Automation details

Command ID: assembly.mass_properties

Parameters:

no parameters → occurrences: [{occurrence (0: the design's own bodies), name, component, path, group, fixed, frame {origin, x, y, z}, bodies: [{name, material, density_g_cm3, closed, volume_mm3, mass_kg}], open_bodies_skipped, mass}], groups: [{group, occurrences, fixed, frame_occurrence, frame, mass}] (occurrences an unsuppressed rigid joint ties together, and a nested occurrence no joint places with its parent; fixed: holds the design or a grounded occurrence), warnings. mass: {mass_kg, center_of_mass_mm (world), center_of_mass_local_mm, inertia_world_kg_mm2, inertia_local_kg_mm2 (about the centre of mass, in world and frame axes)} or null when massless. Read-only; pending moves count; each body at its material's density (the default material's when none is assigned)

Other commands: contact

Edit Contact Set

ParameterTypeRequiredDescription
setid|nameYes
occurrencesNo
nameNo
suppressedboolNo
deleteboolNo
Automation details

Command ID: contact.edit

Parameters:

set: id|name; occurrences?; name?; suppressed?: bool; delete?: bool

List Contact Sets

Automation details

Command ID: contact.list

Parameters:

→ sets, enabled, all

Check Contact

Automation details

Command ID: contact.check

Parameters:

→ pairs in contact sets that pass through each other now

Other commands: motion

Play Motion Study

Read the components and joints guide.

ParameterTypeRequiredDescription
studyid|nameYes
stepYes0…steps
quietboolNopositions of only the occurrences that moved
Automation details

Command ID: motion.play

Parameters:

study: id|name; step (0…steps) — sets the joints to the study's values there (contact sets stop them); quiet?: bool (positions of only the occurrences that moved) → values: [{joint, values: [{name, value, unit}]}], positions, moved: whether any occurrence moved, unmoved: [{joint, name, reason}] (keyed joints that can move nothing, such as a chain joined to nothing grounded)

Export Motion Study

Read the components and joints guide.

ParameterTypeRequiredDescription
studyYes
samplesNodefault steps + 1
pathNo.json or .csv
Automation details

Command ID: motion.export

Parameters:

study; samples? (default steps + 1); path? (.json or .csv) → occurrence positions per step (the design is not changed)

Export Motion Animation

Read the components and joints guide.

ParameterTypeRequiredDescription
studyid|nameYes
jointid|name with from and toYesdegrees for angles, mm for distances, or expressions
viewid|nameYesan exploded view, coming apart from the assembled placement
samplesNoframes, 2…1000; default the study's steps + 1, 100 for a joint, 41 for a view or 81 with back
widthNo
heightNo16…4096 pixels, default 800 × 500
camera{target, yaw, pitch, roll?, distance, fov?, up?}Noui.camera's reply; fields left out keep the default view: view.apply's camera, or the home view framing the model, for a view both assembled and exploded
fpsNo1…50, default 20
Automation details

Command ID: motion.export_animation

Parameters:

study: id|name, or joint: id|name with from and to (degrees for angles, mm for distances, or expressions) and index?, or view: id|name (an exploded view, coming apart from the assembled placement) with back? (default false; true: back together too, exploded at the middle frame of an odd count); path: a .gif file, or a folder for PNG frames (frame-0001.png, frame-0002.png, …); samples? (frames, 2…1000; default the study's steps + 1, 100 for a joint, 41 for a view or 81 with back); width?, height? (16…4096 pixels, default 800 × 500); camera?: {target, yaw, pitch, roll?, distance, fov?, up?} (ui.camera's reply; fields left out keep the default view: view.apply's camera, or the home view framing the model, for a view both assembled and exploded); fps? (1…50, default 20); grid?, axes? (default true: the ground grid and the origin's axes) - draws the study as it plays, the joint as it is driven or the view as it explodes, with the headless renderer → path, format, frames, width, height, fps (GIF), bytes, renderer, stopped_at, stopped_by_loop, stopped_by_contact (the design is not changed, nor its placements)

Keep Motion Trace

Read the components and joints guide.

ParameterTypeRequiredDescription
studyid|nameYes
jointid|name with from and toYesdegrees for angles, mm for distances, or expressions
nameNodefault Motion TraceN
auto_updateboolNodefault true
point_refjoint origin snap - keeps the trace as a root 3D sketch splineNo
Automation details

Command ID: motion.trace.keep

Parameters:

occurrence: id|name, or an occurrence path [ids] (an instance inside sub-assemblies); point: [x,y,z] world point on it, where it is placed now; study: id|name, or joint: id|name with from and to (degrees for angles, mm for distances, or expressions) and index? (which value, default 0); samples? (2…500, default the study's steps + 1, or 100) - the point's path as the study plays or the joint is driven, closed loops following; name? (default Motion TraceN); auto_update?: bool (default true); point_ref?: joint origin snap - keeps the trace as a root 3D sketch spline. Associative curves follow mechanism and driving-input edits; false keeps a fixed curve. point_ref retains a geometry snap; otherwise point is retained in component coordinates → sketch, name, curves, points

Edit Motion Trace

Read the components and joints guide.

ParameterTypeRequiredDescription
sketchid|nameYes
indexNo
samplesNo2…500
auto_updatefalse to detachNo
Automation details

Command ID: motion.trace.edit

Parameters:

sketch: id|name; joint? with from and to, or study?; index?; samples? (2…500); auto_update?: false to detach. Edits a saved associative trace's driving conditions in one undo step

Plot Motion Study

Read the components and joints guide.

ParameterTypeRequiredDescription
studyid|nameYes
joints[id|name]Nodefault: every joint that moves
samplesNo3…1000, default the study's steps + 1
Automation details

Command ID: motion.plot

Parameters:

study: id|name; joints?: [id|name] (default: every joint that moves); occurrence? and point? (a point of a part to follow, as motion.trace takes them); samples? (3…1000, default the study's steps + 1); duration?: seconds the whole study takes, 0.001 or more (velocities and accelerations then per second, else per step) → step: [the study step of each sample], time?: [s], joints: [{joint, name, index, dof, unit, value, velocity, acceleration, velocity_unit, acceleration_unit}] (degrees for angles, mm for distances; angles unwrapped), point?: {occurrence, position: [[x,y,z]…], velocity, acceleration (vectors), speed, acceleration_norm, unit, velocity_unit, acceleration_unit}, samples (taken), stopped_at, stopped_by_loop, stopped_by_contact. The study plays as motion.play plays it, each sample driven from the one before, on a copy (the design is not changed); velocities and accelerations are finite differences between samples (central, one-sided at the ends)

Motion Interference

Read the components and joints guide.

ParameterTypeRequiredDescription
studyid|nameYes
occurrences[ids or names]Notwo or more, checked against each other; default every occurrence
against[ids or names]Nothen each of `occurrences` is checked only against these, such as an arm against the parts it must miss
contact_setstrueNothe pairs of the design's contact sets
exclude_pairs[[a, b]]Nooccurrences by id or name: pairs of bodies under a and b are not checked
samplesNo2…1000, default the study's steps + 1
Automation details

Command ID: motion.interference

Parameters:

study: id|name; occurrences?: [ids or names] (two or more, checked against each other; default every occurrence), with against?: [ids or names] (then each of `occurrences` is checked only against these, such as an arm against the parts it must miss), or contact_sets?: true (the pairs of the design's contact sets); exclude_pairs?: [[a, b]] (occurrences by id or name: pairs of bodies under a and b are not checked); samples? (2…1000, default the study's steps + 1); budget_s?: seconds (above 0, at most 86400; default a fixed work budget): the time the check may take instead, looked at between samples, for a mechanism such as a gear train that needs longer → collisions: [{a, b, first_step, last_step, steps: [[from, to]…] (the step ranges where they overlap), volume_mm3 (the most volume they share at a sample, worked out exactly from the bodies), deepest_step (the first sample sharing that volume), not_measured (null, or why the volume was not worked out at every sample they overlap, such as a budget it ran past: then volume_mm3 and deepest_step are null, never an estimate), depth_mm (how far into each other at most: an estimate from points of the bodies' contact meshes, not exact)}], clear (every sample checked and none collide; never while truncated), pairs (checked), samples (taken), truncated (the work budget, or budget_s, ran out; checked_to: the last step checked), budget_s (when given), stopped_at, stopped_by_loop. The study plays as motion.play plays it but contact sets do not stop it, on a copy (the design is not changed). Parts are tested at the samples only: one that passes wholly through another between two samples is missed

List Motion Studies

Read the components and joints guide.

Automation details

Command ID: motion.list

No parameter description is provided by the command registry.

Delete Motion Study

Read the components and joints guide.

ParameterTypeRequiredDescription
studyYes
Automation details

Command ID: motion.delete

Parameters:

study

Other commands: explode

Show Exploded View

ParameterTypeRequiredDescription
viewid|nameYesor none: back to the assembled placement
Automation details

Command ID: explode.show

Parameters:

view: id|name (or none: back to the assembled placement) — shown only; the design keeps its placements

List Exploded Views

Automation details

Command ID: explode.list

No parameter description is provided by the command registry.

Delete Exploded View

ParameterTypeRequiredDescription
viewYes
Automation details

Command ID: explode.delete

Parameters:

view

Other commands: layout

Apply Layout

ParameterTypeRequiredDescription
nameapply preset to current workspace through retained runtimeYes
expected_revisionapply preset to current workspace through retained runtimeNo
Automation details

Command ID: layout.apply

Parameters:

name, expected_revision?: apply preset to current workspace through retained runtime

Inspect Layout

Automation details

Command ID: layout.get

Parameters:

{}: committed state and registered workspace summaries

Reset Layout

ParameterTypeRequiredDescription
expected_revisionrestore current workspace's registered defaultNo
Automation details

Command ID: layout.reset

Parameters:

expected_revision?: restore current workspace's registered default

Save Layout

ParameterTypeRequiredDescription
nameYes
replacefalseNooverwrite accepted for compatibility
expected_revisionsave complete envelope in injected catalogueNo
Automation details

Command ID: layout.save

Parameters:

name, replace?: false (overwrite accepted for compatibility), expected_revision?: save complete envelope in injected catalogue

Select Layout Workspace

ParameterTypeRequiredDescription
workspaceatomically remember outgoing and replace with destinationYes
expected_revisionatomically remember outgoing and replace with destinationNo
Automation details

Command ID: layout.workspace.select

Parameters:

workspace, expected_revision?: atomically remember outgoing and replace with destination

Other commands: panel

Run Panel Header Action

Automation details

Command ID: panel.action

Parameters:

target or legacy id, action, expected_revision?: invoke a registered owning-controller action

Activate Panel Tab

Automation details

Command ID: panel.activate

Parameters:

target or legacy id, expected_revision?: activate open tab without focus

Close Panel

Automation details

Command ID: panel.close

Parameters:

target or legacy id, expected_revision?: explicit guarded close

Collapse Panel Section

Automation details

Command ID: panel.collapse

Parameters:

target or legacy id, collapsed, expected_revision?

Dismiss Panel Peek

ParameterTypeRequiredDescription
expected_revisiondismiss temporary collapsed-panel presentationNo
Automation details

Command ID: panel.dismiss

Parameters:

expected_revision?: dismiss temporary collapsed-panel presentation

Float Panel

Automation details

Command ID: panel.float

Parameters:

target or legacy id, destination: new_section with floating location, expected_revision?

Focus Panel

Automation details

Command ID: panel.focus

Parameters:

target or legacy id, expected_revision?: reopen retained state, activate and request focus

Focus Next Panel

ParameterTypeRequiredDescription
expected_revisionresolve the focused shared panel control in the live hostNo
Automation details

Command ID: panel.focus.next

Parameters:

expected_revision?: resolve the focused shared panel control in the live host

Focus Previous Panel

ParameterTypeRequiredDescription
expected_revisionresolve the focused shared panel control in the live hostNo
Automation details

Command ID: panel.focus.previous

Parameters:

expected_revision?: resolve the focused shared panel control in the live host

List Panels

Automation details

Command ID: panel.list

Parameters:

{}: committed state, active tabs, capabilities, availability and registered kinds

Move Panel

Automation details

Command ID: panel.move

Parameters:

target or legacy id, destination: new_section, join_tab or relative, activate?: false, expected_revision?

Move Panel to Next Zone

ParameterTypeRequiredDescription
expected_revisionresolve the focused shared panel control in the live hostNo
Automation details

Command ID: panel.move.next_zone

Parameters:

expected_revision?: resolve the focused shared panel control in the live host

Redock Native Panel Section

ParameterTypeRequiredDescription
targetnative_hostYes
expected_revisionnative close redocks without cancelling contentNo
Automation details

Command ID: panel.native_close

Parameters:

target: native_host, expected_revision?: native close redocks without cancelling content

Open Panel

ParameterTypeRequiredDescription
kindYes
instanceYes
destinationrequired for new instanceNo
expected_revisionNo
Automation details

Command ID: panel.open

Parameters:

kind, instance, destination?: required for new instance, expected_revision?

Peek Collapsed Panel

Automation details

Command ID: panel.peek

Parameters:

target or legacy id, expected_revision?: transient presentation without resizing

Pop Out Panel

Automation details

Command ID: panel.popout

Parameters:

target or legacy id, destination: new_section with window location, expected_revision?: web lowers to floating

Reset Panel Sash

ParameterTypeRequiredDescription
firstreset adjacent dock pair to registered default ratioYes
secondreset adjacent dock pair to registered default ratioYes
expected_revisionreset adjacent dock pair to registered default ratioNo
Automation details

Command ID: panel.reset_pair

Parameters:

first, second, expected_revision?: reset adjacent dock pair to registered default ratio

Resize Panel Section

Automation details

Command ID: panel.resize

Parameters:

target or legacy id, size: {width,height}, thickness_only?: false by default; true changes Bottom height or side column width and ignores the other valid size component, expected_revision?

Resize Panel Sash

ParameterTypeRequiredDescription
firstconserve adjacent dock pair extentYes
secondconserve adjacent dock pair extentYes
first_sizeconserve adjacent dock pair extentYes
second_sizeconserve adjacent dock pair extentYes
expected_revisionconserve adjacent dock pair extentNo
Automation details

Command ID: panel.resize_pair

Parameters:

first, second, first_size, second_size, expected_revision?: conserve adjacent dock pair extent

Resize Panel Sash Down

Default keyboard shortcut: Down

ParameterTypeRequiredDescription
expected_revisionresolve the focused shared panel control in the live hostNo
Automation details

Command ID: panel.sash.down

Parameters:

expected_revision?: resolve the focused shared panel control in the live host

Resize Panel Sash Left

Default keyboard shortcut: Left

ParameterTypeRequiredDescription
expected_revisionresolve the focused shared panel control in the live hostNo
Automation details

Command ID: panel.sash.left

Parameters:

expected_revision?: resolve the focused shared panel control in the live host

Reset Focused Panel Sash

ParameterTypeRequiredDescription
expected_revisionresolve the focused shared panel control in the live hostNo
Automation details

Command ID: panel.sash.reset

Parameters:

expected_revision?: resolve the focused shared panel control in the live host

Resize Panel Sash Right

Default keyboard shortcut: Right

ParameterTypeRequiredDescription
expected_revisionresolve the focused shared panel control in the live hostNo
Automation details

Command ID: panel.sash.right

Parameters:

expected_revision?: resolve the focused shared panel control in the live host

Resize Panel Sash Up

Default keyboard shortcut: Up

ParameterTypeRequiredDescription
expected_revisionresolve the focused shared panel control in the live hostNo
Automation details

Command ID: panel.sash.up

Parameters:

expected_revision?: resolve the focused shared panel control in the live host

Toggle Focused Panel

ParameterTypeRequiredDescription
expected_revisionresolve the focused shared panel control in the live hostNo
Automation details

Command ID: panel.toggle

Parameters:

expected_revision?: resolve the focused shared panel control in the live host

Other commands: config

Configuration Column

Read the parameters and expressions guide.

ParameterTypeRequiredDescription
paramNotarget name
featureNotarget name
materialNotarget name
appearanceNotarget name
deleteboolNo
Automation details

Command ID: config.column

Parameters:

param? | feature? | material? | appearance? (target name); delete?: bool

Configuration Row

Read the parameters and expressions guide.

ParameterTypeRequiredDescription
nameYesa row's name, or its id from config.table
fromNorow to copy, by name or id; default current values
values{column key: expression, suppression, material name, appearance name or Look object}No
renameNo
deleteboolNo
Automation details

Command ID: config.row

Parameters:

name (a row's name, or its id from config.table); from? (row to copy, by name or id; default current values); values?: {column key: expression, suppression, material name, appearance name or Look object}; rename?; delete?: bool

Activate Configuration

Read the parameters and expressions guide.

Automation details

Command ID: config.activate

Parameters:

row: a row's name, or its id from config.table — sets its parameter values and suppressions

Preview Configuration

Read the parameters and expressions guide.

Automation details

Command ID: config.preview

Parameters:

row: a row's name, or its id from config.table — validates a rebuild without changing the design or history

Configuration Rule

Read the parameters and expressions guide.

ParameterTypeRequiredDescription
rule{id,name,enabled,condition,effect}No
rules[rule…]Noadded or replaced together, then the rows checked once
Automation details

Command ID: config.rule

Parameters:

rule?: {id,name,enabled,condition,effect}; rules?: [rule…] (added or replaced together, then the rows checked once); id? + delete?; id? + index? (ordered move)

Configuration Rules

Read the parameters and expressions guide.

Automation details

Command ID: config.rules

Parameters:

ordered typed rules, rows and active state

Other commands: parts

Standard Parts

ParameterTypeRequiredDescription
familyNo
Automation details

Command ID: parts.library

Parameters:

family? → families (name, standard, system: metric|inch, sizes, lengths) and, with family, each size's dimensions (mm)

Insert Part

ParameterTypeRequiredDescription
familyYessocket_head_cap_screw | button_head_cap_screw | countersunk_socket_screw | hex_bolt | hex_nut | washer | set_screw | dowel_pin | bearing | hex_cap_screw_inch | hex_nut_inch | washer_sae | washer_uss
sizeYes"M6", "608" for bearings, "1/2-13 UNC" for inch screws and nuts, "1/2" for inch washers
lengthNomm, inch parts too; screws, bolts, set screws, pins; default a standard length near 2.5 d
flipboolNodefault false: turned over, head into the material with at, +z against direction with point
nameNo
Automation details

Command ID: parts.insert

Parameters:

family (socket_head_cap_screw | button_head_cap_screw | countersunk_socket_screw | hex_bolt | hex_nut | washer | set_screw | dowel_pin | bearing | hex_cap_screw_inch | hex_nut_inch | washer_sae | washer_uss); size ("M6", "608" for bearings, "1/2-13 UNC" for inch screws and nuts, "1/2" for inch washers); length? (mm, inch parts too; screws, bolts, set screws, pins; default a standard length near 2.5 d); at?: [x,y,z] on a hole's rim or wall (the part seats on the hole's face with a rigid joint, head on the picked face and shank into the hole, whichever way the face points) or on a flat face (seated at that point, head out of the face), or a list of them (one component, placed and seated once per pick) | point?: [x,y,z] with direction?: [x,y,z] (no joint: the part's seating plane, under a head or the bottom of a nut, washer or bearing, the top of a set screw, pin or countersunk head, goes to point, and direction is its +z, from the shank's tip toward the head; default [0,0,1], head up, shank down -z); flip?: bool (default false: turned over, head into the material with at, +z against direction with point); name? → component, occurrence, joint?, directions (each placement's head direction in the world, the one chosen), and with at: occurrences, joints (one per pick, in pick order)

Change Part Size

ParameterTypeRequiredDescription
componentid|name of an inserted partYes
sizeNo
Automation details

Command ID: parts.resize

Parameters:

component: id|name of an inserted part; size?; length? — rebuilds it (joints keep it seated)

Other commands: body

Rename Body

ParameterTypeRequiredDescription
bodyits nameYes
bodiesseveral body namesYeseach in turn gets the first of name, name (2), name (3)… that no other body has
namethe new nameYesfeatures that use the bodies follow
Automation details

Command ID: body.rename

Parameters:

body: its name, or bodies: several body names (each in turn gets the first of name, name (2), name (3)… that no other body has), name: the new name (features that use the bodies follow)

Repair Part

ParameterTypeRequiredDescription
bodyits nameYesa part the modelling kernel refuses, or an open part that should be a solid
gapexprNodefault 0.2 mm, at most 1 mm: free edges that pair up within it are sewn, each sewn edge carrying its own gap as its tolerance
patch_holesboolNodefault false: loops of free edges left are patched, in the plane of the faces around them where those are that plane, else tangent to them, and sewn in; otherwise the reply offers the patches
nameNo
Automation details

Command ID: body.repair

Parameters:

body: its name (a part the modelling kernel refuses, or an open part that should be a solid); gap?: expr (default 0.2 mm, at most 1 mm: free edges that pair up within it are sewn, each sewn edge carrying its own gap as its tolerance); patch_holes?: bool (default false: loops of free edges left are patched, in the plane of the faces around them where those are that plane, else tangent to them, and sewn in; otherwise the reply offers the patches); name? → the feature, and repair: what changed face by face (rebuilt and refused faces, sewn gaps, holes with their surface.patch), faces and largest edge tolerance before and after, closed, modelling, volume. Refused with why when nothing applies

Default keyboard shortcut: Ctrl+B

ParameterTypeRequiredDescription
bodyits nameYes
Automation details

Command ID: body.print_face

Parameters:

body: its name; point?: [x, y, z] on a flat face of it, in mm (omit to clear) → {body, face?}: marks the face a slicer export (file.send_to_slicer, file.save_mesh with slicer, file.open_in_easyprint), or an STL or 3MF export with lay_on_print_faces (file.export, file.export_stl, file.export_3mf, file.save_mesh), lays the body on: turned so the face points down at Z = 0, the centre of mass over the origin. One per body, replacing the last; flat faces of solid bodies only. A print face that is gone or no longer flat is reported in the export's warnings, and the body exported as it lies

Other commands: document

Change Design Units

ParameterTypeRequiredDescription
unitsmm|cm|m|in|ftYesdisplay and unit-less parameters; values keep their meaning
Automation details

Command ID: document.units

Parameters:

units: mm|cm|m|in|ft (display and unit-less parameters; values keep their meaning)

Design Properties

Automation details

Command ID: document.metadata

Parameters:

→ metadata: the design's metadata object as saved (other tools' keys too); properties: each of SolidCraft's design properties (project, part number, revision…), its text or null (absent, or not text)

Change Design Properties

Automation details

Command ID: document.metadata.set

Parameters:

key and value, or values: {key: value, …} (all or nothing, one undo step). SolidCraft's keys, the ones document.metadata's properties lists, take text ("" or null removes); another tool's data goes under its reverse-DNS key such as com.example.tool (an object; null removes) → metadata, changed: [keys]

Remove Design Property

ParameterTypeRequiredDescription
key[keys]Yes
keys[keys]Yesany key, also one another tool wrote
Automation details

Command ID: document.metadata.remove

Parameters:

key, or keys: [keys] (any key, also one another tool wrote) → metadata, removed: [keys that were there]

Inspect Design

ParameterTypeRequiredDescription
measureboolNoinclude body measurements
sketch_countsboolNoeach sketch also gives its plane and its number of points and constraints
timelinetrue| "summary"| falseNodefault; each feature as id, name, type, with error, warning, suppressed and rolled_back only when set; left out
bodiestrue| "names"| falseNodefault; only the body names, nothing measured; left out
Automation details

Command ID: document.inspect

Parameters:

measure?: bool (include body measurements); sketch_counts?: bool (each sketch also gives its plane and its number of points and constraints); timeline?: true (default) | "summary" (each feature as id, name, type, with error, warning, suppressed and rolled_back only when set) | false (left out); bodies?: true (default) | "names" (only the body names, nothing measured) | false (left out)

Design Revision

Automation details

Command ID: document.revision

Parameters:

→ document_id (this open design's id: kept while it stays open, another for each design started or opened), revision (moves whenever the design, what is hidden or a component's uncaptured position changes, never on a selection or a view; from one counter for the whole run, so it never repeats). Read-only

Other commands: view

New Named View

Read the window tour.

ParameterTypeRequiredDescription
nameNo
camera{target: [x,y,z], yaw, pitch, distance, fov?}Yesreplaces a view of the same name
Automation details

Command ID: view.save

Parameters:

name?; camera: {target: [x,y,z], yaw, pitch, distance, fov?} (replaces a view of the same name)

Rename Named View

Read the window tour.

ParameterTypeRequiredDescription
viewnameYes
nameYes
Automation details

Command ID: view.rename

Parameters:

view: name, name

Delete Named View

Read the window tour.

ParameterTypeRequiredDescription
viewnameYes
Automation details

Command ID: view.delete

Parameters:

view: name

List Named Views

Read the window tour.

Automation details

Command ID: view.list

No parameter description is provided by the command registry.

Restore Named View

Read the window tour.

ParameterTypeRequiredDescription
viewfront|back|top|bottom|left|right|iso|home| a named view's nameNoframes the model
camera{target: [x,y,z], yaw, pitch, distance, fov?, roll?}Noradians, mm, about the up axis; yaw: heading of the line of sight, 0 along +Y (Front), π/2 along +X (Left), π (Back), −π/2 (Right); pitch: the eye's elevation, within ±π/2, π/2 = Top; roll defaults to 0, turns the picture clockwise, must be finite and is clamped to ±π; the View angles readout shows these in degrees; up is refused: the up axis comes from the design settings
Automation details

Command ID: view.apply

Parameters:

view?: front|back|top|bottom|left|right|iso|home (frames the model) | a named view's name; or camera?: {target: [x,y,z], yaw, pitch, distance, fov?, roll?} (radians, mm, about the up axis; yaw: heading of the line of sight, 0 along +Y (Front), π/2 along +X (Left), π (Back), −π/2 (Right); pitch: the eye's elevation, within ±π/2, π/2 = Top; roll defaults to 0, turns the picture clockwise, must be finite and is clamped to ±π; the View angles readout shows these in degrees; up is refused: the up axis comes from the design settings) → camera. The viewport (and the MCP screenshot) moves there. View state: not saved, not an undo step

Viewport Status Details

Read the window tour.

Automation details

Command ID: view.status_details

Parameters:

no parameters → current effect settings/detail capabilities and retained per-seam Continuity diagnostics, with current geometry validity; read-only, no evaluation

Viewport Status Items

Read the window tour.

Automation details

Command ID: view.status_items

Parameters:

no parameters → {items}; persistent viewport effects, oldest slot first, with structured settings and executable clear requests; read-only, no model evaluation

Fit

Default keyboard shortcut: F6

Read the window tour.

Automation details

Command ID: view.fit

Parameters:

no parameters → frame the active canvas or selection; display only

Home View

Read the window tour.

Automation details

Command ID: view.home

Parameters:

no parameters → home orientation framing the active canvas; display only

Read the window tour.

ParameterTypeRequiredDescription
toolorbit|pan|zoom|noneYes
Automation details

Command ID: view.tool

Parameters:

tool: orbit|pan|zoom|none → tool; latch a camera tool, none releases it

Drag to Zoom

Read the window tour.

ParameterTypeRequiredDescription
onboolNoomitted: toggle
Automation details

Command ID: view.tool.zoom

Parameters:

on?: bool (omitted: toggle) → on?; latch the Zoom tool for left-button drags. A shortcut assigned in the UI zooms while held

Continuous Orbit

Read the window tour.

ParameterTypeRequiredDescription
onboolNoomitted: toggle
Automation details

Command ID: view.spin

Parameters:

on?: bool (omitted: toggle) → on?; continuous orbit until the next click or key

Visual Style

Read the window tour.

ParameterTypeRequiredDescription
styleshaded_with_edges|shaded|wireframe|shaded_with_hidden_edgesYes
Automation details

Command ID: view.visual_style

Parameters:

style: shaded_with_edges|shaded|wireframe|shaded_with_hidden_edges → style, index (0..3 in that order)

Grid

Read the window tour.

ParameterTypeRequiredDescription
onboolNoomitted: toggle
Automation details

Command ID: view.grid

Parameters:

on?: bool (omitted: toggle) → on?; show the viewport grid

Ground Shadow

Read the window tour.

ParameterTypeRequiredDescription
onboolNoomitted: toggle
Automation details

Command ID: view.ground_shadow

Parameters:

on?: bool (omitted: toggle) → on?; show the ground shadow

Read the window tour.

ParameterTypeRequiredDescription
visibleboolNoomitted: toggle
Automation details

Command ID: view.navigation_bar

Parameters:

visible?: bool (omitted: toggle) → visible?; show the navigation bar (window setting)

Custom View Navigator

Read the window tour.

ParameterTypeRequiredDescription
styleaxes|cube|customNo
bodybody name|nullNo
texturethemed|modeledNo
arrowsboolNo
homeboolNo
Automation details

Command ID: view.navigator

Parameters:

style?: axes|cube|custom; body?: body name|null; texture?: themed|modeled; arrows?, home?: bool → the navigator as set; the app's view navigator, never saved in the design; never opens a dialog (a toolbar start opens the Custom View Navigator panel)

Ribbon Tabs Only

Read the window tour.

ParameterTypeRequiredDescription
onboolNoomitted: switch
Automation details

Command ID: view.ribbon_tabs_only

Parameters:

on?: bool (omitted: switch) → on?; fold the ribbon to its tab row, a tab click shows its panels over the view (window setting)

Render Focus View

Read the window tour.

ParameterTypeRequiredDescription
onboolYes
Automation details

Command ID: view.render_focus

Parameters:

on: bool → {on}; per-window UI state, never saved in the design; headless validation only

Render Frame Guides

Read the window tour.

ParameterTypeRequiredDescription
aspect16:9|3:2|1:1|4:5|9:16|customNo
Automation details

Command ID: view.render_frame

Parameters:

aspect?: 16:9|3:2|1:1|4:5|9:16|custom; thirds?, safe_area?, centre_mark?: bool. At least one field. Validated transient overlay request, echoed headlessly; never saves guides, changes output size, evaluates geometry or opens a dialog

Render Browser

Read the window tour.

ParameterTypeRequiredDescription
openboolYes
by_lookboolNo
searchboolNo
querystringNoup to 1024 bytes
Automation details

Command ID: view.render_browser

Parameters:

open: bool; or by_look?: bool; search?: bool; query?: string (up to 1024 bytes) → validated transient Browser controls; never edits the design or opens a dialog

Render Inspector

Read the window tour.

ParameterTypeRequiredDescription
visibleboolYes
Automation details

Command ID: view.render_inspector

Parameters:

visible: bool → {visible}; per-document Inspector visibility; never edits the design or opens a dialog

Pause or Resume Live Render

Read the window tour.

ParameterTypeRequiredDescription
pausedboolYes
Automation details

Command ID: view.render_live

Parameters:

paused: bool → {paused}; pause or resume this document's live Render preview; headless validation only, never saves settings or changes a final image

Render selection

Read the window tour.

ParameterTypeRequiredDescription
kindnothing|environment|lighting|light|shot|output|wear|sampling|effects|checkYes
idbounded nonempty string for light/shotNo
Automation details

Command ID: view.render_select

Parameters:

kind: nothing|environment|lighting|light|shot|output|wear|sampling|effects|check; id?: bounded nonempty string for light/shot → validated transient Browser focus; never edits the design or opens a dialog

Render Stage

Read the window tour.

ParameterTypeRequiredDescription
stagelook|light|frame|outputYes
Automation details

Command ID: view.render_stage

Parameters:

stage: look|light|frame|output → {stage}; per-document UI state, never edits or evaluates the design; headless validation only

Shots Strip

Read the window tour.

ParameterTypeRequiredDescription
visibleboolYes
Automation details

Command ID: view.shots_strip

Parameters:

visible: bool → {visible}; Render Shots strip UI preference. Headless echo only, no document edits or undo

Move Render Dock

Command group: RENDER › SCENE

Read the window tour.

ParameterTypeRequiredDescription
panelinspector|browserYes
sideleft|rightYes
Automation details

Command ID: view.render_dock

Parameters:

panel: inspector|browser; side: left|right → {panel, side}; Render dock UI preference; headless echo only, no document edits or undo

Workspace layout

Read the window tour.

ParameterTypeRequiredDescription
modefloating|compactYes
Automation details

Command ID: view.layout

Parameters:

mode: floating|compact → validated workspace layout; display only, never opens a dialog

History layout

Read the window tour.

ParameterTypeRequiredDescription
width1..1000000No
dockbottom|left|under_browser|browserNobrowser: the history in the browser, no timeline
collapsedboolNo
Automation details

Command ID: view.history_layout

Parameters:

width?: 1..1000000; dock?: bottom|left|under_browser|browser (browser: the history in the browser, no timeline); collapsed?: bool → validated layout request; display only, never opens a dialog

Graphics Preset

Read the window tour.

ParameterTypeRequiredDescription
presetperformance|qualityYes
Automation details

Command ID: view.graphics_preset

Parameters:

preset: performance|quality → preset, enabled, display_quality, supersampling, supersampling_adaptive; Quality: AO, Fine, adaptive 2x SSAA; Performance: no AO, Normal, no SSAA

Ambient Occlusion

Read the window tour.

ParameterTypeRequiredDescription
enabledboolYes
Automation details

Command ID: view.ambient_occlusion

Parameters:

enabled: bool → enabled; apply soft contact shading in the GPU viewport

Origin Axes Across the Whole View

Read the window tour.

ParameterTypeRequiredDescription
enabledboolYes
Automation details

Command ID: view.origin_axes_across_view

Parameters:

enabled: bool → enabled; extend the origin axes across the view, or keep the short axes when false; display only, never opens a dialog

Perspective view

Read the window tour.

ParameterTypeRequiredDescription
enabledboolYes
Automation details

Command ID: view.perspective

Parameters:

enabled: bool → enabled; draw the view in perspective, or orthographic when false; a sketch being edited is always shown orthographic

View Angles

Read the window tour.

ParameterTypeRequiredDescription
enabledboolYes
Automation details

Command ID: view.angle_readout

Parameters:

enabled: bool → enabled; show the view's pitch, yaw and roll (degrees, as view.apply's camera in radians) and Ortho or Persp under the orientation gizmo; display only, never opens a dialog

Read the window tour.

ParameterTypeRequiredDescription
cameraCameraYes
direction[x,y,z] in the camera's up-axis frameNo
actionleft|right|up|down|clockwise|counterclockwise|home|fitYes
degrees0..180Nohow far left|right|up|down turn; default 90
fitboolNoframe the whole model from the new direction as well, as home does; default false
motionpan|orbit|zoomYes
delta[dx,dy]Nopixels, each ±10000
free_orbitboolNoorbit through poles, default false
viewport[width,height]No1..1000000, required for pan or pivot
pivot[x,y,z]No
factor0.05..20Norequired for zoom
anchor[x,y,z]No
Automation details

Command ID: view.navigate

Parameters:

camera: Camera; direction?: [x,y,z] in the camera's up-axis frame; or action: left|right|up|down|clockwise|counterclockwise|home|fit, degrees?: 0..180 (how far left|right|up|down turn; default 90), fit?: bool (frame the whole model from the new direction as well, as home does; default false); or motion: pan|orbit|zoom, delta?: [dx,dy] (pixels, each ±10000), free_orbit?: bool (orbit through poles, default false), viewport?: [width,height] (1..1000000, required for pan or pivot), pivot?: [x,y,z], factor?: 0.05..20 (required for zoom), anchor?: [x,y,z] → camera, fit, continuous?

Normal to Selection

Read the window tour.

ParameterTypeRequiredDescription
cameraCameraNodefault: current session camera or home view
Automation details

Command ID: view.normal_to

Parameters:

camera?: Camera (default: current session camera or home view) → camera; Look At the first selected planar face or plane, centered on the picked point; repeat while aligned to flip the side; preserves scale and projection

Front View

Default keyboard shortcut: Ctrl+1

Read the window tour.

ParameterTypeRequiredDescription
cameraCameraNodefault: current session camera or home view
Automation details

Command ID: view.front

Parameters:

camera?: Camera (default: current session camera or home view) → camera; turns to that standard view as the view cube does, keeping target, distance and projection

Back View

Default keyboard shortcut: Ctrl+2

Read the window tour.

ParameterTypeRequiredDescription
cameraCameraNodefault: current session camera or home view
Automation details

Command ID: view.back

Parameters:

camera?: Camera (default: current session camera or home view) → camera; turns to that standard view as the view cube does, keeping target, distance and projection

Left View

Default keyboard shortcut: Ctrl+3

Read the window tour.

ParameterTypeRequiredDescription
cameraCameraNodefault: current session camera or home view
Automation details

Command ID: view.left

Parameters:

camera?: Camera (default: current session camera or home view) → camera; turns to that standard view as the view cube does, keeping target, distance and projection

Right View

Default keyboard shortcut: Ctrl+4

Read the window tour.

ParameterTypeRequiredDescription
cameraCameraNodefault: current session camera or home view
Automation details

Command ID: view.right

Parameters:

camera?: Camera (default: current session camera or home view) → camera; turns to that standard view as the view cube does, keeping target, distance and projection

Top View

Default keyboard shortcut: Ctrl+5

Read the window tour.

ParameterTypeRequiredDescription
cameraCameraNodefault: current session camera or home view
Automation details

Command ID: view.top

Parameters:

camera?: Camera (default: current session camera or home view) → camera; turns to that standard view as the view cube does, keeping target, distance and projection

Bottom View

Default keyboard shortcut: Ctrl+6

Read the window tour.

ParameterTypeRequiredDescription
cameraCameraNodefault: current session camera or home view
Automation details

Command ID: view.bottom

Parameters:

camera?: Camera (default: current session camera or home view) → camera; turns to that standard view as the view cube does, keeping target, distance and projection

Isometric View

Default keyboard shortcut: Ctrl+7

Read the window tour.

ParameterTypeRequiredDescription
cameraCameraNodefault: current session camera or home view
Automation details

Command ID: view.isometric

Parameters:

camera?: Camera (default: current session camera or home view) → camera; turns to that standard view as the view cube does, keeping target, distance and projection

Next Tab

Default keyboard shortcut: Ctrl+Tab

Read the window tour.

Automation details

Command ID: view.next_tab

Parameters:

no parameters → step: 1; the window shows the design tab to the right of the shown one (after the last, the first); never edits the design or opens a dialog

Previous Tab

Default keyboard shortcut: Ctrl+Shift+Tab

Read the window tour.

Automation details

Command ID: view.previous_tab

Parameters:

no parameters → step: -1; the window shows the design tab to the left of the shown one (before the first, the last); never edits the design or opens a dialog

Other commands: note

Add Note

ParameterTypeRequiredDescription
anchor[x,y,z]Nothe point in model space, mm; without it, where the selection points, or the origin
textthe noteYesmay span lines
hiddenboolNosaved but not drawn
Automation details

Command ID: note.add

Parameters:

anchor?: [x,y,z] (the point in model space, mm; without it, where the selection points, or the origin); text: the note (may span lines); hidden?: bool (saved but not drawn) → note: id

Edit Note

ParameterTypeRequiredDescription
noteidYes
textthe new textNo
anchor[x,y,z]Nomove the anchor
hiddenboolNoshow or hide
Automation details

Command ID: note.update

Parameters:

note: id; text?: the new text; anchor?: [x,y,z] (move the anchor); hidden?: bool (show or hide) → note: id

Delete Note

ParameterTypeRequiredDescription
noteidYes
Automation details

Command ID: note.delete

Parameters:

note: id → deleted: id

List Notes

Automation details

Command ID: note.list

Parameters:

→ notes: [{id, anchor: [x,y,z], text, hidden}] in creation order

Other commands: selection

Delete

ParameterTypeRequiredDescription
items[selection items]No
components[ids or names]No
occurrences[ids or names]No
canvases[ids or names]No
point_clouds[ids or names]No
joints[ids or names]No
dry_runboolNo
Automation details

Command ID: selection.delete

Parameters:

items?: [selection items]; components?, occurrences?, canvases?, point_clouds?, joints?: [ids or names]; dry_run?: bool → deleted, deleted_joints, would_fail, skipped (one undo step)

Get Selection

ParameterTypeRequiredDescription
detailboolNodefault true: resolve each item's geometry
Automation details

Command ID: selection.get

Parameters:

detail?: bool (default true: resolve each item's geometry) → {count, items: [{kind (= type: body|face|edge|vertex|feature|sketch_curve|sketch_point|sketch_midpoint|sketch_constraint|profile|plane|axis|construction_point|occurrence…), the selection's own fields as select.set takes them (body, index, point, id, sketch, path…), and with detail: face: name (persistent face name), name_exact, surface ({type: plane|cylinder|cone|sphere|torus|other, normal/point, origin/axis/radius…}), area_mm2, planar; edge: name (persistent edge name), curve (line|circle|arc|other), length_mm, start, end, center/axis/radius for round edges; body: volume_mm3, feature, visible; any body item: component {id, name} and occurrence_path in an assembly; sketch items: sketch (its id, as select.set takes it), sketch_name, entity (line|circle|arc|point|midpoint|…), position for points and midpoints}]}; read-only, [] when nothing is selected

Other commands: ui

Toggle Assistant

Default keyboard shortcut: Ctrl+Shift+A

Automation details

Command ID: ui.assistant

Parameters:

Show or hide Assistant in the app. Headless calls are refused without changing the design

Search Commands

Default keyboard shortcut: S

Automation details

Command ID: ui.palette

Parameters:

Open command search in the app. Headless calls are refused without changing the design

Show/Hide Selection

Default keyboard shortcut: V

Automation details

Command ID: ui.toggle_visibility

Parameters:

Show or hide what is selected in the app. Headless calls are refused without changing the design; browser.visibility takes explicit items

Other commands: edit

Undo

Default keyboard shortcut: Ctrl+Z

Automation details

Command ID: edit.undo

No parameter description is provided by the command registry.

Redo

Default keyboard shortcut: Ctrl+Y

Automation details

Command ID: edit.redo

No parameter description is provided by the command registry.

Repeat Last Command

Default keyboard shortcut: Shift+R

Automation details

Command ID: edit.repeat

Parameters:

Starts the last tool or dialog started in the app again, with its defaults (a toolbar click, not a replay of its values). Only the app has a tool to start: called here it is refused, and changes nothing

Checkpoint

Automation details

Command ID: edit.checkpoint

Parameters:

→ {checkpoint: id}: remember the design, active sketch and undo history (restore with edit.restore_checkpoint; the last 16 are kept, the oldest dropped first)

Restore Checkpoint

ParameterTypeRequiredDescription
checkpointidYes
Automation details

Command ID: edit.restore_checkpoint

Parameters:

checkpoint: id; forget?: bool (true = drop it without going back). Goes back to it, undo history included. Restoring uses it and every later checkpoint up: take a new one to go back again. Forgetting drops only it

Other commands: timeline

Play History

Command group: INSPECT › HISTORY

ParameterTypeRequiredDescription
actiontoggle|play|stopNodefault toggle
Automation details

Command ID: timeline.play

Parameters:

action?: toggle|play|stop (default toggle) → action: validated display request; the connected UI plays from the marker to the end with a half-second pause per rebuilt step. Headless callers only validate the request; no dialog, document edit or animation clock is created

Rename

ParameterTypeRequiredDescription
featureid|nameYes
nameYesreferences to it follow
Automation details

Command ID: timeline.rename

Parameters:

feature: id|name, name (references to it follow)

New Feature Folder

ParameterTypeRequiredDescription
nameNo
features[id|name]No
joints[id|name]No
groups[folder ids]No
parentfolder idNofeatures and joints leave any other timeline folder
Automation details

Command ID: timeline.group

Parameters:

name?; features?: [id|name]; joints?: [id|name]; groups?: [folder ids]; parent?: folder id (features and joints leave any other timeline folder)

Remove Feature Folders

ParameterTypeRequiredDescription
groupidYes
groups[ids]Yesfeatures and joints remain in the timeline
Automation details

Command ID: timeline.ungroup

Parameters:

group: id | groups: [ids] (features and joints remain in the timeline)

Rename Feature Folder

ParameterTypeRequiredDescription
groupidYes
nameYesrenames the folder holding features and joints
Automation details

Command ID: timeline.rename_group

Parameters:

group: id, name (renames the folder holding features and joints)

Move Features to Folder

ParameterTypeRequiredDescription
features[id|name]No
joints[id|name]No
groups[ids]No
groupidNoomit or null to move to the timeline root
Automation details

Command ID: timeline.move_to_group

Parameters:

features?: [id|name], joints?: [id|name], groups?: [ids], group?: id (omit or null to move to the timeline root)

Move Timeline Items

ParameterTypeRequiredDescription
features[id|name]No
groups[folder ids]No
groupfolder idNo
positioninsertion slotYes
activeboolNotrue = the moved contents end up before the history marker, false = behind it; other features keep their state
Automation details

Command ID: timeline.move_many

Parameters:

features?: [id|name], groups?: [folder ids], group?: folder id, position: insertion slot, active?: bool (true = the moved contents end up before the history marker, false = behind it; other features keep their state); atomically changes folder membership and moves all selected contents as one block

Move Timeline Marker

Automation details

Command ID: timeline.rollback

Parameters:

position: number of features to keep active, or "end" (omit = end)

Roll History Marker

Automation details

Command ID: timeline.roll_to

Parameters:

position: number of features to keep active, or "end" (omit = end) | feature: id|name (the marker goes right after it)

Reorder Feature

ParameterTypeRequiredDescription
featureid|nameYes
positionits new indexYes
activeboolNotrue = it ends up before the history marker, false = behind it; other features keep their state
Automation details

Command ID: timeline.reorder

Parameters:

feature: id|name, position: its new index, active?: bool (true = it ends up before the history marker, false = behind it; other features keep their state); refused when a feature that worked would fail, unless allow_errors?: bool (default false): then made, and broken: [{feature, error}] lists the features it broke → feature, position: its index now, from: its index before, moved: bool, note?: why nothing moved, or that a position past the end meant the last slot

Reorder Features

ParameterTypeRequiredDescription
features[id|name]Yes
positioninsertion slot in the current historyYes
activeboolNoas timeline.reorder's
Automation details

Command ID: timeline.reorder_many

Parameters:

features: [id|name], position: insertion slot in the current history; selected features keep their relative order; active?: bool (as timeline.reorder's); refused when a feature that worked would fail, unless allow_errors?: bool (default false): then made, and broken: [{feature, error}] lists the features it broke

Edit Feature

ParameterTypeRequiredDescription
featureid|nameYes
commandthe feature's command idYes
paramsits parametersYesthe feature is rebuilt in place
Automation details

Command ID: timeline.redefine

Parameters:

feature: id|name, command: the feature's command id, params: its parameters (the feature is rebuilt in place) → feature, recomputed, restored, rolled_back: bool (the feature is after the history marker, so the edit is not built yet; `note` then says so), warning?

Feature Dependents

ParameterTypeRequiredDescription
featureid|nameYes
Automation details

Command ID: timeline.dependents

Parameters:

feature: id|name → features deleted with it, and features that would fail without it

Suppress Feature

ParameterTypeRequiredDescription
featureid|nameYes
features[id|name]Yes
groups[timeline folder ids]Yesevery feature and joint in those folders and their nested folders; one undo step
suppressedboolNodefault toggles: features and joints are all suppressed unless all of them already are, then all are unsuppressed
Automation details

Command ID: timeline.suppress

Parameters:

feature: id|name | features: [id|name] | groups: [timeline folder ids] (every feature and joint in those folders and their nested folders; one undo step), suppressed?: bool (default toggles: features and joints are all suppressed unless all of them already are, then all are unsuppressed) → feature: id (or features: [ids]; groups: [ids] and joints: [ids] when groups are given), suppressed

Edit Feature

ParameterTypeRequiredDescription
featureid|nameYes
set{fields to change, merged into the feature's definition as timeline.inspect returns it, e.g. {"extent": {"distance": "30"}} (extrude), {"angle": "90 deg"} (revolve); a dotted key names a nested field ({"plane.distance": "8 mm"} = {"plane": {"distance": "8 mm"}}); expressions are text, or a number in mm or degrees}Yes
Automation details

Command ID: timeline.edit

Parameters:

feature: id|name, set: {fields to change, merged into the feature's definition as timeline.inspect returns it, e.g. {"extent": {"distance": "30"}} (extrude), {"angle": "90 deg"} (revolve); a dotted key names a nested field ({"plane.distance": "8 mm"} = {"plane": {"distance": "8 mm"}}); expressions are text, or a number in mm or degrees} → recomputed: features rebuilt, restored: features whose earlier result for the same inputs was reused

Inspect Feature

ParameterTypeRequiredDescription
groupfolder idYes
Automation details

Command ID: timeline.inspect

Parameters:

group: folder id → group, name, parent, features, joints; or feature: id|name → type, definition (the fields timeline.edit's set changes, with their current values), inputs: [{label, param, expression, unit}] (named inputs parameters.change can also set), error, warning

Copy Features

ParameterTypeRequiredDescription
features[ids or names]Yesin timeline order on paste
Automation details

Command ID: timeline.copy

Parameters:

features: [ids or names] (in timeline order on paste)

Paste Features

ParameterTypeRequiredDescription
translate[x,y,z]Nomoves the copies' geometry
componentwhere the copies goNodefault: the active one
component_pathits placementNodefault: the active one, else its only one
from_designtrue to paste sketch features from the host's cross-design clipboard snapshotNo
Automation details

Command ID: timeline.paste

Parameters:

translate?: [x,y,z] (moves the copies' geometry); component?: where the copies go (default: the active one), component_path?: its placement (default: the active one, else its only one); the copies go at the marker, each where its original is in the world (copies into their own component keep its coordinates); body transfers are left out; from_design?: true to paste sketch features from the host's cross-design clipboard snapshot

Add Recorded Feature

ParameterTypeRequiredDescription
featurea feature as the design file and timeline.inspect hold itYes{type, name?, id?, …}; the name is kept even when another feature has it, the id is kept and refused when another feature has it, so later steps can refer to them
params[{name, expression, unit?, comment?}]Nothe model parameters its sketch dimensions use
Automation details

Command ID: timeline.add

Parameters:

feature: a feature as the design file and timeline.inspect hold it ({type, name?, id?, …}; the name is kept even when another feature has it, the id is kept and refused when another feature has it, so later steps can refer to them); params?: [{name, expression, unit?, comment?}] (the model parameters its sketch dimensions use); the feature goes at the marker → feature, name, error, warning

Export Command Script

ParameterTypeRequiredDescription
pathwrite the script to this fileNo
Automation details

Command ID: timeline.export_script

Parameters:

path?: write the script to this file → script: {commands: [...]} that rebuilds the design (solidcraft-cli eval/run), steps, omitted: what the script leaves out (appearances, folders, views, configurations, print-face assignments); components and assemblies are not supported yet

Other commands: select

Select

Read the window tour.

ParameterTypeRequiredDescription
items[{type: body|edge|face|feature|sketch_curve|sketch_point|profile, …}]Yes
addboolNo
Automation details

Command ID: select.set

Parameters:

items: [{type: body|edge|face|feature|sketch_curve|sketch_point|profile, …}], add?: bool; face/edge: body plus point [x,y,z] in world mm or index (both also accepted); a point uses Measure's pick tolerance, an index supplies a point on the item; invalid partial picks leave the selection unchanged

Selection filter

Read the window tour.

ParameterTypeRequiredDescription
modebody|face|componentYes
Automation details

Command ID: select.filter

Parameters:

mode: body|face|component → validated UI pick granularity; headless echo only, no document edits or undo

Clear Selection

Read the window tour.

Automation details

Command ID: select.clear

No parameter description is provided by the command registry.

Select Same Look

Read the window tour.

ParameterTypeRequiredDescription
bodyworld body nameNo
faceface indexNo
Automation details

Command ID: select.same_look

Parameters:

body?: world body name, face?: face index; defaults to the current body/face selection → selects objects using the resolved appearance

Other commands: parameters

List Parameters

Read the parameters and expressions guide.

ParameterTypeRequiredDescription
favoritesboolNoonly favourites
filterall|user|sketch|feature|favoritesNo
Automation details

Command ID: parameters.list

Parameters:

favorites?: bool (only favourites), filter?: all|user|sketch|feature|favorites → parameters (name, expression, unit, value (mm/deg in every reply), display_value (in its own unit), source, feature/input, comment, favorite, uses, used_by, error) and cycles

Add User Parameter

Read the parameters and expressions guide.

ParameterTypeRequiredDescription
nameYes
expressionYes
unitas parameters.changeNotext: a text parameter, its expression the text
commentNo
favoriteboolNo
Automation details

Command ID: parameters.add

Parameters:

name, expression, unit?: as parameters.change (text: a text parameter, its expression the text), comment?, favorite?: bool

Rename Parameter

Read the parameters and expressions guide.

ParameterTypeRequiredDescription
nameYes
new_nameYesexpressions, feature inputs and dimensions that use it follow
Automation details

Command ID: parameters.rename

Parameters:

name, new_name (expressions, feature inputs and dimensions that use it follow)

Delete Parameter

Read the parameters and expressions guide.

ParameterTypeRequiredDescription
nameYesrefused while something uses it; the error lists the users
Automation details

Command ID: parameters.delete

Parameters:

name (refused while something uses it; the error lists the users)

Favorite Parameter

Read the parameters and expressions guide.

ParameterTypeRequiredDescription
nameboolYes
favoriteboolNodefault true
Automation details

Command ID: parameters.favorite

Parameters:

name, favorite?: bool (default true)

Move Parameter

Read the parameters and expressions guide.

ParameterTypeRequiredDescription
nameanother user parameterYes
beforeanother user parameterYes
afteranother user parameterYes
Automation details

Command ID: parameters.move

Parameters:

name, before | after: another user parameter; the user parameter is listed just before or after it

Comment Parameter

Read the parameters and expressions guide.

ParameterTypeRequiredDescription
nameYes
commentYes
Automation details

Command ID: parameters.comment

Parameters:

name, comment

Parameter Users

Read the parameters and expressions guide.

ParameterTypeRequiredDescription
nameYes
Automation details

Command ID: parameters.users

Parameters:

name → users (parameters, feature inputs, dimensions) and the features it drives

Parameter Graph

Read the parameters and expressions guide.

Automation details

Command ID: parameters.graph

Parameters:

→ nodes, edges [user, used] (users include Feature:<name>), cycles, errors

Export Parameters

Read the parameters and expressions guide.

ParameterTypeRequiredDescription
formatcsv|jsonNodefault csv, or from the path
pathwrite to a fileNo
Automation details

Command ID: parameters.export

Parameters:

format?: csv|json (default csv, or from the path), path?: write to a file → text

Import Parameters

Read the parameters and expressions guide.

ParameterTypeRequiredDescription
textCSVYesheader with name, expression, unit?, comment?
Automation details

Command ID: parameters.import

Parameters:

text: CSV (header with name, expression, unit?, comment?) or JSON [{name, expression, unit?, comment?}] | path; all or nothing

Complete Expression

Read the parameters and expressions guide.

ParameterTypeRequiredDescription
prefixthe text before the cursorNo
Automation details

Command ID: parameters.complete

Parameters:

prefix?: the text before the cursor → word, suggestions [{text, kind: parameter|function|unit|constant, detail}]

Other commands: expr

Evaluate Expression

Read the parameters and expressions guide.

ParameterTypeRequiredDescription
expressionlength|angle|unitlessYes
kindlength|angle|unitlessNoor unit?: mm|in|deg…; text: the text a text parameter would hold, as `text`
Automation details

Command ID: expr.evaluate

Parameters:

expression, kind?: length|angle|unitless (or unit?: mm|in|deg…; text: the text a text parameter would hold, as `text`) → ok, value (mm/rad), display_value, references, error

Other commands: file

New Design

Default keyboard shortcut: Ctrl+N

Read the import and export guide.

ParameterTypeRequiredDescription
nameNo
Automation details

Command ID: file.new

Parameters:

name?

Save

Default keyboard shortcut: Ctrl+S

Read the import and export guide.

ParameterTypeRequiredDescription
pathNodefault: current file; a name that opens as another kind of file, such as .step, .stl or .dxf, is refused: file.export writes those
formatcontainer|jsonNodefault: json for .json, otherwise container
compressionfast|compactNodefault: fast
cacheboolNothe evaluated model, so the design opens without rebuilding; default: true
previewboolNosaved display meshes, default: false
overwriteboolNoreplace the design's file even though another program or computer changed it since it was opened or saved; without it that save is refused
signboolNothe app's own Save only: sign the save with GnuPG, appending a provenance record; container only; a script or agent is refused
signing_keyfingerprint or user idNodefault: GnuPG's default signing key
Automation details

Command ID: file.save

Parameters:

path? (default: current file; a name that opens as another kind of file, such as .step, .stl or .dxf, is refused: file.export writes those); format?: container|json (default: json for .json, otherwise container); compression?: fast|compact (default: fast); cache?: bool (the evaluated model, so the design opens without rebuilding; default: true); preview?: bool (saved display meshes, default: false); overwrite?: bool (replace the design's file even though another program or computer changed it since it was opened or saved; without it that save is refused); sign?: bool (the app's own Save only: sign the save with GnuPG, appending a provenance record; container only; a script or agent is refused); signing_key?: fingerprint or user id (default: GnuPG's default signing key) → signature: {method, fingerprint, user_id?} when signed

Save As

Default keyboard shortcut: Ctrl+Shift+S

Read the import and export guide.

ParameterTypeRequiredDescription
pathYesa design file name, .sc as a rule; one that opens as another kind of file, such as .step, .stl or .dxf, is refused: file.export writes those
formatcontainer|jsonNodefault: json for .json, otherwise container
compressionfast|compactNodefault: fast
cacheboolNothe evaluated model, so the design opens without rebuilding; default: true
previewboolNosaved display meshes, default: false
overwriteboolNoas file.save, when `path` is the design's own file
signbool and signing_key?Noas file.save, and likewise only from the app's own Save
Automation details

Command ID: file.save_as

Parameters:

path (a design file name, .sc as a rule; one that opens as another kind of file, such as .step, .stl or .dxf, is refused: file.export writes those); format?: container|json (default: json for .json, otherwise container); compression?: fast|compact (default: fast); cache?: bool (the evaluated model, so the design opens without rebuilding; default: true); preview?: bool (saved display meshes, default: false); overwrite?: bool (as file.save, when `path` is the design's own file); sign?: bool and signing_key? (as file.save, and likewise only from the app's own Save)

Verify Signature

Read the import and export guide.

ParameterTypeRequiredDescription
pathNodefault: the design's current file
Automation details

Command ID: file.verify

Parameters:

path? (default: the design's current file) → {path, status, reason?, truncated, records: [{method, key_id, user_id?, author?, created?, format, covered}]}: status is unsigned, valid, not_covered (signed earlier, saved since without a key), invalid or unsupported

Signing Keys

Read the import and export guide.

Automation details

Command ID: file.gpg_keys

Parameters:

→ {available, keys: [{fingerprint, key_id, user_id, on_card}]}: the GnuPG secret keys a save can sign with (desktop only)

Export

Read the import and export guide.

ParameterTypeRequiredDescription
pathYes
formatstl|stla|obj|step|3mf|3mf-tagged|glb|urdf|jsonNodefault from extension; 3mf-tagged: 3MF whose triangles carry their B-rep face, at full precision, for meshing and analysis tools; docs/formats/3mf-face-tags.md; obj: the colours of the bodies go in a material library (`.mtl`) written next to the mesh file; glb: binary glTF in metres with Y up, each body placed and named, its colours, metallic and roughness as materials; a transparent body or face, or a body a render setup gives an image texture or optics, is refused; urdf: the whole assembly as a robot, with STL meshes written next to it; docs/urdf.md
bodies[names]NoJSON and URDF export the complete design
qualitystandard|fine|finestNoSTL, OBJ, 3MF and GLB: how closely the mesh follows curved faces, a chord tolerance of 2e-4, 5e-5 or 1e-5 of each body's size; default standard
objectsbody|componentNo3MF only: one object per body, the default, or one object for each placed component with two or more bodies, with those bodies as its parts, so a slicer loads them as one object with parts
lay_on_print_facesboolNoSTL and 3MF only, default false: each body with a print face (body.print_face) is written laid on it, as a slicer export lays it → print_faces: the bodies laid, warnings: each body that has a print face but was written as it lies
Automation details

Command ID: file.export

Parameters:

path; format?: stl|stla|obj|step|3mf|3mf-tagged|glb|urdf|json (default from extension; 3mf-tagged: 3MF whose triangles carry their B-rep face, at full precision, for meshing and analysis tools; docs/formats/3mf-face-tags.md; obj: the colours of the bodies go in a material library (`.mtl`) written next to the mesh file; glb: binary glTF in metres with Y up, each body placed and named, its colours, metallic and roughness as materials; a transparent body or face, or a body a render setup gives an image texture or optics, is refused; urdf: the whole assembly as a robot, with STL meshes written next to it; docs/urdf.md); bodies?: [names] (JSON and URDF export the complete design); urdf only: mesh_prefix? (before each mesh file name, e.g. package://robot/meshes/), effort? (default 1000), velocity? (default 10); quality?: standard|fine|finest (STL, OBJ, 3MF and GLB: how closely the mesh follows curved faces, a chord tolerance of 2e-4, 5e-5 or 1e-5 of each body's size; default standard); objects?: body|component (3MF only: one object per body, the default, or one object for each placed component with two or more bodies, with those bodies as its parts, so a slicer loads them as one object with parts); lay_on_print_faces?: bool (STL and 3MF only, default false: each body with a print face (body.print_face) is written laid on it, as a slicer export lays it → print_faces: the bodies laid, warnings: each body that has a print face but was written as it lies)

Export STEP

Read the import and export guide.

ParameterTypeRequiredDescription
pathYes
bodies[names]Nodefault: every body; a design with components then exports as a STEP assembly
Automation details

Command ID: file.export_step

Parameters:

path; bodies?: [names] (default: every body; a design with components then exports as a STEP assembly) → {path, bytes, format}: STEP, whatever the path's extension

Export STL

Read the import and export guide.

ParameterTypeRequiredDescription
pathYes
bodies[names]Nodefault: every body
qualitystandard|fine|finestNodefault standard
lay_on_print_facesboolNodefault false: as file.export
Automation details

Command ID: file.export_stl

Parameters:

path; bodies?: [names] (default: every body); quality?: standard|fine|finest (default standard); lay_on_print_faces?: bool (default false: as file.export) → {path, bytes, format, print_faces?, warnings?}: binary STL, whatever the path's extension

Export 3MF

Read the import and export guide.

ParameterTypeRequiredDescription
pathYes
bodies[names]Nodefault: every body
qualitystandard|fine|finestNodefault standard
objectsbody|componentNoas file.export
lay_on_print_facesboolNodefault false: as file.export
Automation details

Command ID: file.export_3mf

Parameters:

path; bodies?: [names] (default: every body); quality?: standard|fine|finest (default standard); objects?: body|component (as file.export); lay_on_print_faces?: bool (default false: as file.export) → {path, bytes, format, print_faces?, warnings?}: 3MF, whatever the path's extension

Export OBJ

Read the import and export guide.

ParameterTypeRequiredDescription
pathYes
bodies[names]Nodefault: every body
qualitystandard|fine|finestNodefault standard
Automation details

Command ID: file.export_obj

Parameters:

path; bodies?: [names] (default: every body); quality?: standard|fine|finest (default standard) → {path, bytes, format, files}: OBJ, with the colours of the bodies in a material library (`.mtl`) next to it

Export URDF

Read the import and export guide.

ParameterTypeRequiredDescription
pathYes
mesh_prefixNo
effortNodefault 1000
velocityNodefault 10
Automation details

Command ID: file.export_urdf

Parameters:

path; mesh_prefix?; effort? (default 1000); velocity? (default 10) → {path, format, files, links, joints, warnings}: the whole assembly as a robot, with STL meshes written next to it; docs/urdf.md

Save As Mesh

Command group: INSERT › FILES

Read the import and export guide.

ParameterTypeRequiredDescription
pathYes
formatstl|3mf|3mf-taggedNodefault from the extension, else stl; 3mf-tagged: docs/formats/3mf-face-tags.md
bodies[names]Nodefault: all
asciiboolNoSTL
single_instanceboolNodefault true; see file.send_to_slicer
qualitystandard|fine|finestNoSTL, OBJ and 3MF: how closely the mesh follows curved faces, a chord tolerance of 2e-4, 5e-5 or 1e-5 of each body's size; default standard
objectsbody|componentNo3MF only: one object per body, the default, or one object for each placed component with two or more bodies, with those bodies as its parts, so a slicer loads them as one object with parts
lay_on_print_facesboolNodefault false: each body with a print face (body.print_face) is saved laid on it; a file sent to `slicer` is laid on them whatever this says
Automation details

Command ID: file.save_mesh

Parameters:

path; format?: stl|3mf|3mf-tagged (default from the extension, else stl; 3mf-tagged: docs/formats/3mf-face-tags.md); bodies?: [names] (default: all); ascii?: bool (STL); slicer?: id or name from file.slicers, the program path it lists, or a program the user added under Custom… (send the saved file there, through the same steps as file.send_to_slicer: a slicer that has to be started first is waited for; may be deprecated later in favour of file.send_to_slicer or a Send to App workflow, kept for scripts); single_instance?: bool (default true; see file.send_to_slicer); quality?: standard|fine|finest (STL, OBJ and 3MF: how closely the mesh follows curved faces, a chord tolerance of 2e-4, 5e-5 or 1e-5 of each body's size; default standard); objects?: body|component (3MF only: one object per body, the default, or one object for each placed component with two or more bodies, with those bodies as its parts, so a slicer loads them as one object with parts); lay_on_print_faces?: bool (default false: each body with a print face (body.print_face) is saved laid on it; a file sent to `slicer` is laid on them whatever this says)

Send to Slicer

Command group: INSERT › FILES

Read the import and export guide.

ParameterTypeRequiredDescription
bodies[names]Nodefault: the selected bodies
waitblock|return|noNodefault block: a slicer that drops a file at a cold start is started alone and the call waits up to 10 s for it to be ready, then sends; return: start it and answer {starting}; no: send now
single_instanceboolNodefault true: PrusaSlicer, SuperSlicer and Cura load the file in the window that is open
format3mf|stl|3mf-taggedNodefault 3mf, in millimetres; STL if that can't be written
Automation details

Command ID: file.send_to_slicer

Parameters:

slicer: id or name from file.slicers, or a program the user added under Custom…; bodies?: [names] (default: the selected bodies); wait?: block|return|no (default block: a slicer that drops a file at a cold start is started alone and the call waits up to 10 s for it to be ready, then sends; return: start it and answer {starting}; no: send now); single_instance?: bool (default true: PrusaSlicer, SuperSlicer and Cura load the file in the window that is open); format?: 3mf|stl|3mf-tagged (default 3mf, in millimetres; STL if that can't be written) → saves the bodies in this user's slicer folder and opens the file in the slicer. The file name stays the same for the same design and bodies, so sending again replaces it (use the slicer's Reload from disk)

Open with EasyPrint

Read the import and export guide.

ParameterTypeRequiredDescription
consentboolYesrequired: true only once the user has agreed to upload the model to Printables' servers; it stays there for 30 minutes and opening it needs a Printables sign-in
bodies[names]Nodefault: the selected bodies
format3mf|stlNodefault 3mf, in millimetres
Automation details

Command ID: file.open_in_easyprint

Parameters:

consent: bool (required: true only once the user has agreed to upload the model to Printables' servers; it stays there for 30 minutes and opening it needs a Printables sign-in); bodies?: [names] (default: the selected bodies); format?: 3mf|stl (default 3mf, in millimetres) → uploads the bodies to EasyPrint, Printables' slicer in the browser, and opens it in the browser. 100 MB at most. Works in the desktop app and the browser build; the CLI has no way to upload yet

Installed Slicers

Read the import and export guide.

Automation details

Command ID: file.slicers

Parameters:

→ the slicers found on this machine: [{id, name, program, icon?}]; pass an id, a name or the program path as file.send_to_slicer's or file.save_mesh's `slicer`

Clear Reused Imports

Read the import and export guide.

Automation details

Command ID: file.import_cache_clear

Parameters:

→ removed: number of local cached imports deleted; leaves open designs and remembered stand-up answers unchanged

Export Current View as DXF

Read the import and export guide.

ParameterTypeRequiredDescription
cameraCameraYescurrent view
hiddenomit|dashedNodefault omit
tolerance0.0001..1 mmNocurve chord tolerance, default 0.01
curveslines|arcsNolines, default, writes the short lines the view is built from; arcs writes visible circles and arcs as CIRCLE and ARC
zerotarget|originNowhere the drawing's 0,0 is: the camera target, default, or the model origin as seen from the camera
path.dxf fileNoelse returns dxf text
Automation details

Command ID: file.export_view_dxf

Parameters:

camera: Camera (current view); hidden?: omit|dashed (default omit); tolerance?: 0.0001..1 mm (curve chord tolerance, default 0.01); curves?: lines|arcs (lines, default, writes the short lines the view is built from; arcs writes visible circles and arcs as CIRCLE and ARC); zero?: target|origin (where the drawing's 0,0 is: the camera target, default, or the model origin as seen from the camera); path?: .dxf file (else returns dxf text). Visible placed bodies and shown sketches' profile curves (no construction geometry, points, dimensions or constraints); 2D LINE/LWPOLYLINE entities on VISIBLE/HIDDEN layers (sketch curves on SKETCH), mm; finished sketches are hidden behind bodies, the active sketch is drawn on top; perspective projected onto the camera target plane; no viewport crop or overlays.

Other commands: doc

Open

Default keyboard shortcut: Ctrl+O

Read the import and export guide.

ParameterTypeRequiredDescription
unitsfor an STL or OBJ fileNosee file.insert_mesh
originbool for a mesh fileNosee file.insert_mesh
reuse_importboolNoSTEP/IGES: reuse an unchanged import from the local saved-model cache, default true; false recomputes without reading or writing the cache; → import_reused: bool, content_sha256: exact source digest
historyfeatures|solids for a FreeCAD fileNosee file.insert_step
componentsboolNodefault true
silkscreenboolNodefault true: the silk shapes and text
color"#rrggbb" or [r, g, b]Noboard substrate colour
representationmesh|brepNothe board itself; default brep: a solid whose faces can be picked
read_onlyboolNoa design: saving to its own file is refused, file.save_as keeps the changes elsewhere
verifyboolNodefault false: a design's signature is checked and reported, `signature`
Automation details

Command ID: doc.open

Parameters:

path: .sc design (or a .solidcraft design from an earlier release), .dxf/.svg drawing (opens as a new, unsaved design with an XY sketch, using sketch.insert_dxf/sketch.insert_svg units and defaults), or a .step/.stp/.igs/.fcstd/.3dm/.3mf/.stl/.obj/.glb/.gltf/.kicad_pro/.kicad_pcb file (STEP, IGES, FreeCAD, Rhino, 3MF, STL, OBJ, glTF or KiCad PCB; opens as a new design holding the file as a component named after it, as file.insert_step and file.insert_mesh make), or a .ply/.xyz/.pts point cloud (opens as a new design holding it, as pointcloud.insert makes; its `units` and `max_points` as there); up?: z|y for a STEP, IGES, FreeCAD, Rhino or glTF file (see file.insert_step and file.insert_mesh); names?: auto|solid|product|instance for a STEP, IGES, FreeCAD or Rhino file (see file.insert_step); units?: for an STL or OBJ file (see file.insert_mesh); origin?: bool for a mesh file (see file.insert_mesh); folder?: bool for a STEP, IGES, FreeCAD or Rhino file (see file.insert_step); reuse_import?: bool (STEP/IGES: reuse an unchanged import from the local saved-model cache, default true; false recomputes without reading or writing the cache; → import_reused: bool, content_sha256: exact source digest); history?: features|solids for a FreeCAD file (see file.insert_step); for a KiCad PCB: thickness?: board thickness in mm (default from the board); components?: bool (default true); copper?: "overlay" (default: flat copper just above the board), "none" (leave the traces out) or "mesh" (modelled with thickness; true also means mesh; pads are included whenever copper is enabled); silkscreen?: bool (default true: the silk shapes and text); color?: "#rrggbb" or [r, g, b] (board substrate colour); representation?: mesh|brep (the board itself; default brep: a solid whose faces can be picked); read_only?: bool (a design: saving to its own file is refused, file.save_as keeps the changes elsewhere); verify?: bool (default false: a design's signature is checked and reported, `signature`) → open_elsewhere: {host, user, app, opened} when another SolidCraft has the design open (its lock file is fresh); newer_file: {format, reads_up_to, unknown_changes, not_understood, notice} and warnings when a newer SolidCraft saved it: it opens all the same, and what this version can't read is named there and kept by saving

Recoverable Designs

Read the import and export guide.

ParameterTypeRequiredDescription
dirNodefault: the recovery folder
Automation details

Command ID: doc.recovery_list

Parameters:

dir? (default: the recovery folder) → designs autosaved by apps that crashed or closed with unsaved changes

Recover Design

Read the import and export guide.

ParameterTypeRequiredDescription
idYesfrom doc.recovery_list
dirNo
discardboolNodelete the entry once open
unsavedYes
Automation details

Command ID: doc.recover

Parameters:

id (from doc.recovery_list); dir?; discard?: bool (delete the entry once open) — opens it with its file path, unsaved

Discard Recovered Design

Read the import and export guide.

ParameterTypeRequiredDescription
idtrueYes
alltrueYes
dirNo
Automation details

Command ID: doc.recovery_discard

Parameters:

id, or all: true; dir?

Other commands: appearance

Copy Look

Default keyboard shortcut: Ctrl+Alt+C

ParameterTypeRequiredDescription
targetappearance.chain targetYes
Automation details

Command ID: appearance.copy

Parameters:

target: appearance.chain target. Copies its resolved look to a transient session clipboard. No document change or dialogs.

Paste Look

Default keyboard shortcut: Ctrl+Alt+V

Automation details

Command ID: appearance.paste

Parameters:

bodies/faces/components: appearance.assign targets. Applies the copied look atomically in one undo step; no dialogs.

Favorite Look

ParameterTypeRequiredDescription
lookappearance.assign lookYes
favoriteboolYes
Automation details

Command ID: appearance.favorite

Parameters:

look: appearance.assign look; favorite: bool → validated look and preference request; UI library preference only, no document change or dialogs.

Appearance Chain

ParameterTypeRequiredDescription
target{body: name} | {body: name, face: zero-based model.faces index} | {component: name or id}Yes
Automation details

Command ID: appearance.chain

Parameters:

target: {body: name} | {body: name, face: zero-based model.faces index} | {component: name or id}; a placed copy's name (`KB (K:2)`, `K:2`) means the body or component it shows. Returns steps [{step: face|body|component|material, look: {name,color,opacity,pbr?} or null}] in precedence order and active (step or null). Component material is the common default look of its directly owned bodies, null if mixed/empty. Imported paint is a display fallback outside the four steps. Read-only; no dialogs

Replace Appearance

ParameterTypeRequiredDescription
fromlibrary name | saved look {name,color,opacity,pbr?}Yescopy appearance.list in_design
Automation details

Command ID: appearance.replace

Parameters:

from: library name | saved look {name,color,opacity,pbr?} (copy appearance.list in_design); to: library name | assignment look {appearance? | color, name?, opacity?, pbr?} (appearance.assign rules; no clear). Matches the full look including name and PBR. scope?: {bodies?: [names], components?: [names or ids]} (a placed copy's name means the body or component it shows), at most 10000 targets; body scope includes face overrides, component scope selects only the component override. Omitted scope means all overrides, including hidden/suppressed ones; physical defaults are unchanged. Duplicate targets count once. Returns count of rewritten overrides; zero adds no history. One undo step, atomic; no dialogs. Designs above 100000 overrides refuse

Appearance Library

Automation details

Command ID: appearance.library

Parameters:

→ 20 legacy scalar appearances, six opaque PBR finishes and 40 starter recipes with IDs, categories, provenance, requirements and availability; six starter glass recipes are unavailable

List Appearances

ParameterTypeRequiredDescription
bodynameNoor a placed copy's, `KB (K:2)`: the body it shows
Automation details

Command ID: appearance.list

Parameters:

body?: name (or a placed copy's, `KB (K:2)`: the body it shows) → assigned appearances; with body, its resolved look and its faces' (face index, look)

Paint Appearance

Default keyboard shortcut: B

Automation details

Command ID: appearance.paint

Parameters:

appearance or color/name/opacity/pbr: appearance.assign look parameters; validates and returns look for ui.start paint. done?: true ends the UI tool. Programmatic calls never open UI.

Pick Up Appearance

Default keyboard shortcut: I

ParameterTypeRequiredDescription
targetappearance.chain targetNo
Automation details

Command ID: appearance.pick_up

Parameters:

target?: appearance.chain target; returns its resolved look, or requests the UI sampling tool when omitted. Programmatic calls never open UI.

Other commands: material

List Materials

Automation details

Command ID: material.list

Parameters:

→ built-in and design-owned materials (user: true, look, optional physical_card/physical_validation/density_comparison). Read-only; candidate engineering records do not certify sources or supply a solver

New Physical Material

ParameterTypeRequiredDescription
nameYesnot a built-in material's
density_g_cm3Yes
appearancelibrary nameNoappearance.library
color"#rrggbb" or [r, g, b]No0–255
opacityNo0–1
pbr{metallic:0..1, roughness:0.045..1, dielectric_ior:1..3}Noopaque only, needs color/appearance
pbrnull selects scalar shadingYes
Automation details

Command ID: material.create

Parameters:

name (not a built-in material's); density_g_cm3: (0, 1000]; appearance?: library name (appearance.library) | color?: "#rrggbb" or [r, g, b] (0–255), opacity? (0–1), pbr?: {metallic:0..1, roughness:0.045..1, dielectric_ior:1..3} (opaque only, needs color/appearance): the default look. A named appearance keeps its preset response when pbr is omitted; pbr: null selects scalar shading. Optical edits do not change density. physical_card?: validated SI candidate record (null/omitted means unknown); card density never changes assigned density. Assign it with material.assign

Edit Physical Material

ParameterTypeRequiredDescription
materiala material defined in this designYes
nameNobodies and configuration cells follow
density_g_cm3No
appearanceNo
colorNo
opacityNo
pbrNoas material.create; color: null clears the look
Automation details

Command ID: material.edit

Parameters:

material: a material defined in this design; name? (bodies and configuration cells follow); density_g_cm3?; appearance? | color?, opacity?, pbr? (as material.create; color: null clears the look). physical_card?: validated SI candidate record (omitted retains, null clears). Omitted appearance/color retains the entire current look

Inspect Physical Material

Automation details

Command ID: material.inspect

Parameters:

material: built-in or design-owned name. Returns assigned density, default appearance, optional physical_card, validation and density comparison. Read-only; missing engineering data remains unknown and card density does not change mass

Delete Physical Material

ParameterTypeRequiredDescription
materiala material defined in this designYes
replacematerial for the bodies and configuration cells that use itNowithout it, a material in use is refused
Automation details

Command ID: material.delete

Parameters:

material: a material defined in this design; replace?: material for the bodies and configuration cells that use it (without it, a material in use is refused)

Validate Physical Material Data

Automation details

Command ID: material.properties.validate

Parameters:

card: schema_version 1 engineering material card in SI units. Returns numerical consistency, derived isotropic moduli, missing conditions and sources needing review. Read-only; does not assign/save properties or provide an analysis solver. See docs/physical-material-cards.md

Directional Elastic Modulus

ParameterTypeRequiredDescription
cardschema_version 1 engineering material cardYes
Automation details

Command ID: material.properties.directional

Parameters:

card: schema_version 1 engineering material card; direction: three finite nonzero-vector components in the card's material-local axes. Returns normalized direction, Young modulus in Pa and source/condition review. Read-only constitutive query; no structural solver, persistence or body-axis assignment. See docs/physical-material-cards.md

Other commands: engine

List Commands

Automation details

Command ID: engine.commands

No parameter description is provided by the command registry.

Describe Command

Automation details

Command ID: engine.describe

Parameters:

command: a command id. Returns {id, label, params, input, output} (and param_warnings in the warn parameter-check mode): the command's parameter documentation and, once the command has a typed contract, the JSON Schemas (2020-12) of its parameters and of a successful result (null otherwise). Read-only.

Other commands: library

Inspect Local Library

ParameterTypeRequiredDescription
pathv1/v2 library JSONYes
Automation details

Command ID: library.inspect

Parameters:

path: v1/v2 library JSON; returns validated catalog and exact entry revisions, without changing the design

Export Design Library

ParameterTypeRequiredDescription
path.jsonYes
Automation details

Command ID: library.export

Parameters:

path: .json; id? (default user.local), name?, provenance?, overwrite? false, format? solidcraft-library/2 (default) | solidcraft-library/1 (refuses PBR/cards). Copies assigned looks and design-local physical definitions; never changes the design

Apply Local Library Entry

ParameterTypeRequiredDescription
pathYes
entryqualified IDYes
revisionexact content SHA-256Yes
targets{bodies?, faces?: [{body,point}], components?}No
namephysical material name on importNo
Automation details

Command ID: library.apply

Parameters:

path, entry: qualified ID, revision: exact content SHA-256; targets?: {bodies?, faces?: [{body,point}], components?}; name?: physical material name on import. Copies values in one undo step; material conflicts are explicit

Other commands: settings

Settings Profiles

Automation details

Command ID: settings.profile.list

Parameters:

→ profiles: [name], active: name|null; handled by the app

Save Settings Profile

ParameterTypeRequiredDescription
name1..64 charactersYes
Automation details

Command ID: settings.profile.save

Parameters:

name: 1..64 characters → name; keeps the current input and interface settings under `name` (replacing a profile of that name) and makes it the active one

Apply Settings Profile

ParameterTypeRequiredDescription
nameYes
Automation details

Command ID: settings.profile.apply

Parameters:

name → name, applied, skipped: [preference]; switches every setting the profile holds at once

Rename Settings Profile

ParameterTypeRequiredDescription
name1..64 charactersYes
to1..64 charactersYes
Automation details

Command ID: settings.profile.rename

Parameters:

name, to: 1..64 characters → name

Delete Settings Profile

ParameterTypeRequiredDescription
nameYes
Automation details

Command ID: settings.profile.delete

Parameters:

name → deleted

Export Settings Profile

ParameterTypeRequiredDescription
namefileYes
pathfileNo
Automation details

Command ID: settings.profile.export

Parameters:

name, path?: file → profile (a `solidcraft-profile/1` document), written to `path` when given; never opens a dialog

Import Settings Profile

ParameterTypeRequiredDescription
pathfileYes
profilesolidcraft-profile/1 documentYes
name1..64 charactersNo
Automation details

Command ID: settings.profile.import

Parameters:

path: file | profile: `solidcraft-profile/1` document, name?: 1..64 characters → name; adds the profile without switching to it

Other commands: diagnostics

Crash Reports

Automation details

Command ID: diagnostics.crash_reports

Parameters:

limit?: number (default 20). Lists this computer's crash reports, newest first: {dir, reports: [{id, kind, time, message, command, count, version, path, replayable}]}. kind: command_panic | panic | kernel_panic_recovered | unclean_exit. Read one with diagnostics.crash_report; reproduce it with `solidcraft-cli crashes replay <id>`

Crash Report

Automation details

Command ID: diagnostics.crash_report

Parameters:

id: report id. The whole report: message, location, backtrace, app version and commit, system, the command that crashed and the recent ones, notes, and design_path / replay_path when the crash can be replayed (`solidcraft-cli run <replay_path> --in <design_path>`)

Delete Crash Reports

ParameterTypeRequiredDescription
idone reportNodefault: all
Automation details

Command ID: diagnostics.crash_reports.clear

Parameters:

id?: one report (default: all). Returns {deleted}

Profile

ParameterTypeRequiredDescription
enabledboolNostart or stop recording
resetboolNoforget what was recorded
toprowsNodefault 20, 0: all
Automation details

Command ID: diagnostics.profile

Parameters:

enabled?: bool (start or stop recording); reset?: bool (forget what was recorded); top?: rows (default 20, 0: all); trace?: path (write the recorded spans as a Chrome trace-event file, for chrome://tracing or Perfetto). Returns what was recorded before enabled and reset apply: {compiled, enabled, wall_ms, spans, dropped, by_category: [{category, self_ms, share}], rows: [{category, name, count, total_ms, self_ms, max_ms}], rows_total, trace?}, the rows the most self time first. Categories: solidcore (the kernel's calls), kernel, feature (one per feature computed, by its type), doc, command, import (STEP/IGES stages), io. Self time is a span's time less the spans inside it; total counts its outermost run only. Recording also starts with SOLIDCRAFT_PROFILE=1, the CLI's --profile and the app's preference; off, it costs nothing measurable. Read-only

Validation

Automation details

Command ID: diagnostics.validation

Parameters:

mode?: each_step | each_feature | off (default: leave it). When the kernel's full check runs on the features computed from now on: after every sweep, boolean and blend (each_step), once on the bodies each feature makes or changes (each_feature, the default; a body it refuses still fails its feature, with the check's reason), or never (off, experimental). Results already there stand until their features are computed again (timeline.compute_all). The app's preference of the same name sets it. Returns {mode, previous}

Crash Test

Automation details

Command ID: diagnostics.crash_test

Parameters:

mode: panic | kernel | worker | worker_raw | abort. Checks that crash reports work: panic = an internal error in this command (caught; the design is unchanged), kernel = one inside the geometry kernel (caught), worker / worker_raw = one on a Rayon worker, caught inside / outside the kernel's guard, abort = the app ends at once (reported at the next start). Only when the SOLIDCRAFT_CRASH_TEST environment variable is set

Other commands: render

Import Surface Maps

ParameterTypeRequiredDescription
nameYes
settyped CPU raytrace or GPU path setup patchNo
frameworld-mm origin/u/v physical repeat vectorsYes
mapsexplicit base_color_srgb/normal_opengl/roughness_linear/metallic_linear {path,expected_sha256?}Yes
provenance{author,source,license}Yes
normal_strength1No
flip_vfalseNo
replacefalseNo
Automation details

Command ID: render.surface.import

Parameters:

name; set?: typed CPU raytrace or GPU path setup patch; body_index and body_name from render.scene; frame: world-mm origin/u/v physical repeat vectors; maps: explicit base_color_srgb/normal_opengl/roughness_linear/metallic_linear {path,expected_sha256?}; provenance:{author,source,license}; normal_strength?:1; flip_v?:false; replace?:false. Captures exact PNG/JPEG bytes and saves definition-local binding in one undo. CPU maps require noninterlaced 8-bit PNG. Saved designs reopen without the source files.

List Embedded Surface Images

Automation details

Command ID: render.texture.list

Parameters:

No parameters. Lists bounded design-owned texture digests, byte counts, dimensions, declared provenance and referencing setups; never returns image bytes or source filesystem paths.

Remove Embedded Surface Image

Automation details

Command ID: render.texture.remove

Parameters:

sha256: exact embedded image digest. Refuses while any saved setup uses the image, listing its users. Removing an unused image is one undo step.

Set Render Environment

ParameterTypeRequiredDescription
nameYes
setnumerical setup patchNo
Automation details

Command ID: render.environment.set

Parameters:

name; set?: numerical setup patch; rotation_deg?: finite degrees within +/-1000000, wrapped to [-180,180); intensity?: finite linear intensity 0..64 CPU HDR/constant or 0..100 CPU/GPU Sky and GPU HDR. Edits the existing HDR, sky or constant environment in one undo, without selecting a source, moving the camera or starting a render. Absent environments and raster refuse.

Set Render Light

ParameterTypeRequiredDescription
nameYes
settingsfull unsaved RenderSetupNo
expected_revisionu64No
Automation details

Command ID: render.lights.set

Parameters:

name; id except add; settings?: full unsaved RenderSetup; expected_revision?:u64; set for set/add: kind directional/point/spot/area, intensity_ev, temperature_k, softness, azimuth_deg, elevation_deg, distance_mm, size_mm, aim_at, width_mm, height_mm, position_mm, direction, edge_u_mm, edge_v_mm, radiance, colour, reference_distance_mm, minimum_distance_mm. Validated finite light edit in one undo. Existing family caps and backend support apply; refusal is atomic. Names remain defaults.

Add Light

ParameterTypeRequiredDescription
nameYes
settingsfull unsaved RenderSetupNo
expected_revisionu64No
Automation details

Command ID: render.lights.add

Parameters:

name; id except add; settings?: full unsaved RenderSetup; expected_revision?:u64; set for set/add: kind directional/point/spot/area, intensity_ev, temperature_k, softness, azimuth_deg, elevation_deg, distance_mm, size_mm, aim_at, width_mm, height_mm, position_mm, direction, edge_u_mm, edge_v_mm, radiance, colour, reference_distance_mm, minimum_distance_mm. Validated finite light edit in one undo. Existing family caps and backend support apply; refusal is atomic. Names remain defaults.

Duplicate Render Light

ParameterTypeRequiredDescription
nameYes
settingsfull unsaved RenderSetupNo
expected_revisionu64No
Automation details

Command ID: render.lights.duplicate

Parameters:

name; id except add; settings?: full unsaved RenderSetup; expected_revision?:u64; set for set/add: kind directional/point/spot/area, intensity_ev, temperature_k, softness, azimuth_deg, elevation_deg, distance_mm, size_mm, aim_at, width_mm, height_mm, position_mm, direction, edge_u_mm, edge_v_mm, radiance, colour, reference_distance_mm, minimum_distance_mm. Validated finite light edit in one undo. Existing family caps and backend support apply; refusal is atomic. Names remain defaults.

Remove Render Light

ParameterTypeRequiredDescription
nameYes
settingsfull unsaved RenderSetupNo
expected_revisionu64No
Automation details

Command ID: render.lights.remove

Parameters:

name; id except add; settings?: full unsaved RenderSetup; expected_revision?:u64; set for set/add: kind directional/point/spot/area, intensity_ev, temperature_k, softness, azimuth_deg, elevation_deg, distance_mm, size_mm, aim_at, width_mm, height_mm, position_mm, direction, edge_u_mm, edge_v_mm, radiance, colour, reference_distance_mm, minimum_distance_mm. Validated finite light edit in one undo. Existing family caps and backend support apply; refusal is atomic. Names remain defaults.

List Render Lights

ParameterTypeRequiredDescription
setupsaved setup name OR settings?: complete RenderSetup snapshotNo
Automation details

Command ID: render.lights.list

Parameters:

setup?: saved setup name OR settings?: complete RenderSetup snapshot; neither uses defaults. Returns {lights:[{id,kind,name,name_key,number,implicit,intensity,colour,...spatial fields}]}. IDs are kind:zero-based-source-slot, stable across numerical edits but not family changes/reordering/removal/rig replacement; point and spot share punctual slots. Includes implicit default/preset lights. GPU RGB radiance is factored as colour * intensity; area edges are full world-mm vectors. Read-only, no host, camera change, save, render, model evaluation or undo. See docs/render-settings.md.

Layered Surface Aging

Automation details

Command ID: render.aging.describe

Parameters:

Empty object. Read-only original Rust/patina/dust/grime/scratch/chip/fading presets and exact GPU aging contract. UI activation opens the shared Aging tab; edits remain a saved render draft, never engineering geometry or material-property mutation.

Lens and Creative Effects

Automation details

Command ID: render.effects.describe

Parameters:

Empty object. Read-only original photographic/creative filter defaults and constraints. UI activation opens shared Effects controls. Saved finishing and exact-master regrading preserve raw EXR bytes; no physical lens or branded-film calibration is implied.

Save Clear Coat

ParameterTypeRequiredDescription
nameYes
setnumerical setup patchNo
expected_revisionYes
coat{weight:0..1,roughness:0.045..1,ior:1..3}Yes
replacefalseNo
Automation details

Command ID: render.coat.bind

Parameters:

name; set?: numerical setup patch; body_index/body_name: exact render.scene identity; expected_revision; coat:{weight:0..1,roughness:0.045..1,ior:1..3}; replace?:false. Stores up to the existing 64 target bound as stable definition/feature/component/occurrence bindings for CPU/GPU. Even zero-weight coats require opaque targets. Same-target Glass refuses. One undo; no geometry, engineering material or appearance mutation.

Remove Clear Coat

ParameterTypeRequiredDescription
nameYes
targetexact saved definition/feature/component/occurrence_pathYes
Automation details

Command ID: render.coat.unbind

Parameters:

name; target: exact saved definition/feature/component/occurrence_path; expected_revision. Removes only that stable coat assignment in one undo, without device or file access.

Save Optical Binding

ParameterTypeRequiredDescription
nameCPU or GPU setupYes
setcomplete numerical setup patchNo
expected_revisionYes
optics{ior,absorption_per_mm:[r,g,b],boundary:closed_mesh|closed_box|thin_sheet,thickness_mm,shadow_transmission,subsurface?:{scattering_per_mm,anisotropy},model?:{roughness:0..1,dispersion?:{abbe_number:5..200},thin_film?:{ior:1..3,thickness_nm:0..100000}}}Yes
Automation details

Command ID: render.optics.bind

Parameters:

name: CPU or GPU setup; set?: complete numerical setup patch; body_index and body_name: exact captured target; expected_revision; optics:{ior,absorption_per_mm:[r,g,b],boundary:closed_mesh|closed_box|thin_sheet,thickness_mm,shadow_transmission,subsurface?:{scattering_per_mm,anisotropy},model?:{roughness:0..1,dispersion?:{abbe_number:5..200},thin_film?:{ior:1..3,thickness_nm:0..100000}}}; replace?:false. The optional model adds isotropic rough transmission, three-band dispersion and one lossless coherent film. CPU models require explicit Path; rough sheets require straight transmitted shadows off. See docs/render-glass-material-models.md. Stores stable definition/feature/occurrence identity in one undo without modifying engineering geometry/materials. CPU Path consumes at most 128 certified optical targets; the legacy non-Path producer retains eight. Without options.path, sheets and axis-aligned boxes retain the existing transport. Explicit progressive options.path enables exact closed-mesh certification, bounded nested convex-parent media, Beer/Snell/Fresnel and thin transmitted shadows; touching or unproven boundaries and closed transmitted-shadow connections refuse explicitly. Existing subsurface parameters may be consumed on certified closed Path media with denoising explicitly off; scalar free-flight/HG walks and separate Beer absorption are supported without refracting-boundary next-event approximation. A zero scattering rate preserves transport. GPU nested media require separate job.options.nested_media and producer certification. Unresolved or incompatible targets refuse atomically.

Remove Optical Binding

ParameterTypeRequiredDescription
namesaved setupYes
targetexact saved definition/feature/component/occurrence_pathYes
Automation details

Command ID: render.optics.unbind

Parameters:

name: saved setup; target: exact saved definition/feature/component/occurrence_path; expected_revision. Removes only that optical assignment in one undo without a device or file access.

Regrade Linear Master

ParameterTypeRequiredDescription
jobexact completed JobIdNodefault selected result
Automation details

Command ID: render.result.regrade

Parameters:

job?: exact completed JobId (default selected result); presentation: {exposure_ev?,tone_map?,finishing?,backdrop?,background?,transparent_background?}. Queues cancellable nondestructive PNG presentation from that retained native FLOAT32 master, not from PNG. Captured geometry/camera/lighting and source EXR remain unchanged; no path tracing, denoising, external EXR import or automatic file writes. Returns a distinct job with source identity and original-master digest. Existing frame retention and export rules still apply.

Set Render Camera

Automation details

Command ID: render.camera.set

Parameters:

camera: complete Render Camera snapshot (target, yaw, pitch, distance, fov; roll defaults to 0 and up to z). Validates the same finite mm/radian bounds as saved render intent, preserves explicit roll/up and changes only transient view state. No model evaluation, setup save, renderer, dirty flag or undo step. Design view.apply retains its separate up-axis policy.

Inspect Render Camera

Automation details

Command ID: render.camera.get

Parameters:

No parameters. Returns the explicitly defined current view camera, or null if the session has no camera override. No camera is fitted, no job is started and no saved setup or engineering document is changed.

Frame Render Subject

Automation details

Command ID: render.camera.fit

Parameters:

aspect?: the picture's width over its height, 0.000001 to 1000000 (default 1). Fits the current camera to the visible product bounds through the shared Camera::fit contract, moving back for a picture taller than wide so its sides hold the product too; changes only view state, not a saved setup, rendering job, stage or engineering geometry.

Review Native Studio

ParameterTypeRequiredDescription
idNATIVE-STUDIO-001..025Yes
nameYes
settyped CPU/GPU setup patchNo
Automation details

Command ID: render.preset.resolve

Parameters:

id: NATIVE-STUDIO-001..025; name; set?: typed CPU/GPU setup patch; expected_revision?:u64. Resolves original built-in HDR/rig/passive ground with conservative geometry clearance, returns all replacements and preserved fields without mutation. Unbound SCENE-* concepts are not accepted.

Apply Native Studio

Automation details

Command ID: render.preset.apply

Parameters:

Same reviewed input as render.preset.resolve, expected_revision required, replace_lighting_and_environment:true and replace_stage:true when an existing stage would change. Re-resolves the complete candidate and publishes one undo or no mutation; camera, optical bindings, geometry and engineering materials are preserved.

Review Fixed Sampling Budget

ParameterTypeRequiredDescription
idQ-*Yes
Automation details

Command ID: render.quality.prepare

Parameters:

id: Q-*; variant:fixed_samples_png for the existing GPU projection, or cpu_progressive_png for Q-001/002/003 CPU cost budgets; acknowledge_omissions:true. Optional cpu:{minimum_budget:compact|studio|workstation,adaptive?:captured AdaptiveSampling} selects the smallest existing profile admitting pilots plus production without lowering minimum_budget. CPU budgets use authored sizes, 16/32/64 production samples and seed21026; preserve transport depth, denoise, optics and effects. Read-only numerical projections, not convergence/time guarantees or complete quality acceptance. Never switches executor, adopts fixed CPU sampling or mutates a shot.

Duplicate Render Shot

ParameterTypeRequiredDescription
nameu64Yes
new_nameu64Yes
expected_revisionu64Yes
Automation details

Command ID: render.shot.duplicate

Parameters:

name, new_name, expected_revision:u64. Copies exact saved setup and resource references in one undo. Existing destinations refuse; captured jobs and camera navigation are unchanged.

Rename Render Shot

ParameterTypeRequiredDescription
nameu64Yes
new_nameu64Yes
expected_revisionu64Yes
Automation details

Command ID: render.shot.rename

Parameters:

name, new_name, expected_revision:u64. Renames exact saved setup in one undo; an existing destination or stale review refuses.

Remove Render Shot

ParameterTypeRequiredDescription
nameu64Yes
expected_revisionu64Yes
Automation details

Command ID: render.shot.remove

Parameters:

name, expected_revision:u64. Removes only that saved setup in one undo; retains resources, geometry and captured results.

Inspect Original Surface Map

Automation details

Command ID: render.library.preview

Parameters:

id: SURF-*. Returns bounded hash-verified original base-color PNG bytes and source-map fidelity label, not a rendered material thumbnail. No external paths, rendering or document edits.

Prepare Physical Reference

ParameterTypeRequiredDescription
idnamed built-in physical profileYes
nameproposed new material labelYes
acknowledge_referencetrueYes
Automation details

Command ID: render.library.physical.prepare

Parameters:

id: named built-in physical profile, name: proposed new material label, acknowledge_reference:true. Read-only loss-aware creation plan using native ValidatedCard admission and one point density; returns source receipts, extended fields and omissions. Does not execute material.create, assign bodies or infer effective-part physics.

Save Render Ground

ParameterTypeRequiredDescription
nametyped raytrace setupYes
setsetup patchNo
margin_fraction0.25No
gap_mm0.1No
reflectance_linear[0.18,0.18,0.18]No
replace_stagefalseNo
Automation details

Command ID: render.stage.ground

Parameters:

name: typed raytrace setup, set?: setup patch, margin_fraction?:0.25, gap_mm?:0.1, reflectance_linear?:[0.18,0.18,0.18], replace_stage?:false. Fits one finite passive ground below the visible product once and saves its physical transforms in one undo. Product AABB separation is rechecked on every render.

Save Physical Render Background

ParameterTypeRequiredDescription
nametyped raytrace setupYes
setsetup patchNo
kindfloor_wall or cycloramaYes
reflectance_linear[0.18,0.18,0.18]No
expected_revisionreviewed revisionYes
replace_stagefalseNo
Automation details

Command ID: render.stage.backdrop

Parameters:

name: typed raytrace setup, set?: setup patch, kind: floor_wall or cyclorama, reflectance_linear?: [0.18,0.18,0.18], expected_revision: reviewed revision, replace_stage?:false. Fits a finite floor/wall or a 24-strip polygonal cyclorama behind the visible subject using the saved camera azimuth; world +Z up and a minimum one-mm stage envelope. Preserves subject geometry, lighting, output, pose and engineering materials; one undo and rechecked product separation. Not an infinite/analytically smooth/terrain background.

Remove Render Stage

ParameterTypeRequiredDescription
namesetupYes
setsetup patchNo
Automation details

Command ID: render.stage.clear

Parameters:

name: setup, set?: setup patch. Removes saved render-only panels atomically; CAD geometry and lighting are unchanged.

Save Surface Binding

ParameterTypeRequiredDescription
nameCPU or GPU setupYes
setsetup patchNo
surfacesame input as render.library.surface.prepareYes
Automation details

Command ID: render.library.surface.bind

Parameters:

name: CPU or GPU setup; set?: setup patch; surface: same input as render.library.surface.prepare; replace?: false. Pins exact source hashes and definition/feature/occurrence identity with local-mm mapping in one undo. No paths, mesh ordinals or physical material changes.

Remove Surface Binding

ParameterTypeRequiredDescription
namesetupYes
targetsaved definition/feature/component/occurrence_pathYes
setsetup patchNo
Automation details

Command ID: render.library.surface.unbind

Parameters:

name: setup, target: saved definition/feature/component/occurrence_path, set?: setup patch. Removes only that explicit binding in one undo; no device or current geometry required.

Retain Displayed Render Result

Automation details

Command ID: render.result.pin

Parameters:

job?: completed JobId (default selected result), or null to clear selection. Selects within the eight retained completed results; oldest is evicted first. Preserves exact export bytes and original host memory accounting. No design/undo change; preview or foreign jobs refuse.

Find Render Library Targets

ParameterTypeRequiredDescription
queryexact-name substringNo
offset0No
Automation details

Command ID: render.library.targets

Parameters:

query?: exact-name substring; offset?:0; limit?:128 (1..128). Returns a bounded page of captured body ordinals/names, revision and scene bounds. Settles the model through ordinary command handling; never returns a large mesh payload.

Browse Render Library

ParameterTypeRequiredDescription
querybounded searchNo
availabilityall/usable/specification/blockedNo
offset0No
Automation details

Command ID: render.library.list

Parameters:

kind?: surfaces, selene_appearances, hdr, rigs, scenes, materials, lighting, environments, quality, native_scenes, selene_shots or capabilities; query?: bounded search; availability?: all/usable/specification/blocked; offset?:0; limit?:48 (1..128). Returns source status and explicit implemented projections separately. No device, file IO or design change.

Inspect Render Library Resource

Automation details

Command ID: render.library.describe

Parameters:

kind, id: exact authored resource. Returns original specification, runtime projection variants and explicit omissions. A concept is not an executable preset.

Apply Library Opaque Appearance

ParameterTypeRequiredDescription
idSURF-* or SELENE-AP-*Yes
variantuniform_opaqueYesrequired
Automation details

Command ID: render.library.appearance.apply

Parameters:

id: SURF-* or SELENE-AP-*; variant: uniform_opaque (required); bodies: 1..256 exact body names. Applies the original scalar baseline through ordinary appearance assignment in one undo, preserving physical properties. Spatial maps and advanced target effects are explicitly excluded.

Apply Built-in HDR Environment

ParameterTypeRequiredDescription
idHDR-*Yes
namesaved setupYes
settyped CPU/GPU setup patchNo
rotation_radauthored rotationNonative origin offset is explicit in the reply
intensity1No
samples16 for CPUNo
Automation details

Command ID: render.library.environment.apply

Parameters:

id: HDR-*; name: saved setup; set?: typed CPU/GPU setup patch; rotation_rad?: authored rotation (native origin offset is explicit in the reply); intensity?:1; samples?:16 for CPU; replace_environment?:false. Hash-verifies and embeds original bytes, saves one atomic undo without external files or a render device. An HDR-owned sun cannot duplicate an analytic sun.

Prepare Authored Surface Maps

ParameterTypeRequiredDescription
idSURF-*Yes
origin[0,0,0]No
planexy/yz/zxNo
scale1No
Automation details

Command ID: render.library.surface.prepare

Parameters:

id: SURF-*; body_index and body_name from render.scene; origin?:[0,0,0]; plane?:xy/yz/zx; scale?:1; rotation_rad?:0 (positive around the plane normal). Returns an explicit GPU temporary planar binding and a source usable by saved CPU/GPU surface.bind, with source hashes, original millimetre scale, image-up normal orientation and absolute base-color semantics. No document change; height/displacement and true anisotropy are not included.

Prepare Authored Studio Lights

ParameterTypeRequiredDescription
idRIG-*Yes
Automation details

Command ID: render.library.studio.prepare

Parameters:

id: RIG-*; variant: complete for emitter-only rigs, emitters_only for rigs with passive cards; center_mm and subject_extent_mm required. Converts exact rectangular half-edges and solar direction/radius. Optional name and typed CPU set return the complete CPU projection and explicit indirect-lighting adoption plan; authored radiance beyond CPU intensity64 refuses without scaling. Blocked calibration and external-sun rigs refuse. Returns a geometry-clearance review requirement; does not create cards or modify the scene.

Apply Authored Studio Lights

ParameterTypeRequiredDescription
nameYes
settyped CPU/GPU setup patchNo
expected_revisionu64No
enable_indirectfalseNoexplicit CPU Sun adoption after review
Automation details

Command ID: render.library.studio.apply

Parameters:

rig: id, variant, center_mm and subject_extent_mm; name; set?: typed CPU/GPU setup patch; expected_revision?:u64; enable_indirect?:false (explicit CPU Sun adoption after review); replace_lighting_and_environment:true required. Saves the explicit rig projection in one undo, removing prior lighting/environment only with that confirmation. Subject clearance still needs review; passive cards are never silently omitted.

Inspect HDR Source

ParameterTypeRequiredDescription
pathscoped Radiance HDR fileYes
Automation details

Command ID: render.hdr.inspect

Parameters:

path: scoped Radiance HDR file; validates the complete bounded source and returns sha256, width, height, encoded_bytes and primaries without saving or rendering

Save Render Settings with HDR

ParameterTypeRequiredDescription
nametyped CPU/GPU setup patch with hdr/environment unsetYes
settyped CPU/GPU setup patch with hdr/environment unsetYes
pathscoped HDRYes
expected_sha256digest returned by render.hdr.inspectNochanged sources refuse
rotation_rad0No
intensity1No
samples1/4/16/64 for CPU onlyNodefault16
Automation details

Command ID: render.settings.capture_hdr

Parameters:

name, set: typed CPU/GPU setup patch with hdr/environment unset; path: scoped HDR; expected_sha256?: digest returned by render.hdr.inspect (changed sources refuse); rotation_rad?:0, intensity?:1; samples?:1/4/16/64 for CPU only (default16); assume_linear_srgb?:false for GPU only. Captures immutable bytes and saves resource plus setup atomically in one undo, without a host; returns name/setup/resource metadata, never source bytes

Check Render Request

ParameterTypeRequiredDescription
setupsaved name OR settings?: full RenderSetupNo
progressivefalseNoregistered GPU path, CPU integrator/LED options are mutually exclusive
texture_filteringfalseNoGPU
max_depth1..64No
seedu32 for progressiveNo
environment{path: scoped HDR, expected_sha256?: inspected digest, rotation_radians: 0, intensity: 1, samples: 16} OR {resource: saved HDR digest, rotation_radians, intensity, samples}No
Automation details

Command ID: render.preflight

Parameters:

setup?: saved name OR settings?: full RenderSetup. A typed job selects raster/cpu_raytrace/gpu_path and its numerical options plus embedded HDR before dispatch; only replace and transient studio_weights may accompany it. Without job, the legacy runtime controls below remain available. See docs/render-settings.md. studio_weights?: three finite 0..1 visibility weights for raster or implicit GPU studio sources only; never saved. integrator?: raster (default) or raytrace; progressive?: false (registered GPU path, CPU integrator/LED options are mutually exclusive); texture_filtering?: false (GPU); CPU raytrace.texture_filtering?: false. Explicitly enables ray-footprint mip and bounded anisotropic map filtering on GPU, and bounded anisotropic filtering on CPU. GPU filtering off retains bilinear LOD0; CPU filtering off retains isotropic mip selection, using the coarsest available mip when footprint data is unavailable. samples?: 1..65536; max_depth?: 1..64; seed?: u32 for progressive; raytrace?: bounded runtime progressive/primary_samples/lights/punctual_lights/area_lights/area_samples/seed/exposure_ev/shadows/reflections/anisotropic_faces/emissions/surface_effects/dielectrics/environment options, only with raytrace. CPU raytrace.lens?: {aperture_radius_mm:0..10000000,focus_distance_mm:0.000001..1000000000}; positive aperture requires explicit CPU progressive sampling and a bounded perspective camera; absent/zero keeps the original CPU ray stream. See docs/render-thin-lens.md. CPU raytrace.volume?: one HomogeneousVolume box with world-mm min_mm/max_mm, extinction_per_mm:0..1000, RGB scattering_albedo:0..1 and anisotropy:-0.95..0.95. Positive extinction requires explicit Indirect lighting and cannot combine with Glass/SSS or surface Denoise. Physical eye/pupil prefixes and the orthographic line/box union preserve camera/model framing under existing ray/profile caps. See docs/render-cpu-volume.md. CPU raytrace.sun? uses the shared finite-disk Sun contract; raytrace.sky? uses {gradient,rotation_rad,intensity}. Both require explicit Indirect lighting, and gradient Sky excludes a constant/HDR environment. Their medium NEE uses the HG response/PDF and physical light source; see docs/render-cpu-celestial.md. CPU transparent_background?:false exports straight-alpha primary geometry coverage for fixed/progressive sampling; body/face opacity must remain 1, and active bloom/backdrop/photographic effects require opaque output. CPU raytrace.output?: {tone_map,finishing,backdrop,linear_exr}; shared presentation supports up to 8192 per side and 33554432 output pixels, CPU exposure remains raytrace.exposure_ev; raw FLOAT32 EXR is retained only at final completion. CPU raytrace.budget?: compact (legacy default), studio or workstation; see docs/render-cpu-resource-profiles.md for explicit image/geometry/memory/work limits. CPU raytrace.progressive?: {samples_per_pixel:1..profile limit, seed:u32}; requires primary_samples:1 and selected reflections.samples:1; area/LED sources and certified planar anisotropy/thin-sheet/axis-aligned-box dielectric continuations share the existing bounded transport and compatibility checks. See docs/render-cpu-progressive.md; environment?: {path: scoped HDR, expected_sha256?: inspected digest, rotation_radians: 0, intensity: 1, samples: 16} OR {resource: saved HDR digest, rotation_radians, intensity, samples}; embedded resources cannot combine with path/hash; explicit sRGB/D65 HDR primaries required. Opaque CPU preview with selected mirror/rough-specular reflections and certified planar anisotropy. HDR with rough reflections or anisotropy requires fully metallic selected bodies and all effective faces; unsupported combinations refuse. Optional raytrace.path:{max_depth:1..64} enables CPU diffuse/specular GI with matched rectangle/HDR light and BSDF MIS plus certified closed-mesh Glass. It requires explicit progressive camera sampling, primary_samples:1 and traced shadows; selected reflections and certified anisotropy refuse before HDR IO without changing intent. CPU Path dielectric subsurface?:{scattering_per_mm:0..100,anisotropy:-0.95..0.95} enables homogeneous free-flight/HG walks inside certified closed media; positive rates require denoising off. Contained light connections include scattering survival; closed refractive light connections and combining Glass with external volume remain unsupported. Absent path keeps the old certified thin_sheet/closed_box producer and refuses subsurface intent explicitly. See docs/render-cpu-general-glass.md and render-raytrace-dielectrics.md. CPU emissions are visible radiance and may light neighbors through sampled Path continuation, without mesh-emitter next-event sampling; surface_effects change roughness/shading normals without geometry changes. led_strip?: continuous world-mm LED rectangle recipe; led_samples?: 1/4/16/64 for the whole strip, default16. At most8directional+8punctual+4rectangle/LED sources; shared area N. See docs/render-light-union.md. transparent_background?: false; replace?: false. With progressive:true, raytrace instead accepts GPU dielectrics/clear_coats/textures; environment instead accepts tagged hdr/embedded_hdr/sky with rotation_rad/intensity; file hdr supports expected_sha256, embedded_hdr uses a document resource digest. See docs/render-hdr-resources.md. GPU lens, studio lighting, tone_map, denoise, clamp_indirect?: 0.000001..1000000 (biased; caps each contribution from the first bounce on, keeping its hue; see docs/render-path.md), bounded finishing/backdrop and linear_exr retention are also available. CPU raytrace and GPU top-level controls accept supersampling:1|2 (default 1; doubles internal width and height at 2, averages scene-linear premultiplied samples before tone mapping, exports selected dimensions), denoise_settings:{passes:1..3,strength:0..1,firefly_suppression:bool,optical_filter:bool}, and denoise_diagnostics:false. Optical filtering defaults to true for eligible mirror/glass guides; optical_filter:false restores legacy opaque-only display denoising. Guides and diagnostics retain their independent eligibility policy and conservative highlight protection. CPU raytrace.threads:0..64 selects bounded automatic workers at 0 (default), serial work at 1 or an explicit count from 2 through 64. Resource checks include internal image buffers and worker scratch. See docs/render-quality-controls.md. Both path tracers accept optional metal_energy_compensation: true for rough isotropic metals (inside CPU raytrace or top-level GPU). Both methods accept optional caustics {photons:1..1048576,radius_mm:0.000001..1000000,max_depth:1..64,seed:u32,area_lights?:false,environment?:false,guided_emission?:false,refresh_per_pass?:false} (inside CPU raytrace; top-level GPU). Optical assignments accept optional model {roughness:0..1,dispersion?:{abbe_number:5..200},thin_film?:{ior:1..3,thickness_nm:0..100000}}. CPU advanced models require Path; rough sheets require straight transmitted shadows off. See docs/render-glass-material-models.md. GPU adaptive?: {pilot_samples:16..256,min_samples:16..samples,relative_error:0..1,absolute_error:0.000001..1000000,coverage_error:0.001..0.25} allocates independent production budgets; pilots are discarded, not a certified error guarantee. transmission_samples?:1|2|4 stratifies the first Fresnel branch with extra path cost. volume?:{min_mm:[x,y,z],max_mm:[x,y,z],extinction_per_mm:0..1000,scattering_albedo:[r,g,b],anisotropy:-0.95..0.95} is one finite homogeneous world-mm medium; active media require explicit lighting/environment and refuse glass, denoise and multiple transmission strata. These fields also persist in GPU job.options. See docs/render-materials/TRANSPORT-MASTER-BATCH-2026-10-23.md; see docs/render-transmission.md, render-environment.md, render-runtime-textures.md, render-thin-lens.md, render-studio-lighting.md and render-denoise.md. Optional CPU raytracing requires a raytrace-cpu build and native host, checked before scene capture. Stale geometry is rejected. See docs/render-jobs.md

Render Image

Command group: RENDER › IMAGE

Default keyboard shortcut: R

Automation details

Command ID: render.start

Parameters:

Same request as render.preflight. Captures an immutable product scene and queues a bounded image job. Returns its session-scoped job identity, captured scene_generation and progress. Does not change the design. See docs/render-jobs.md

Render Progress

Automation details

Command ID: render.status

Parameters:

job?: {session, serial, generation}. Non-blocking progress, errors and captured frame metadata with queue:{running,waiting,job,shot,error}. Without job returns queue only. Advances an explicitly started render.batch through render.start as jobs finish; never pumps render work. Preview is separate from the exportable final image.

Cancel Render

Automation details

Command ID: render.cancel

Parameters:

job?: {session, serial, generation}, default the active render.batch job. Requests cooperative cancellation and drops its waiting shots; completed jobs remain completed. Late cancelled frames cannot be published.

Copy Render Image

ParameterTypeRequiredDescription
jobcompleted JobIdNodefault selected result
Automation details

Command ID: render.result.copy

Parameters:

job?: completed JobId (default selected result). Validates the retained PNG for app clipboard copying (max 4096 per side, 4194304 pixels); headless returns frame metadata only. Never copies a provisional image or starts a job.

Export Render Image

Automation details

Command ID: render.export

Parameters:

job?: {session, serial, generation} (default selected result), path: destination matching format?: png (default), exr or denoise_mask (PNG extension), overwrite?: false. Guide masks require denoise_diagnostics captured during rendering; their separate bytes, dimensions and SHA are returned under denoise_mask, while frame identifies the parent colour result. EXR requires an opt-in raw linear master retained by that completed CPU or GPU job; never converts PNG pixels. Exports a retained completed image atomically; an existing destination is preserved on failure. See docs/render-jobs.md

Render Backends

Automation details

Command ID: render.backends

Parameters:

No parameters. Returns {schema_version: 1, default_backend: builtin, backends: [{id, label, availability, executors: [{id, availability, profiles, limits, scratch}]}]}. Metadata only; does not start rendering or probe a GPU. See docs/render-backends.md

Render Settings

ParameterTypeRequiredDescription
namesaved setupNo
Automation details

Command ID: render.settings.get

Parameters:

name?: saved setup. Without name returns {setups, defaults}; with name returns {name, setup}. Does not evaluate the model or apply a camera. See docs/render-settings.md

Save Render Settings

ParameterTypeRequiredDescription
namesetup nameYes
setobject of fields to replaceYesbackend, mode, camera, width, height, format, exposure_ev, environment_rotation, environment_intensity, background, job, surfaces, optics, coats, stage
Automation details

Command ID: render.settings.set

Parameters:

name: setup name, set: object of fields to replace (backend, mode, camera, width, height, format, exposure_ev, environment_rotation, environment_intensity, background, job, surfaces, optics, coats, stage). job?: typed active raster/cpu_raytrace/gpu_path configuration with optional validated embedded HDR reference; omitted preserves, null clears, object replaces the complete job. Creates from defaults and saves without a host, validates atomically, one undo step; identical edits are no-ops. See docs/render-settings.md

Inspect Render Scene

Command group: RENDER › SCENE

Automation details

Command ID: render.scene

Parameters:

No parameters. Authoritative product-scene metadata, without modelling overlays or mesh payloads. Explicitly settles stale geometry; does not render an image. See docs/render-workspace.md

Apply Render Camera

Automation details

Command ID: render.settings.apply

Parameters:

name: saved setup. Applies its existing Camera snapshot including roll/up as view state, without modifying the document or starting a render.

Copy Render Check Report

Automation details

Command ID: render.preflight.copy

Parameters:

text: nonempty report, at most 65536 bytes. Validated clipboard request; headless echo only, never renders or edits a design.

Select Render Result

Automation details

Command ID: render.result.select

Parameters:

index: integer 0..7, oldest first; must be retained. Selects a completed result as transient per-document view state; echoes index.

Discard Render Result

Automation details

Command ID: render.result.discard

Parameters:

index: integer 0..7, oldest first; must be retained. Drops only that transient result, selecting the newest remaining result if necessary; echoes index.

Set Render Output Size

ParameterTypeRequiredDescription
width1..16384Yes
height1..16384Yes
customboolNo
Automation details

Command ID: render.output.size

Parameters:

width, height: 1..16384; custom?: bool. Validated Render output draft request; headless echo only, no design edits or job. The UI separately limits new selections to 8192 per side, 33554432 output pixels and the selected rendering profile.

View Render Image

ParameterTypeRequiredDescription
viewmodel|image|fit|actual|denoise_maskYes
Automation details

Command ID: render.result.view

Parameters:

view: model|image|fit|actual|denoise_mask. Guide-mask view requires a selected completed result captured with denoising diagnostics. Validated result canvas request; headless echo only, never starts a job.

Close Render Image

Default keyboard shortcut: Escape

Automation details

Command ID: render.result.close

Parameters:

No parameters. Return to Model Preview; headless echo only, never cancels a job.

Solo Light

Command group: RENDER SCENE › LIGHTS

ParameterTypeRequiredDescription
idkind:slot light addressNo
soloboolYes
Automation details

Command ID: render.lights.solo

Parameters:

id?: kind:slot light address; solo:bool. Enabling requires an id; false clears solo. Validates and echoes per-document UI view state. Never saves settings, evaluates geometry, opens an editor or changes history.

Mute Light

Command group: RENDER SCENE › LIGHTS

ParameterTypeRequiredDescription
idkind:slot light addressYes
Automation details

Command ID: render.lights.mute

Parameters:

id: kind:slot light address; mute:bool. Validates and echoes per-document UI view state, applied to live preview and captured render copies only. Never changes saved settings or history.

Compare Images

Command group: RENDER › RESULT

Automation details

Command ID: render.compare

Parameters:

left, right: live or retained result index 0..7 (oldest first), distinct and retained; OR off:true. Validated per-document comparison view request; headless echo, no rendering or design/history change.

Move Render Comparison Divider

Automation details

Command ID: render.compare.divider

Parameters:

position: finite number, clamped to 0.05..0.95. Transient comparison divider request; headless echo only.

Render All Shots

Command group: RENDER › BATCH

Automation details

Command ID: render.batch

Parameters:

request?: render.start parameters, shared across all saved shots except camera. Default: first saved setup in name order. Captures up to 32 shot cameras and queues sequentially through render.start; returns {queued:n}. Refuses active renders, no shots or stale geometry. render.status (also with {}) and the UI tick advance the queue; Cancel on the active job drops waiting shots. No design/history/revision change.

Glossary

This page sets SolidCraft’s ubiquitous language: one agreed term for each idea, defined once and used the same way in labels, command ids, parameter keys, the design file, the docs, the MCP tools and every translation.

Two rules apply:

  • One idea, one term. When this page names a term for an idea, use that term, not a synonym.
  • One term, one idea. A word that means two things gets two qualified terms (for example construction entity and construction plane), and each meaning gets its own entry in the message catalogues.

A pull request that adds a new idea (a command, a parameter, a file key, a panel) adds its term here. A pull request that renames a label, id or key to match this page keeps the old id or key working as an alias and lists it in docs/compatibility.md.

How an entry reads

Each entry gives the term and a definition: one short sentence in plain technical English that could replace the term in a sentence. It names no product and doesn’t use the term to define itself, in the way ISO 704 and ISO 10241-1 describe for terminology. A note follows, with the context:

  • In SolidCraft: the command ids, parameter keys and design-file keys that use the term.
  • Don’t use: the other words for the same idea.
  • Elsewhere: general names that people coming from other CAD software may know.
  • For translators: what the term must not be confused with.

Some of the ids and labels below still use a word this page says not to use. They change in later pull requests, one topic at a time, each pointing at its entry here.

Terms

abbe number

Number measuring optical dispersion as (nd - 1) / (nF - nC) at the Fraunhofer d, F and C wavelengths.

  • In SolidCraft: Glass model.dispersion.abbe_number; larger values give less chromatic dispersion.
  • For translators: an optical material quantity, not a sample or photon count.

appearance

How a body, face or component looks on screen and in renders.

An appearance sets colour and surface finish only. It has no effect on mass or strength. Compare material.

  • In SolidCraft: appearance.assign, appearance.library, appearance.list; the appearances design-file key.
  • Don’t use: look, colour (for the whole appearance), render material.
  • Elsewhere: appearance, visual material, colour.
  • For translators: not the physical material.

bill of materials

The list of the components placed in a design or in one component, with the quantity of each.

It lists every part at any depth, or only the direct children. Each component’s bill of materials type decides whether it has a row and whether what it holds is counted.

  • In SolidCraft: assembly.bom (“Bill of Materials”) and its window on the INSPECT panel; each row’s item, part_number and count; a component’s metadata and properties design-file keys, which component.metadata.set changes.
  • Don’t use: BOM in labels; parts list (for the bill of materials itself).
  • Elsewhere: bill of materials, BOM, parts list.
  • For translators: not a parts list on a drawing, which is one table that shows it.

bill of materials type

The rule that decides whether a component has a row in a bill of materials, and whether what it holds is counted there.

There are five:

  • normal, something you make: a row; each occurrence adds one to its quantity.

  • phantom, something just to group: no row; what it holds counts where it is placed.

  • reference, something to use as a reference: no row, and nothing inside it counts.

  • purchased, something you buy: one row; nothing inside it is counted or listed.

  • inseparable, something made as one piece (welded, glued, riveted and so on): one row; of what it holds, only the purchased components are listed, and only in a list of every part.

  • In SolidCraft: the bom_type part property, which component.metadata.set changes and component.metadata reads; the bom_type of each assembly.bom row.

  • Don’t use: BOM structure, item type, kind; made (for normal: it reads as “already made”).

  • Elsewhere: BOM structure, BOM type.

  • For translators: normal here is not the sketch line type, a direction or the display quality (see Words with more than one meaning); reference is not a construction plane; phantom is not a hidden or see-through display.

body

One connected solid or surface shape that features create and change.

A design can hold many bodies. A component groups bodies; an occurrence places them.

  • In SolidCraft: body.rename; the bodies browser folder; body_name parameters.
  • Don’t use: part, solid (for a surface body too), object.
  • Elsewhere: body, solid body, surface body.
  • For translators: not a component and not a design.

browser

The panel that lists the design’s components, bodies, sketches, construction planes and joints as a tree.

  • In SolidCraft: browser.group, browser.move, browser.order, browser.visibility, and the other browser.* commands.
  • Don’t use: model tree, scene browser, parts browser.
  • Elsewhere: browser, model tree, feature tree, design tree.
  • For translators: not the web browser. A language with its own CAD word for the tree may use it (in French, arborescence), so that “browser” and “web browser” don’t share a word. See web browser.

caustic

A concentration or structured redistribution of light caused by reflection or refraction.

  • In SolidCraft: Render job caustics controls the photon estimate for point and hard directional sources; finite sources also have camera-path estimates.

chromatic dispersion

Variation of refractive index with wavelength that can separate colours along refracted paths.

  • In SolidCraft: Glass model.dispersion; the current renderer uses three representative wavelength bands.

component

A named container of bodies, sketches, construction geometry and other components that can be placed one or more times.

Each placement of a component is an occurrence. Editing a component changes every occurrence of it.

  • In SolidCraft: component.create, component.activate, and the other component.* commands; the components design-file key.
  • Don’t use: part, assembly, sub-assembly (for the component itself).
  • Elsewhere: component, part, sub-assembly.
  • For translators: not an occurrence.

constraint

A rule that fixes the relation between sketch entities, such as coincident, parallel or tangent.

A constraint with a value is a dimension.

  • In SolidCraft: sketch.constraint.coincident, sketch.constraint.parallel, and the other sketch.constraint.* commands; the constraint parameter key; the sketch’s constraints design-file key.
  • Don’t use: relation, condition.
  • Elsewhere: constraint, geometric constraint, relation.
  • For translators: a joint between occurrences is not a constraint.

construction entity

A sketch entity that guides the drawing but isn’t part of any profile.

A construction entity can be a line, an arc, a circle or any other curve, and it can carry constraints and dimensions like any sketch entity.

  • In SolidCraft: the construction parameter of the sketch drawing commands; the construction field of a sketch curve; the ui.construction catalogue entry.
  • Don’t use: “construction” on its own where it could mean a construction plane.
  • Elsewhere: construction line, construction geometry, reference line.
  • For translators: not a construction plane, axis or point. The browser’s Construction folder and this line type still share the Construction catalogue entry, so German, Korean and Turkish show the folder as “construction line”.

construction plane, construction axis, construction point

A plane, axis or point that a feature creates to place other features, and that is not part of any body.

They are listed together in the browser’s Construction folder.

  • In SolidCraft: construct.plane.offset, construct.axis.two_points, construct.point.vertex, and the other construct.* commands; the CONSTRUCT toolbar panel.
  • Don’t use: “construction” on its own where it could mean a construction entity; work plane.
  • Elsewhere: construction plane, reference plane, datum plane (and axis, point).
  • For translators: not a sketch’s construction entity.

curve

A sketch entity that is not a point: a line, an arc, a circle, an ellipse, a spline or a conic.

  • In SolidCraft: the sketch’s curves design-file key; curve ids in parameters such as path.
  • Don’t use: geometry, edge (for a sketch curve).
  • Elsewhere: curve, sketch curve, segment.
  • For translators: not an edge of a body.

design

The model that is open in SolidCraft: its timeline, components, bodies, sketches, parameters and settings.

A design is saved as a design file.

  • In SolidCraft: file.new (“New Design”), document.units (“Change Design Units”), document.metadata (“Design Properties”), doc.open, doc.recover.
  • Don’t use: document, doc, project, file (for the model itself), part.
  • Elsewhere: design, document, model, part file, assembly file.
  • For translators: not the Project design property and not the sketch Project command.

design file

A file in which SolidCraft saves one design, with the .sc extension (.solidcraft in earlier releases; those files still open).

  • In SolidCraft: file.save, file.save_as, doc.open; the solidcraft/N format tag.
  • Don’t use: document, project file.
  • Elsewhere: design file, model file.
  • For translators: “file” is only for what is on disk: opening, saving, importing and exporting.

dimension

A constraint that sets a size, such as a length, a distance, an angle or a radius, by its value.

A dimension can drive the sketch, or be driven by it and only show the value.

  • In SolidCraft: sketch.dimension, sketch.dimension_text, sketch.toggle_driven; a dimension’s id is a constraint id.
  • Don’t use: measurement (for a dimension), size.
  • Elsewhere: dimension, dimensional constraint, driving dimension.
  • For translators: not a drawing dimension and not a measured value.

end cap

A plate that closes the open end of a frame member, shaped like the member profile’s outer outline and set in from its edge.

An end cap is a separate body named after its member. Compare a mounting plate, which is rectangular or round and need not follow the profile.

  • In SolidCraft: frame.end_cap; the frame_end_cap design-file key.
  • Don’t use: plug, lid, blank (for the plate).
  • Elsewhere: end cap, end plate, cap plate.
  • For translators: a closing plate on a tube’s end, not a cap of a sketch or surface.

equation curve

A sketch curve whose shape comes from formulas over a range of one variable.

It is a fit point spline through fixed points that the formulas place, and they place them again when the design’s parameters change. Compare spline, whose points are placed by hand.

  • In SolidCraft: sketch.equation_curve, sketch.equation_curve.edit; the equations design-file key of a sketch; variable_unit says whether the variable is a plain number or a length like its range.
  • Don’t use: formula curve, function curve, parametric curve (for the command).
  • Elsewhere: equation-driven curve, curve from equation, function plot.
  • For translators: “equation” is a mathematical formula, not a balance or an equality constraint.

feature

One step in the timeline that creates or changes geometry, such as an extrude, a fillet or a construction plane.

  • In SolidCraft: timeline.edit, timeline.suppress, timeline.reorder, and the other timeline.* commands; the features design-file key.
  • Don’t use: operation, step (in labels); feature (for something SolidCraft can do in general: say tool or capability).
  • Elsewhere: feature, operation.
  • For translators: French uses fonction for a timeline feature, so “AI features” had to become fonctionnalités IA. Translate “feature” only in the timeline sense.

folder

One of the browser’s fixed sections of a component: Bodies, Sketches, Construction, Joints.

A folder is part of every component and can’t be renamed or removed. A container that someone makes is a group.

  • In SolidCraft: the folder parameter of browser.group, browser.order and browser.visibility (bodies, sketches, construction).
  • Don’t use: folder for a group.
  • Elsewhere: folder, node.
  • For translators: not a group and not a folder on disk.

gather radius

Radius of the surface neighbourhood used to estimate illumination from nearby photon hits.

  • In SolidCraft: Caustics radius_mm; smaller values preserve sharper details and require more photons to reduce noise.

group

A named container that someone makes, in the browser or in the timeline, to keep items together.

A group changes only how items are listed. It has no effect on the geometry or the order in which features are computed.

  • In SolidCraft: browser.group, browser.ungroup, browser.rename_group; timeline.group, timeline.ungroup, timeline.rename_group, timeline.move_to_group; the group parameter key; the groups parameter of timeline.delete and timeline.suppress (a group with all its features); the browser_groups and timeline_groups design-file keys.
  • Don’t use: feature folder, folder (for a group).
  • Elsewhere: folder, group.
  • For translators: not a rigid group (a joint) and not a fixed browser folder.

material

The physical substance of a body, which sets its density and mechanical properties.

A material sets mass, centre of gravity and strength values. How the body looks is its appearance.

  • In SolidCraft: material.assign (“Physical Material”), material.list, material.create, and the other material.* commands; the materials and user_materials design-file keys.
  • Don’t use: material for a render or visual material.
  • Elsewhere: material, physical material.
  • For translators: not an appearance.

moment of inertia

How a body’s mass resists turning about an axis: the mass times the square of its distance from that axis, summed over the body.

SolidCraft reports the whole inertia tensor about the centre of mass in world axes, with its principal moments and principal axes: the three axes with no products of inertia between them. The first moments are the mass times the centre of mass.

  • In SolidCraft: inertia_g_mm2, principal_moments_g_mm2, principal_axes, first_moment_g_mm and about in component.properties and occurrence.properties; the Properties window’s Moment of inertia section.
  • Don’t use: rotational inertia, second moment of mass, mass moment.
  • Elsewhere: moment of inertia, inertia tensor, mass moment of inertia.
  • For translators: not the second moment of area of a cross-section, a different quantity.

occurrence

One placed copy of a component inside its parent component.

A component can have several occurrences. Moving an occurrence moves only that copy; editing the component changes every copy. The browser names occurrences with a number, such as Bolt M20:3.

  • In SolidCraft: occurrence.copy, occurrence.move, occurrence.drag, occurrence.ground, occurrence.properties; the occurrences design-file key; the path of occurrence ids.
  • Don’t use: instance, placement.
  • Elsewhere: occurrence, instance, component instance.
  • For translators: not a pattern instance.

opaque path lighting

Indirect diffuse and specular illumination computed by tracing finite light paths between opaque surfaces. Shown as Indirect lighting (opaque surfaces) in Sampling.

  • In SolidCraft: optional CPU job.options.path / runtime raytrace.path, enabled explicitly in Sampling.
  • Don’t use: ambient occlusion, glass, full optical parity.
  • For translators: this describes light transport; it is unrelated to a modelling path or physical material density.

operations panel

The panel in the view’s bottom-right corner that lists each long operation under way, with its progress and a button that cancels it.

An operation is work that takes long enough to run beside the window: opening or inserting a file, a feature dialog’s OK computing, an undo or a history marker move recomputing the timeline, an agent’s command. A new one goes on top; one that ends fades out.

  • In SolidCraft: the ui.operations control method; the operations_panel preference.
  • Don’t use: job, task, activity.
  • Elsewhere: progress panel, background tasks.
  • For translators: an operation is work under way, not a feature’s operation (join, cut, intersect).

part number

A design property that holds the identifier an organisation gives a design.

  • In SolidCraft: the part_number design property, shown in Design Properties and set with document.metadata.set.
  • Don’t use: part number for a standard part’s size.
  • For translators: see Words with more than one meaning, “part”.

path depth

The maximum number of shaded surfaces in a rendering path, starting with the first visible surface. A depth of 1 uses direct lighting only; higher values allow more light bounces. This does not count CAD mesh vertices.

  • In SolidCraft: CPU path.max_depth is 1–16; longer paths are truncated.
  • For translators: a count, not a geometric depth in millimetres or timeline depth.

pattern instance

One copy that a pattern makes of its features, bodies or sketch entities.

The first instance is the original.

  • In SolidCraft: solid.pattern.rectangular, solid.pattern.circular, solid.pattern.path, sketch.pattern.rectangular, sketch.pattern.circular; the count and spacing parameters (“between instances”).
  • Don’t use: instance for an occurrence; element, item.
  • Elsewhere: instance, element, occurrence.
  • For translators: not an occurrence of a component.

personal sheet metal rule

A sheet metal rule kept in the user’s preferences instead of a design, ready to add to any design.

Picking one in Sheet Metal Rules fills the rule manager, and its OK copies the rule into the design, which then keeps a copy of its own. It is an app setting, not part of a design.

  • In SolidCraft: Sheet Metal Rules › Personal rules (sheet.manage_rules); the personal_sheet_rules key of the stored preferences.
  • Don’t use: preset, template, library rule.
  • Elsewhere: rule library, sheet metal style library.
  • For translators: “personal” as in the user’s own, not private or confidential.

The flat face of a body that lies on the print bed when the body is sent to a slicer.

A body has one print face at most. The export turns a copy of the body so this face points down at Z = 0, with the centre of mass over the origin; the design itself does not move.

  • In SolidCraft: body.print_face; the print_faces of a design; the print_faces and warnings of a file.send_to_slicer reply.
  • Don’t use: bottom face, base face (both say where a face is now, not where it is printed).
  • Elsewhere: place on face, lay flat, bed face.
  • For translators: the face the part stands on while it prints, not the printer’s bed.

reference edge

A straight edge of a flat face that a Single hole’s centre or a cylinder’s base centre keeps a set distance from, measured in that face’s plane.

Up to two reference edges place one hole or cylinder. Each distance is a parameter, and the feature follows its edges when the part changes.

  • In SolidCraft: the references parameter of solid.hole and solid.cylinder (each {edge, distance}); the windows’ Reference 1, Reference 2 and Distance 1, Distance 2 rows; the “Reference distance” input (ui.reference_1, ui.reference_2, ui.reference_distance).
  • Don’t use: datum edge, anchor edge.
  • Elsewhere: reference edge, edge offset, distance from edge.
  • For translators: an edge of a body, not a construction axis or a reference plane; the distance is a length, not a reference value for light or material.

rollback marker

The marker in the timeline after which no feature is computed.

Moving the rollback marker back shows the design as it was at that step. Features after the marker are kept, not deleted.

  • In SolidCraft: timeline.rollback, timeline.roll_to; the marker design-file key.
  • Don’t use: timeline marker, history marker, rollback bar.
  • Elsewhere: rollback bar, rollback marker, end-of-part marker.
  • For translators: “history” is the undo history, not the timeline. French had three names for this marker while English did.

settings profile

A named set of input and interface preferences that can be switched, exported and imported as one.

A settings profile holds the Interface, Navigation and Sketch preferences, the interface language, keyboard shortcuts, the toolbar and the S box favourites. Settings that belong to one computer (the 3D mouse, graphics, units) stay out. It is an app setting, not part of a design.

  • In SolidCraft: settings.profile.list, settings.profile.save, settings.profile.apply, settings.profile.rename, settings.profile.delete, settings.profile.export, settings.profile.import; the settings_profiles key of the stored preferences; the solidcraft-profile/1 file format.
  • Don’t use: preset, configuration, workspace (for the set of settings).
  • Elsewhere: settings profile, user profile, preferences set.
  • For translators: not a sketch profile and not a frame profile (see Words with more than one meaning, “Profile”), and not a design configuration.

sketch

A set of points and curves, with their constraints, on one plane, that features use as profiles, paths and guides.

  • In SolidCraft: sketch.create, sketch.finish, and the other sketch.* commands; the sketches browser folder.
  • Don’t use: drawing, profile (for the whole sketch).
  • Elsewhere: sketch, 2D sketch.
  • For translators: not a drawing (a sheet with views and dimensions).

sketch entity

A point or a curve in a sketch.

  • In SolidCraft: the entities parameter of the sketch commands (it takes point and curve ids); the sketch’s points and curves design-file keys.
  • Don’t use: geometry (for sketch content), element, object.
  • Elsewhere: sketch entity, sketch geometry, sketch element.
  • For translators: “entity” names both points and curves.

standard part

A ready-made component, such as a screw, nut, washer, pin or bearing, that SolidCraft builds from a standard size.

  • In SolidCraft: parts.library (“Standard Parts”), parts.insert, parts.fastener, parts.resize; the parts design-file key.
  • Don’t use: library part, part (on its own), content.
  • Elsewhere: standard part, library part, fastener, catalogue part.
  • For translators: see Words with more than one meaning, “part”.

standard steel size

A named steel section from a published standard, such as the channel RSC 203x89 or the hollow section SHS 100x100x5, that a frame member can be made of without typing its dimensions.

Its frame profile carries the standard’s dimensions; a taper flange channel’s flanges slope by its flange slope.

  • In SolidCraft: frame.profile.list (sizes), profile: {"size": …} in frame.member, frame.profile.properties and frame.profile.insert; a channel’s flange_slope.
  • Don’t use: catalogue profile, library profile (for the size).
  • Elsewhere: standard section, section size, section designation.
  • For translators: a steel section’s size by name, not a dimension and not a standard part.

thin-film interference

Interference between coherent optical waves reflected at the interfaces of a thin layer.

  • In SolidCraft: Glass model.thin_film, with ior and thickness_nm; this differs from an incoherent ideal Glass sheet.

timeline

The ordered list of a design’s features, which SolidCraft computes from first to last.

  • In SolidCraft: the timeline.* commands; the features design-file key, in timeline order.
  • Don’t use: feature history, history (for the timeline).
  • Elsewhere: timeline, history, feature tree.
  • For translators: not the undo history.

turn (of two rounds)

How two rounds of a fillet continue where they turn round a sharp corner edge between them, such as the rounds along a boss’s base at its upright edges: in a mitre, or swept round the corner edge as a torus.

  • In SolidCraft: the turn parameter of solid.fillet and its design-file key, mitre (the default) or swept; the Fillet dialog’s Turns row.
  • Don’t use: corner (that is what fills a corner where three rounded edges meet, corner).
  • Elsewhere: mitred corner, swept corner, corner sweep.
  • For translators: “Mitre” and “Swept” name the two shapes, not a direction of travel.

view angles

The yaw, pitch and roll, in degrees, that give the direction a view looks along and how its picture is turned.

Yaw is the heading of the line of sight about the up axis (Front view 0°, Left 90°, Right −90°, Back 180°); pitch is how far the eye is above the ground plane (Top view 90°, Bottom −90°); a positive roll turns the picture clockwise. They are the camera’s own angles, which view.apply and the MCP camera take in radians.

  • In SolidCraft: view.angle_readout and the View angles readout under the orientation gizmo; the yaw, pitch and roll of view.apply’s camera.
  • Don’t use: camera angles, view rotation.
  • Elsewhere: view orientation, azimuth and elevation.
  • For translators: the view’s pitch is a tilt, not a thread’s or a pattern’s pitch.

web browser

A program that shows web pages, such as the one that runs SolidCraft’s web build.

  • In SolidCraft: the web build; the home.in_this_browser catalogue entry.
  • Don’t use: browser on its own.
  • For translators: not the browser panel. Where a language uses one word for both, use the CAD word for the panel.

Words with more than one meaning

An English word that means two things can be translated correctly for only one of them while both share a catalogue entry. Each meaning below gets its own term and, in time, its own entry.

WordMeaningUseWhere
Constructiona sketch entity that isn’t part of a profileconstruction entitysketch drawing commands, ui.construction
the browser folder of construction planes, axes and pointsconstruction plane, axis, pointthe Construction folder, construct.*
Instanceone copy a pattern makespattern instancepattern commands
one placed copy of a componentoccurrence“Paste (Instance)” on occurrence.copy
Historythe undo and redo historyhistoryUndo, Redo
the list of featurestimeline“Feature history” (ui.feature_history), “Roll History Marker”
Parta standard partstandard partparts.*
a body, an occurrence or the designbody, occurrence, designcatalogue texts that say “part”
a design propertypart numberDesign Properties
Profilea closed region of a sketch that a feature usesprofilesolid.extrude, solid.revolve
the cross-section a frame member followsframe profileframe.profile.*
a named set of preferencessettings profilesettings.profile.*, Preferences › General
Gapthe space a hem leaveshem gapthe Hem dialog’s Gap row
an open end in the sketch continuity legendopen endthe sketch continuity legend
Clearancethe Clearance commandclearance checkinspect.clearance
a minimum distance fieldclearancethe Interference and plastic rules dialogs
a hole sized for a bolt to pass throughclearance (hole)the Hole dialog’s Hole Tap Type, ui.hole_clearance
Fitfit the view to the modelfit (view)the view’s Fit action, ui.fit
how much room a clearance hole leaves around the boltclearance fitthe Hole dialog’s Fit, ui.clearance_fit
Flata hole’s flat bottomflat bottomthe Hole dialog’s tip
a hem that lies flat on the sheetflat hemthe Hem dialog’s type
Rounda round relief or corner shaperound (shape)relief shapes, plastic rests, ruled surface corners
a filletfilletsolid.fillet
Normala sketch entity that isn’t a construction entitynormal (line type)the sketch palette’s line type
the direction perpendicular to a surfacenormal (direction)the ruled surface’s Normal mode
the middle display qualitynormal (quality)ui.display_quality_normal
a component with a row of its own in a bill of materialsnormal (bill of materials type)bom_type
Quantitythe number of pattern instancescountthe pattern dialogs’ Quantity rows, ui.quantity
what the motion plot shows: value, velocity or accelerationplotted quantitythe motion study’s Quantity row, motion.plot_quantity
Referencean edge a hole or cylinder keeps its distance fromreference edgereferences of solid.hole and solid.cylinder, ui.reference_1
the measured values a material is checked againstreference (material)material.editor.reference
the distance a light’s intensity is given forreference distance (light)render.cpu_light_reference

Some translations show the clash today: Japanese and Chinese show a hole’s flat bottom with the word for “unfold” or “plane”, Chinese shows “Round” as “fillet”, and Chinese and Russian show the Normal line type as “normal vector”.

Two commands, one label

A label names one command. Today two pairs share one:

  • “Edit Feature”: timeline.redefine and timeline.edit.
  • “Break Link”: component.break_link (an occurrence’s link to another design) and sketch.break_link (a projected sketch entity’s link to its source).

Search lists both under the same name, and the label lookup gives both the same translation.

A feature’s type

A feature type has one name: its design-file key, such as sheet_flange. The label (Flange) is its display name. Replies and scripts use the design-file key (timeline.inspect reports both type: "sheet_flange" and type_name: "EdgeFlangeFeature").

Open questions for the maintainer

  • British or American spelling in the docs. The labels are settled: en.json is American English, and English (UK) (en-GB.json, #2501) holds the British spelling. The docs still use both (colour and color, centre and center); ids and keys use American spelling.