Skip to content

Getting Started ​

This page is the reference: one section per panel, feature by feature. If you would rather learn by building something, the tutorials walk through a real part from start to finish and link back here for the details.

Installation ​

Install CAD Preview from the VS Code Marketplace:

  1. Open VS Code.
  2. Press Ctrl+Shift+X (or Cmd+Shift+X on macOS) to open the Extensions view.
  3. Search for CAD Preview by kratos-multiphysics.
  4. Click Install.

Alternatively, install from the command line:

bash
code --install-extension kratos-multiphysics.cad-preview

Or download and install the .vsix directly:

bash
code --install-extension cad-preview-<version>.vsix

Settings ​

CAD Preview contributes a few cross-document defaults under CAD Preview in VS Code's Settings UI (Ctrl+,, search "CAD Preview"). Most only affect newly opened documents — a document's own saved state (a .mesh.json sidecar's size, the toolbar Grid toggle for the current session) always wins once set. tessellationQuality is the one exception, re-read on every edit (see its own row below).

SettingDefaultEffect
cadPreview.background#1e1e1e3D view background color (CSS hex)
cadPreview.defaultMeshSizePresetmediumSeeds the FE Mesh panel's target size (Coarse/Medium/Fine) for a model with no saved mesh options yet
cadPreview.showGridAndAxesOnOpentrueShow the ground grid and axes helper when a model is opened
cadPreview.openscadBackendautoGeometry engine for .scad evaluation: auto passes no flag, cgal / manifold pass --backend=… (Manifold needs a recent OpenSCAD). Headless twin: OPENSCAD_BACKEND. Unused when no .scad file is opened
cadPreview.openscadLibraryPaths[]Extra OpenSCAD library folders (BOSL2, MCAD, …) prepended to OPENSCADPATH for the .scad conversion only. Absolute paths. Unused when no .scad file is opened
cadPreview.openscadBinaryopenscadOpenSCAD binary used to convert .scad sources to .csg on open (resolved via PATH when bare). Wins over the OPENSCAD_BINARY environment variable, the headless (MCP server) escape hatch. Unused when no .scad file is opened
cadPreview.kernelTimeoutMinutes5Watchdog for a single kernel job (load, mesh, export…): a job running longer is killed and the kernel worker respawned. Applies to newly opened documents
cadPreview.tessellationQualitystandardB-rep (STEP/IGES/BREP/CSG/SCAD) tessellation density — draft/standard/fine; standard is identical to every previous version's fixed behavior. Unlike the other settings here, re-read on every edit, so a change applies to the next edit without reopening the file. Face triangle density only — does not affect edge display or the FE Mesh panel's own (separate) mesh generation
cadPreview.upAxisyDefault up-axis for newly opened models — set to z for Z-up source conventions
cadPreview.navigationPresetdefaultMouse navigation: default (left-drag orbits, middle-drag zooms, right-drag pans) fusion (middle-drag pans, Shift+middle-drag orbits, the left button is free for selection) or touchpad (left-drag orbits; a two-finger scroll pans and a pinch zooms — a physical mouse wheel sends the same plain scroll events, so it pans too). The wheel zooms under the other presets; applied when a viewer opens
cadPreview.ribbontrueShows the ribbon when a viewer opens. Turn it off to start with the ribbon hidden (its tab strip is gone; use the menubar Ribbon button to show it). A choice made in a document is saved in its .view.json and wins; read when a viewer opens
cadPreview.singleKeyCommandsfalseTurns on the single-letter command keys (E Extrude, R Revolve, F Fillet, C Chamfer, H Hole, M Move, S Shell, P Linear Pattern, Q Measure mode) and Shift+R to repeat the last command. Off by default because single keys collide with typing habits; read when a viewer opens

Opening a File ​

CAD Preview activates automatically via the VS Code Custom Editor API. There is nothing to configure.

Open any supported file — for example, from the Explorer or via File > Open File…. VS Code routes it to the CAD Preview custom editor and the 3D view renders immediately.

You can also drag a file from the OS file explorer (or another editor tab) and drop it onto the 3D view to open it the same way. If the browser drop event doesn't expose a real filesystem path for the dropped item, CAD Preview falls back to showing the normal Open… dialog instead of silently failing.

The CAD Preview icon in the Activity Bar opens the Models view: every CAD/mesh file in the open workspace folder(s), discovered with the same routing rules (and the same depth cap and .git/node_modules exclusions) as the headless list_workspace_models tool. Click a file to open it in the 3D viewer; the toolbar offers Open…, Refresh, and New Blank Model… (new empty session — always visible, even when the workspace already has models). With no folder open — or no models found — the view shows Open CAD File… and New Blank Model… buttons instead.

Samples and the welcome walkthrough ​

CAD Preview: Open Sample… (Command Palette, and a link in the empty Models view) offers three small bundled models — a STEP ring, an STL cube and an OpenSCAD .csg bracket. Pick one, then pick a folder: the sample is copied there and the copy opens. The bundled original is never opened (that would write .edits.json/.view.json sidecars inside the extension), and a file of the same name in the folder is never overwritten. Get started with CAD Preview in VS Code's Welcome page walks through opening a model, editing and saving, generating a mesh and connecting an agent.

Starting from Scratch ​

You don't need an existing file. File ▾ → New Blank Model… (also CAD Preview: New Blank Model… in the Command Palette, and the New button in the Models view toolbar) asks where to put a new .brep file, creates it empty, and opens it. From there the Edits panel's whole creation vocabulary is available — primitives (Box, Sphere, Cylinder, Cone, Torus, Prism, Wedge), 2D sketch profiles to extrude/revolve/sweep/loft, and bottom-up wireframe modeling (points → lines/arcs → Build → Surface → Build → Volume) — plus booleans, fillets, chamfers, patterns and everything else that works on a B-rep source.

Two things are worth knowing about how a blank model is stored:

  • The .brep file itself stays empty until you explicitly save. Exactly as for an edited STEP file, your geometry is an ordered, replayable op-list in the <model>.brep.edits.json sidecar beside it; nothing writes the CAD file as a side effect of editing. Keep the pair together, or use Export… / Save Preprocess… to produce something standalone — or save in place (below) to bake the geometry into the .brep file itself.
  • Export offers STEP/IGES and the mesh formats, plus the source's own format as a confirmed save-in-place — the export list excludes a document's own format only for the formats with no same-format writer (glTF, meshio++, CAD-text). Picking your own STEP/IGES/BREP/STL/OBJ/PLY writes the baked model back to the open file (with .bak + history watermark); export anything else for a file that carries the geometry itself.

New Blank Model only ever creates new files: if you point it at a path that already exists it refuses rather than overwriting, since blanking a model would leave its existing edit history replaying against nothing.

Supported Formats ​

FormatExtensionsRendering Pipeline
STEP.step, .stpOpenCascade.js tessellation
IGES.iges, .igsOpenCascade.js tessellation
BREP.brepOpenCascade.js tessellation
OpenSCAD CSG.csgCSG parse → OCCT build → tessellation
OpenSCAD Src.scadbundled OpenSCAD WASM → .csg → same as above (binary fallback)
STL.stlThree.js STLLoader
OBJ.objThree.js OBJLoader
PLY.plyThree.js PLYLoader
glTF / GLB.gltf, .glbThree.js GLTFLoader
3MF.3mfThree.js 3MFLoader (display-only on the host)
Collada.daeThree.js ColladaLoader (import-only; display-only on the host)
AMF.amfThree.js AMFLoader (import-only; display-only on the host)
USD.usd .usda .usdc .usdzThree.js USDLoader (import-only; display-only on the host)
VRML.wrlThree.js VRMLLoader (import-only; display-only on the host)
FBX.fbxThree.js FBXLoader (import-only; display-only on the host)
VTK / VTU.vtk, .vtumeshio++ → STL boundary surface → Three.js
MED.medmeshio++ → STL boundary surface → Three.js
CGNS.cgnsmeshio++ → STL boundary surface → Three.js
Exodus.exo, .emeshio++ → STL boundary surface → Three.js
XDMF.xdmfmeshio++ → STL boundary surface → Three.js
Kratos MDPA.mdpameshio++ → STL boundary surface → Three.js
OpenFOAM.foammeshio++ (case staging) → STL boundary surface → Three.js
Gmsh Mesh.msh, .msh2meshio++ → STL boundary surface → Three.js
Abaqus.inpmeshio++ → STL boundary surface → Three.js
I-DEAS Universal.unvmeshio++ → STL boundary surface → Three.js
SU2.su2meshio++ → STL boundary surface → Three.js
INRIA Medit.meshmeshio++ → STL boundary surface → Three.js
GiD Postprocess.post.msh (+ .post.res)meshio++ → STL boundary surface → Three.js
libMesh.xda, .xdr, .xda.gz, .xda.bz2meshio++ → STL boundary surface → Three.js
Kratos CAD JSON.cad.jsonmeshio++ → in-place linearization → Three.js; the exact NURBS B-rep is what the iga_sample/iga_refine/iga_evaluate tools read
Tecplot binary.pltmeshio++ → STL boundary surface → Three.js
FEBio plot.xpltmeshio++ → STL boundary surface → Three.js
INRIA Medit binary.meshbmeshio++ → STL boundary surface → Three.js (shares the .mesh reader)
MSC Marc post file.t19meshio++ → STL boundary surface → Three.js
Abaqus results.filmeshio++ → STL boundary surface → Three.js
Ansys MAPDL results.rstmeshio++ → STL boundary surface → Three.js (ReStructuredText caveat on open)
LS-DYNA d3plot.d3plotmeshio++ → STL boundary surface → Three.js
Nastran OP2 results.op2meshio++ → STL boundary surface → Three.js
Nastran Bulk Data.bdfrouted; a bulk-data deck this extension's own Gmsh export writes, and hand-authored fixed/free/large-field decks, open and mesh (decks emitted by a real Nastran tool unverified — see below)

B-rep vs mesh: STEP, IGES, and BREP are boundary-representation formats that are tessellated on-the-fly in the extension host. STL, OBJ, PLY, and glTF are already triangulated and are loaded directly into the webview by Three.js. An OBJ shows the colours and diffuse texture of the .mtl beside it, and a PLY its vertex colours — display-only; a Part colour replaces them and removing the Part brings them back (see file formats).

VTK/VTU/MED/CGNS/Exodus/XDMF/MDPA/OpenFOAM/Gmsh Mesh/Abaqus/I-DEAS Universal/SU2/INRIA Medit/GiD Postprocess/libMesh/Kratos CAD JSON — plus the meshio++ 17.3.0 binary writers (Tecplot .plt, FEBio .xplt, Medit binary .meshb, MSC Marc .t19, Abaqus results .fil, Ansys MAPDL .rst, LS-DYNA .d3plot) and 17.4.0's Nastran OP2 results (.op2) — have no native Three.js loader, so the extension host converts them to a triangulated boundary surface in STL form first (meshio++, entirely host-side — no browser involved) and hands that to the webview exactly like a native .stl open. This means Parts, Edits, Export, Mass Properties, and Measurement all work identically to STL. Named cell regions in the source file now auto-become real Parts on first import (one per region, pre-coloured and pre-assigned — for a tetrahedral/triangular boundary; a quad/hex boundary still doesn't correlate); scalar field data (temperatures, stresses, …) beyond region names is still not preserved — only its names are shown, not its values. Headlessly, compare_models, get_mass_properties and measure now read the same extracted boundary and report it as surfaceDerived (volume only when watertight); Mesh Health and B-rep promotion still require the source's own triangle soup and refuse these formats. If you need to inspect scalar field values or colour by them, keep using a dedicated viewer (e.g. ParaView) for those formats — CAD-Preview's support here is for quick geometry (and now region) previews alongside your CAD files, not full FE post-processing. Nastran .bdf is routed as a bulk-data deck and works for decks this extension's own Gmsh export writes — the missing BEGIN BULK line is added on read, and the deck then opens, previews and meshes like any other meshio format. Hand-authored decks in fixed, free and large-field formats (10-node tetrahedra with continuation lines) also read to the exact expected geometry. What is not verified is a deck emitted by a real Nastran-family tool, since none is available to this project; the reader's card coverage is tracked in the roadmap.

OpenFOAM (.foam) is a case marker, not a mesh file. It's an (often empty) marker whose real mesh lives in sibling files under <parent>/constant/polyMesh/ — CAD-Preview stages the whole case and hand-builds the boundary surface. OpenFOAM import is geometry-only: no patch names or field data are preserved (no Parts auto-create, no colour-by-field for it).

Gmsh Mesh / Abaqus / I-DEAS Universal / SU2 / INRIA Medit close a real export/import asymmetry: the FE Mesh panel already wrote .msh/.inp/.unv/.su2/.mesh, but until now had no way to re-open them. .msh, .inp and .bdf are ambiguous extensions (also used by ANSYS/FreeFem, ANSYS APDL, and X11 bitmap fonts) — CAD-Preview shows a one-line status caveat on open; there's no automatic disambiguation into the alternate formats. Nastran .bdf is verified for this extension's own Gmsh-written decks (they open, mesh, and round-trip through an exported .bdf) and for hand-authored fixed/free/large-field decks, but not for decks emitted by other tools. A .bdf re-exported from an already-meshed volume may then fail to re-mesh with a Gmsh topology error — see GMSH Integration for that format-independent limitation.

GiD Postprocess (.post.msh) is a sibling pair, and unlike XDMF it re-meshes correctly after reimport. Geometry lives in the .post.msh, results in a .post.res beside it that the reader finds by name convention — keep the two together; only the .post.msh is opened directly. It is both an import format and an FE Mesh panel export target.

User Interface ​

The CAD Preview editor: 3D viewer with orientation cube, the Components/Parts/Edits/FEMesh sidebar, toolbar, and view-controls panel.

The full editor — here previewing bull.stp with three colour-coded parts assigned, parametric variables, and every panel populated.

Opening a STEP/IGES/BREP/CSG/SCAD file for the first time (or reopening one after an external change) shows a native VS Code progress notification with a Cancel button while OpenCascade parses and tessellates it. Clicking Cancel really stops the work — the toolbar status line immediately shows "Cancelled", this document's queued kernel jobs are dropped, and its running job is interrupted. It is scoped to this document: another open file's load or mesh is never interrupted by it, and closing a tab cancels only that tab's pending work. A subsequent edit or reopen starts a fresh load.

When a sidecar changes on disk while you have unsaved changes. Parts, edits, planes, annotations and mesh options autosave shortly after each change, and an external write (an agent's MCP call, a git checkout, another editor) is normally adopted automatically. If that external write lands while this editor still has a change waiting to autosave, neither version is dropped silently: a notification names the sidecar and both sides' counts, with Reload from disk (take the disk version) or Keep mine (overwrite). Dismissing it pauses that sidecar's autosave; File ▸ Save then overwrites the disk deliberately. If the CAD file itself is replaced while it has unsaved (unbaked) edits, you are asked whether to reload it rather than reloading silently. When an agent (or any other writer) simply appends operations to the open document's edit list, they are adopted automatically and their rows in the Edits history carry an external badge until your next edit; the faces those operations produced flash once. The marking lives only in the open view — nothing is written to disk. Routine edits (adding/undoing an operation) don't show this notification — with the model already parsed, they're normally near-instant and stay on the lightweight toolbar status line only.

The Advanced Group ​

Every section header reads chevron · icon · title, so a collapsed sidebar is still scannable by icon. The sidebar's top level holds only the sections that edit the document — Components, Parts, Layers, Edits, Customizer (.scad sources only), FE Mesh. Everything that only reports on it, or pulls from a library, lives under a single collapsible Advanced group at the bottom:

Advanced ▸Sections
AnalysisMass Properties, Clash, B-rep Health, Mesh Health, Surface Tools, Region fit, Primitives, Passages, Printability
LibraryMacros, Standard Parts

Advanced starts collapsed. Its header carries a badge counting how many of its nine sections apply to the current file (7 of 9 on a STEP/IGES/BREP source; 5 of 9 on a native STL/OBJ/PLY/glTF source; 3 of 9 on a meshio-converted source), so you can tell whether opening it is worth the click without opening it. Each section inside keeps its own chevron and collapses independently, exactly as before — the group simply adds one more level.

Collapsing Sidebar Sections ​

Every sidebar section — and the Advanced group itself — has a chevron at the left of its header. Click it to collapse that section down to just its header, and again to expand it. Sections collapse independently, so you can reduce the sidebar to only what you're actually working with; collapsing the two flex-growing panels (Parts, Edits) hands their space back to the rest rather than leaving a gap.

The collapsed/expanded layout is remembered per document, in the same <model>.view.json sidecar that already stores the camera, display mode and clip plane, so reopening a file restores the sidebar exactly as you left it. Merely opening a document never creates that file — only an actual change does.

Sections that don't apply to the current file (Clash, B-rep Health, Primitives and Passages are shown only for a STEP/IGES/BREP source, Surface Tools only for a STEP/IGES source, Printability for any source with host-measurable triangles (not 3MF, Collada, AMF, USD, VRML or FBX); Mesh Health and Region fit only for a native STL/OBJ/PLY/glTF source) are hidden entirely rather than collapsed, independently of this — the Advanced badge is what tells you how many were left out.

Resizing the Sidebar ​

Drag the thin handle at the sidebar's right edge (or hover/focus it — it highlights) to resize the whole column between a workspace-usable minimum and maximum. The handle is also keyboard-operable when focused: ArrowLeft/ArrowRight step the width, Home/End jump to the minimum/maximum. The chosen width is remembered per document in the same <model>.view.json sidecar (like the collapse state), with the pre-resizer 220px width treated as "default" — an unresized document never grows a sidebarWidth entry just from being opened.

The floating view-controls dock is centred over the 3D view and abreast of the sidebar, so resizing the sidebar never hides its controls; if the control row would be wider than the remaining canvas, it wraps onto a second row inside the dock rather than covering nearby panels.

Keyboard Use ​

The File ▾ / View ▾ / Select ▾ / Measure ▾ / Markup ▾ dropdowns are keyboard-reachable end to end: Tab from the main canvas reaches the toolbar triggers; opening a menu and pressing ArrowDown/ArrowUp cycles its buttons (wrapping), Home/End jump to the first/last item, and Escape closes the menu and returns focus to its trigger. Icon-only buttons (Fit, Tree, the per-row eye/delete buttons, the collapse chevrons) are screen-reader labelled; their aria-label always mirrors the visible tooltip. Inline renames — Parts, Variables, construction planes — commit on Enter (or on clicking away) and cancel on Escape, leaving the value they had when the edit began.

Camera Interaction ​

ActionControl
OrbitLeft-click drag
PanRight-click drag
ZoomScroll wheel

Camera movement uses Three.js OrbitControls with damping enabled for smooth deceleration.

File Menu ​

A full-width menu bar sits at the very top of the editor with a single File ▾ dropdown:

ItemActionShortcut
New Blank Model…Start an empty model and build it from scratch with the Edits panel — see Starting from Scratch—
Open…Pick another CAD/mesh file and open it in CAD PreviewCtrl+O
SaveImmediately flush the parts/annotations/edits/mesh sidecars (.parts.json / .annotations.json / .edits.json / .mesh.json). The sidecars also autosave on a ~500 ms debounce, so this just forces an immediate write — the CAD source itself is untouched (use Ctrl+S / the dirty dot to bake the op tail into the file).—
Save As…Convert the model to a new file/format via the Export flowCtrl+Shift+S
Export…Convert the model to a compatible format and save it — or pick the source's own format to save the edits back into the open file itself (confirmed, with a .bak backup) — see Exporting a ModelCtrl+E
Save Preprocess…Bundle the CAD file plus whichever of its .parts.json / .annotations.json / .edits.json / .mesh.json sidecars currently exist into a single .zip archive (with a per-entry SHA-256 checksum recorded in its manifest), so the whole working state can be shared or archived as one fileCtrl+Alt+S
Load Preprocess…Restore a .zip built by Save Preprocess: pick a destination for the CAD file, write back whichever sidecars it contains, and open the result — rejects a corrupted/tampered archive or a destination whose file extension doesn't match the archive's own formatCtrl+Alt+O
Import SVG…Pick a .svg file and import every <path>/<rect>/<circle>/<ellipse>/<line>/<polyline>/<polygon> in it (a <use> is followed to its target, so repeated symbols import once per instance) as sketch Polyline edit ops — one per closed/open subpath (a shape with a hole, e.g. a traced letter "O", becomes two separate polylines) — with every ancestor's transform composed in, so a real "convert text to outlines" export (typically wrapped in one or more <g transform="..."> groups) imports at the right place and scale. Build → Surface then accepts an outer loop's edges together with its hole's to build a single holed face in one step, ready to Extrude/wrap onto a cylinder. B-rep only; <use> and <text> are recognized and reported in the status line rather than silently skipped (<text> needs converting to outlines first)—
Import DXF…Pick a .dxf file and import its model-space LINE / LWPOLYLINE (bulge arcs sampled) / POLYLINE / CIRCLE / ARC / SPLINE entities as the matching Line/Polyline/Circle/Arc/Spline edit ops. B-rep only; blocks/INSERT/TEXT/DIMENSION/HATCH and paper space are skipped, and geometry lands flat at z=0 (1 DXF unit = 1 mm, Y-up native — adjust afterward with Scale/Move/Rotate)—
Export Silhouette SVG…Write a 2D outline of the model as an .svg file — pick a view (Current view, or Front/Back/Top/Bottom/Left/Right/Iso), then an export unit, then a destination. An outline, not a dimensioned technical drawing (see Exporting a Silhouette SVG)—
Export Silhouette DXF…The same outline flow with a DXF serializer (LWPOLYLINE chains + LINE singletons; tessellated circular runs are fitted back to bulged arcs and full CIRCLEs, within 0.5 % of the radius), saved with a .dxf extension—
Export Technical Drawing…A 2D drawing with hidden-line removal: feature edges solid where visible, dashed where hidden behind the part. Unlike the two silhouette exports it draws interior edges too, so a hole's far rim shows dashed. Same view and unit picks; SVG or DXF (where hidden geometry lands on a HIDDEN layer). Still a review artifact and a single view — pinned annotations still bake in as dimensions—
Export Drawing Sheet…Several views (front/top/right/iso by default) on one sheet at a shared scale, orthographically aligned (first-angle by default) with a title block. Format, then paper size, then export unit, then a destination. A pinned annotation is drawn once, in whichever view shows it at true length (see Exporting a Drawing Sheet)—

The File dropdown open, showing New Blank Model, Open, Save, Save As, Export, Save Preprocess, Load Preprocess, Import SVG, Import DXF, and the silhouette/technical-drawing exports.

Every item is also a VS Code command (CAD Preview: … in the Command Palette). The keyboard shortcuts are scoped to a focused CAD Preview tab, so they don't override VS Code's global Open/Save elsewhere.

Reporting a problem ​

CAD Preview: Report Issue… (Command Palette, with a CAD tab focused) opens a draft in a new editor tab: the first operation that did not apply (its kind, parameters and diagnostic), the file's name, and the extension, kernel-package, VS Code and OS versions. Absolute paths are replaced by file names and the model itself is never attached. Edit the draft freely; only if you click Open on GitHub in the prompt is a prefilled issue page opened in your browser. An agent gets the same text from the diagnostics_report MCP tool.

The same draft is reachable from the viewer: File ▸ Report Issue…, the Report… link beside a ⚠ row in the Edits history (it drafts that specific op), and a Report issue… button next to a kernel-crash or timeout error in the status line (the crash message goes into the draft).

What's New ​

The first time you open a CAD file (or run any CAD Preview: … command) after an update, a What's New tab opens beside the editor summarizing everything that changed since the version you last had installed — just close it (or click Got it) and keep working. It won't show again until the next update. You can reopen it anytime via CAD Preview: Show What's New in the Command Palette, which shows the full changelog rather than only what's new since your last session.

The Ribbon ​

The ribbon command strip under the menu bar: five tabs and, on the Model tab, large icon-over-label buttons in labelled groups.

Under the menu bar, a ribbon gathers the commands that otherwise live in the Edits panel's button grid, the toolbar menus and the collapsed Advanced group, so what the tool can do is visible without opening a panel first. It has five tabs; the choice is remembered per document. A button opens the existing form, menu action or panel; the ribbon adds no features of its own.

TabGroupsButtons
ViewNavigate, Display, Show, Layout, Pick & markFit, Reset, Zoom to selection, Tree; the six display modes; Grid, Edges, Projection; the four split layouts; Select, Markup, Screenshot
ModelOne group per Edits-panel category: Primitives, Holes, Features, Refine, Boolean, Modify, Transform, Assembly, Build (3D), Wireframe, Curves, Sketch, Build (2D), then ImportEvery Edits-panel operation (the same list as the GEOMETRY and EDIT tabs), plus Import SVG and Import DXF
InspectMeasure, AnalysisMeasure mode and its four tools, Clear; Mass Properties, Clash, Draft, Printability, B-rep Health, Passages, Primitives (each expands its sidebar panel)
MeshFE mesh, Repair & fit, SurfaceFE Mesh overlay toggle, Mesh settings; Mesh Health, Region fit; Surface Tools
ExportModel, Drawings, Bundle, MeshSave, Save As, Export; SVG and DXF outline, Drawing, Drawing sheet; Save and Load preprocess; FE mesh export
  • Operations open the same form as the Edits panel's button and never apply anything by themselves. A B-rep-only operation is dimmed on a mesh source. The button of the operation whose form is open shows as pressed.
  • Toggles (Grid, Selection mode, the display modes, Measure…) show their live state, including when you change them through the toolbar menus. A button whose target is unavailable (Tree outside an assembly, a panel the current file does not offer) is dimmed.
  • Narrow editors. When the buttons do not fit, the ones that do not move into a More ▸ menu, listed under their group headings, so every command stays reachable at any width. The Model tab has more than sixty buttons and always uses it.
  • Compact (the button at the right of the tab row) hides the labels and group titles for a one-row, icons-only strip; names stay on the tooltip and the accessible name.
  • Icons. The Model-tab operation icons that draw a solid are two-tone: a neutral body with an accent (your theme's button colour) on the part the command acts on, such as the extruded profile or the filleted corner. High-contrast themes drop the accent and stay monochrome.
  • Hiding the ribbon. The Ribbon button in the menu bar (next to File) hides the whole strip to give the viewport the space back; the floating toolbar slides up with it. Every command stays reachable from the Edits panel (its grid button), the toolbar menus and Ctrl+K command search. The choice is saved per document.
  • The Edits panel. Because the ribbon covers the operation buttons, the panel's GEOMETRY/EDIT tabs and button grids are collapsed by default. The grid button in the Edits header (next to Undo) brings them back; an opened operation's parameter form and the history are always shown. The dock button beside it moves the parameter form over the viewport (top right, clear of the orientation cube) and back; typed values and the live preview carry across, and the choice is saved per document.
  • History rows. Drag a row (or press Alt+↑/↓ on a focused row) to reorder the history: the op ends up where the drop line shows. A move is refused, with the reason in the status bar, when it touches a saved row, crosses an op that a stored selector query names, or swaps two operations that both reshape the model (their face-N/point-N ids would renumber under each other); rigid transforms move freely. The folder button files a row in a folder: the field starts with the folder of the row above, so one Enter extends it. Consecutive rows with the same folder name show under one collapsible header (a count, Ungroup to dissolve it). Folders are display only; replay never reads them.

Toolbar ​

The toolbar appears at the top-right of the editor, just below the ribbon. Three always-visible buttons sit on the left; everything else is grouped behind four dropdown menus, so the strip stays compact:

The viewer toolbar: Fit, Tree, FE Mesh, and the View / Select / Measure / Markup dropdown menus.

ButtonAction
FitReframe the model to fill the viewport (keeps current camera orientation)
TreeShow/hide the component tree panel (visible only for models with multiple components)
FE MeshToggle the generated finite-element mesh overlay on/off (see Generating an FE Mesh). The FE Mesh panel itself is always visible in the sidebar; this button only shows/clears the overlay.

A dropdown closes when you click its trigger again, press Escape, or click anywhere outside it; opening one closes any other that was open. Clicks inside a panel leave it open, so you can flip a mode on, pick a tool, and choose a colour in one visit. When the mode behind a menu is active, its trigger stays highlighted with a small dot, so you can tell at a glance that Measure or Markup is still live after the panel has closed.

View ▾

The View menu: Grid, Edges, Screenshot.

ItemAction
GridShow/hide the world-space grid and axis helpers (ticked when shown)
EdgesShow/hide edge lines independently of the shaded faces (ticked when shown)
Hide smooth edgesDeclutter tangent patch-seam edges (e.g. between adjacent NURBS patches of one conceptually-curved surface on an imported STEP file) while keeping genuine feature edges — ticked when smooth edges are hidden. Off by default, so an existing model looks unchanged until you opt in
Snap to gridWhile dragging the Transform Gizmo in Translate mode, round the move to whole multiples of the Grid size field (view-controls Appearance group, default 1) — off by default
Snap to pointsWhile dragging the gizmo, snap a moved solid's nearest corner onto a nearby existing point-N vertex (any B-rep vertex or standalone point in the model) once it's within about 1% of the model's size — off by default. Both toggles can be on together; grid-snap applies once to the whole dragged group, point-snap then applies per solid, so different solids may snap to different nearby points
Pane layoutFour mutually-exclusive layouts over the same scene — 1×1 (single view), 1×2 (two side-by-side columns), 2×1 (two stacked rows), 2×2 (quad) — each pane an independent camera (direction/up/ortho) over the single WebGL canvas; display mode, clip plane, selection, parts colours, overlays, and markup stay shared across all panes. Orbit, pan, zoom, the Persp/Ortho toggle, and the orientation cube all work per pane, and clicking a pane makes it the one the view-control buttons act on. All panes of a new layout start as copies of the current view; collapsing keeps whichever pane you were last in. Persisted to <model>.view.json alongside camera direction (reopens exactly as you left it, including the per-pane cameras — older sidecars without a layout still restore sensibly to single-pane)
Link cameras across tabsShare the focused pane's camera (direction/up/ortho) across all open CAD Preview tabs — orbiting one tab's view mirrors in the others within ~500 ms, re-framed to each document's own extents. Provider-level on/off (one checkbox for all tabs), session-only, never written to .view.json
Save current view…Save the current camera, display mode, and clip plane as a named view bookmark — a per-document collection of inspection viewpoints persisted to <model>.view.json. Each saved row restores on click (into the focused pane, re-framed to the model's current extents, so it stays meaningful after edits change the model's size), and offers Replace (overwrite with the current view), Rename (inline, Enter commits / Escape cancels), and ✕ Delete. Restoring also becomes the persisted latest view, so reopening shows what was last on screen
Screenshot…Save the current 3D view as a PNG via a Save dialog (see Taking a Screenshot) — in a split view this captures the whole quad

Select ▾

The Select menu: Selection mode, the Point/Vol/Surf/Line pick modes, Zoom to selection, and the filter form.

Selection mode toggles entity picking; the Point · Vol · Surf · Line row chooses what a click picks — points (vertices), volumes (solids), surfaces (faces), or lines (edges). Used to assign geometry to parts (see Defining Parts) and to feed the wireframe Build composer (see Editing Geometry). Pick modes a given file format can't offer are greyed out. Zoom to selection frames the current selection in the focused pane (keeping its orientation, in perspective or ortho — explicit Fit still frames the whole model); an empty or hidden/stale selection explains itself on the status line instead. Also available as CAD Preview: Zoom to Selection from the Command Palette. A filter form below the row lets you select by shape instead of by clicking: faces — Normal ±X/±Y/±Z, Planar, Area ≥/≤, Largest/Smallest N; lines — Along X/Y/Z, Length ≥/≤, Longest/Shortest N — plus a No seams toggle that skips tangent seam edges; volumes — Size ≥/≤ (bbox diagonal), Center ±X/±Y/±Z (bbox-center coordinate), Largest/Smallest N (by bbox volume); points — Near XY/XZ/YZ (distance to the coordinate plane), Near selection ≤ and In selection box × (measured against the current selection's centroid — select something first). All thresholds are in model millimetres, like the geometry itself. Select replaces the current selection; Add unions into it.

Measure ▾

The Measure menu: Measure mode, the four tools, and Clear measurement.

Measure mode toggles measurement picking; the tool row selects Distance, Length, Angle, or Radius, and Clear measurement discards the current one (see Measuring). The result appears on its own line just below the toolbar, so it stays readable with the menu closed. A Saved list at the bottom of the panel shows any pinned annotations (📌, see Pinning a measurement below).

Markup ▾

The Markup menu: Markup mode, the six tools, a colour swatch, and Undo/Redo/Clear.

Markup mode toggles annotation drawing; the tool row selects Freehand, Line, Arrow, Rectangle, Circle, or Eraser, the swatch sets the stroke colour, and Undo / Redo / Clear manage the strokes (see Markup Annotations).

Taking a Screenshot ​

Click View ▾ → Screenshot… in the toolbar (or run CAD Preview: Screenshot to PNG… from the Command Palette, Ctrl+Alt+P) to save the current 3D view — whatever orientation, display mode, mesh overlay, and markup annotations are currently shown — as a PNG. A native Save dialog defaults to the source file's folder.

Markup Annotations ​

Open Markup ▾ in the toolbar and click Markup mode to start drawing review notes directly over the 3D view — "this boss", "gap here" — without leaving the viewer. Pick a tool from the row below it (Freehand, Line, Arrow, Rectangle, Circle, or Eraser) and a stroke colour from the swatch, then click-drag on the view to draw. Undo/Redo step through your strokes one at a time; Clear removes them all. Annotations are session-only — never saved to any sidecar or the CAD file — but they ARE baked into the next Screenshot you take (see above), so you can mark up a view and export the annotated image in one flow. Loading a different model clears any existing annotations; switching display mode, applying an edit, or rotating/panning the view does not. Erasing a stroke with the Eraser tool is immediate and does not go through Undo/Redo. Toggle Markup mode off to resume orbiting/panning/picking normally — while it is active, clicks draw instead of orbiting the camera. The Markup ▾ trigger stays highlighted while the mode is on, even after the menu closes.

The View-Controls Dock ​

The floating dock at the bottom of the 3D view holds the controls you reach for while orbiting. It is one compact row with a ⋯ button at its right end that opens a popover for the controls used less often. (The counts, mesh stats and cursor position that used to sit under it moved to the full-width status bar along the bottom of the window.)

WhereControls
In the rowIcon navigation — reset view, fit, zoom out, zoom in — then the Display modes (Shaded / Wire / X-Ray / Hidden / Flat / Geom / Zebra, as text segments), the Cut Plane (its on/off toggle, then X / Y / Z and the offset slider), Persp / Ortho, and the Units dropdown. A small chevron at the very end of the bar collapses the whole dock
Behind ⋯Rotate and Pan arrow pads, clip Face / 3 Pts, the Planes group, background colour, opacity, Grid size, and Colour by field (shown only for a meshio++ source that declares one)

Nothing was removed — every control that used to sit in the tall two-tier panel is still one click away. The popover opens upward, stays on the canvas rather than covering the sidebar, closes on Escape or an outside click, and is arrow-key navigable like the toolbar menus. On a narrow editor the row wraps onto a second line instead of overflowing.

Colours follow your VS Code theme. The dock, toolbar, chip and status bar use the theme's own colours (light, dark, and high-contrast all work); only the model's own colours — background, faces, edges — come from the separate scene palette.

The panel's remaining controls in detail:

  • Rotate buttons — Step the camera by 15°, 45°, or 90° around the azimuth or elevation.

  • Pan buttons — Shift the camera target by a fraction of the viewport.

  • Zoom buttons (magnifier − / +) — Dolly in or out by a fixed factor.

  • Fit (frame icon) — Same as the toolbar Fit button (reframe in current orientation).

  • Reset (circular-arrow icon) — Reset to the default isometric view (1, 0.8, 1) and reframe.

  • Cut Plane group (formerly "Clip") — Enable a live section/clipping plane, then drag the offset slider to sweep it across the model's bounding box (-1 = the min-side face, 0 = centre, 1 = the max side), measured along whichever normal is active. The cross-section is solid-filled, not see-through, and also applies to the FE Mesh overlay when shown. Turning it off instantly restores the full model.

    The plane can be any direction, not just an axis:

    • X / Y / Z are fast presets.
    • Face clips along a selected planar face — select exactly one face in Surf mode and click it. The plane is oriented so the model is kept rather than cut away entirely, so for a face on the outside of the part nothing is cut until you drag the slider, which then sweeps inward from that face. Picking a curved face reports which surface type it found instead of applying a meaningless plane. B-rep sources only.
    • 3 Pts clips through three selected points — select exactly three in Point mode. The result doesn't depend on the order you clicked them. Three points in a straight line are rejected with an explanation.

    Once a custom normal exists, a fourth N segment appears beside X/Y/Z showing it (hover for the vector and where it came from). It stays selectable, so you can flip to an axis preset to look at something and flip straight back without re-picking. The custom normal is saved to <model>.view.json along with everything else in the panel.

  • Planes group — Named construction planes, saved beside the model in <model>.planes.json so they survive closing the file. Save clip stores whatever the Clip group is currently showing; Enter… reveals fields for typing a point and a normal directly, which is the only way to author a plane with no geometry to pick; Midplane… reveals two dropdowns of the already-saved planes and adds a new plane halfway between the picked pair (their normals must be parallel). Each row offers Use (apply it as the clip — the same path the Face/3 Pts buttons take, so a saved plane behaves identically to a freshly derived one), rename, and delete. Hover a row for its point, normal, and where it came from.

    A plane stays put. It stores the actual vectors, not a reference to the face it came from, so an edit that renumbers face ids leaves it exactly where you saved it — unlike Part and annotation assignments, which are re-matched geometrically. See Construction Planes Sidecar.

  • Appearance group — A background-colour swatch (live preview only — the session-only override always wins over the cadPreview.background setting until you reload), an opacity slider for the whole model, a Persp / Ortho button toggling between perspective and orthographic projection (orbit/pan/zoom, picking, and the orientation cube all keep working under either projection), a Units dropdown (mm/cm/m/in/ft, see Units below), and a Grid size field controlling the Transform Gizmo's Snap to grid increment (see View ▾ above — unrelated to the display grid's own Grid toggle). For a meshio++-imported source that declares point or cell scalar data (temperatures, stresses, …), a Colour by field dropdown also appears here — picking a field paints the model as a viridis colour ramp with a min/max legend; picking "None" reverts. Background/opacity/units/grid-size/colour-by-field stay session-only (never exported/persisted); colour-by-field additionally resets whenever an edit is applied, since a field's values only stay meaningful for the model's original, unedited geometry.

  • Display group — Seven mutually exclusive rendering modes, replacing the old standalone Wireframe toolbar toggle: Shaded (the default, lit faces), Wire (faces rendered as a mesh of lines), X-Ray (translucent faces so edges show through), Hidden (edges of occluded geometry shown faintly through solid faces, full-strength where actually visible), and Flat (unlit, constant-colour faces — no lighting gradient, useful for reading true part colours without shading artifacts), and Geom (the B-rep structure, GiD-style: faces fade to a faint fill, every edge is redrawn through them in a single colour, points are enlarged markers, and each solid gets a bounding shell in its own stable colour; a small legend names the primitives. Picking and selection work as in Shaded, since the overlay is never pickable. B-rep models only — a mesh has no points or edges to show), and Zebra (reflection stripes, the standard check of surface continuity: stripes that run unbroken across an edge mean the surfaces meet tangent-continuously, stripes that jump mean a crease. Works on B-rep and mesh sources and composes with Part colours. The ⋯ More menu shows a stripe-count slider and a direction (X/Y/Z) selector while the mode is active. The stripes are computed from the tessellation's normals, so they are a sampled estimate, not a certification).

The view-controls dock: icon navigation, Display modes, Cut Plane, Persp/Ortho and Units in one row, with the ⋯ overflow button at the right.

The ⋯ popover open above the dock: Rotate and Pan pads, clip Face / 3 Pts, Planes, and Appearance.

The camera direction/up vector, Persp/Ortho, Display mode (and the Zebra stripe settings, when changed from the default), and the Clip plane are all saved automatically to a <model>.view.json sidecar and restored the next time you open the same file, so reopening a large assembly picks up right where you left off instead of always resetting to the default isometric — see View State Sidecar for the format. Applying an edit reframes in your CURRENT direction rather than snapping back to the saved (or default) one. Background colour, opacity, the Units dropdown, and Colour by field remain purely session-only, as does explode-preview state (the committed explode op itself is saved in .edits.json like any other edit).

The Document Chip and Status Bar ​

Two small readouts report facts about the open document without asking you to do anything:

  • Document chip — at the right end of the menu bar: the file name, a small format badge (STEP, STL, …), and — when the document has unsaved edits, meaning an edit-op tail that a save would bake into the source file — a dot and the count (3 unsaved edits). It reads the same rule the editor tab's own dirty dot does, so the two agree while you edit and save; hover the chip for the full path. Two moments where it can differ from the tab, both intentional: on opening a file whose sidecar already holds edits that haven't been saved into the source, the chip shows the dot straight away (the tab stays clean until you change something), and after undoing back to the save point the chip's dot clears while the tab's dot stays until you save or revert (VS Code has no way to un-dirty a tab from an undo).
  • Status bar — the full-width strip along the bottom of the window. At its left, under the sidebar, kernel readiness (OCCT ready · Gmsh ready): which of the WebAssembly kernels (OCCT, Gmsh, meshio++, fTetWild) this session has actually used. They load lazily on first use, so a document that never needs one — a plain STL open, for instance — honestly reads Kernels idle; a kernel shows ready only after a call that needed it has succeeded, and goes back to idle if the worker is cancelled or restarts. To the right of that: entity counts for a B-rep document (36 faces · 98 edges · 64 points, the entities you can pick), the FE-mesh stats once you generate one (mesh 51,200 el · min SICN 0.412 — hover it for the node count too; the full quality histogram stays in the FE Mesh panel), and the live cursor position (x 142.06 y -18.40 z 27.00 mm) in the model's own frame, following the Units dropdown. The position appears while the pointer is over the model and clears when it leaves; it works whether or not selection mode is on. Counts are shown for B-rep documents only — a mesh file has no comparable count that wouldn't change with how its facets are split — and there is deliberately no solid count, since sketch faces would make it off by one.

Explaining the geometry under the cursor ​

With a pick mode active (Select ▾), two things explain what you are pointing at.

Stepping under the cursor: when more than one entity lies under the pointer (a solid's front and back faces, say), the status line reads k of n under the cursor. ↑/↓ (or +/−) step through them front to back and the tooltip follows; the next click within the drag tolerance picks the stepped entity. The keys do nothing when only one entity is under the cursor.

Hovering shows a small tooltip with the entity's id (face-12, edge-3, solid-0) and which of your applied ops mention that id. This is the quickest way to read your own .edits.json: the ids in the sidecar are exactly the ids under your cursor. It says mentions, not acts on, deliberately — ids are positional, so the same face-12 in two different ops can refer to different geometry once an op in between renumbers things.

Clicking additionally opens an inspector card in the top-right corner of the view, classifying the entity analytically: a planar / cylindrical / conical / spherical / toroidal face, or a straight / circular / elliptical / spline edge. It lists only the measurements that classification gives meaning to — a plane gets its area, normal, and a point on its plane; a cylinder gets no normal at all, because a curved face has no single one.

The card needs the CAD kernel, so it is B-rep only (STEP/IGES/BREP/CSG/SCAD) and appears on selection rather than on hover — a triangle mesh has no analytic surface type to report, and a fine-faceted prism is indistinguishable from a cylinder in triangles.

Right-clicking an entity offers computed selection groups with their member counts — "Same facing (3)", "Planar faces (19)", "Area ≤ this (36)" for a face; "Parallel to this", "Length ≥/≤ this" for an edge. Hovering a row previews exactly what choosing it would select; clicking replaces the selection, and shift-clicking adds to it.

These are the same predicates as the Select ▾ panel's filter form, with one difference that makes them worth reaching for: the entity you right-clicked supplies the number. "Area ≤ this" is the filter form's Area ≤ with the threshold already filled in. Groups are offered for Surf and Line modes only — the same modes the filter form supports — and a group that would select only the entity you clicked is not offered at all.

The menu's second row, Copy reference, writes a quotable line to the clipboard for pasting to an agent — the MCP server runs in another process and cannot see your selection. It names the right-clicked entity (or every selected entity when it is part of the selection, as Copy N references): examples/STP/bull.stp#face-12 — planar, 8,119.27 mm² (valid for 3 ops). The path is workspace-relative. Entity ids are positional, so the trailing valid for N ops is part of the reference: it is the length of the edit history the id was read against, and the same id after further topology-changing edits may name a different face. The facts (planar, 8,119.27 mm²) are included only when the selection pill already fetched them for that entity at that op count; copying never triggers a fetch. Resolve a pasted reference with inspect, or render_ops_prefix to see the model at that op count. For a face produced by the last topology-changing edit, the line also ends with — query {…}, a stored query (role in that op's build-time bucket) that an agent can pass to resolve_selector to re-find the face after renumbering; faces from earlier ops, edges and mesh sources get no query, because their ids may already have moved.

The menu's first row, Pin note…, works in every pick mode: it turns the menu into a text field; type a note and press Enter to pin it to the entity you right-clicked, at the point you clicked (Escape cancels). A note is a saved annotation like a pinned measurement: it lists in the Measure ▾ Saved list as Note: …, survives reopening the file, follows its entity across edits, is baked into SVG/DXF drawing exports as a label, and is the same record the pin_annotation MCP tool writes with tool: "note".

Macros ​

The Macros sidebar panel saves a set of edits you have already applied as a named, reusable script, so a bolt pattern or a standard bracket treatment does not have to be rebuilt by hand every time.

Save current records the current edit history under a name you choose; the document's own parametric variables come along as the macro's parameters. Each saved macro then lists those parameters with an editable field seeded from its saved default — change one and press Run to apply the macro at the new values.

Running a macro pushes its ops onto the ordinary edit history, so it is undoable, visible in the history list, and removable op-by-op exactly like a hand-applied edit — there is no separate "macro" state to reason about.

Macros live in cad-preview-macros.json in the model's own folder, shared by every model there. That is the same file the MCP tools read and write, so a macro you record by hand is directly runnable by an agent and vice versa.

Three starter macros ship with the extension and are listed above your own entries — spring (a helical spring: wire-radius circle swept along a helix, parameters R/W/P/N), bolt-circle-flange (the bolt-circle flange walkthrough as a runnable macro, parameters R/N), and hex-bolt (hex-prism head fused to a shaft, parameters headR/headH/shaftR/shaftL). They run like any macro but show no Delete button (read-only). All three assume a blank model (File ▸ New Blank Model) — their sweep/pattern/boolean steps reference the entity ids those steps create on an empty base.

Theme ​

The 3D scene follows VS Code's active colour theme. Switching between a light, dark, or high-contrast theme repaints the background, the default face/edge/point colours, the grid, the scene lighting, and the FE mesh overlay immediately — no reload, and nothing is written to any sidecar.

Colours you chose yourself are never re-tinted. A Part's colour swatch, and a per-part FE mesh colour, are your data: they win over the themed default, so a theme switch leaves them exactly as you set them. The same applies to a background you picked by hand in the Appearance group — once you drag that swatch, your choice wins for the rest of the session.

Two things deliberately do not follow the theme: the orientation cube's red/green/blue axis arrows (an axis-colour convention shared across CAD tools, not a theme detail), and a live operation preview's intent tint, which is regenerated whenever the draft changes anyway.

Units ​

CAD Preview always keeps geometry internally in one consistent unit (millimetres) — for STEP files this is automatic: the OCCT reader converts every shape to millimetres at load time regardless of what unit the file was authored in (inches, centimetres, …), so numbers are always consistent no matter the source. The Units dropdown in the view-controls Appearance group is purely a display preference on top of that: it rescales how Mass Properties and Measurement results are shown (with a unit suffix, e.g. 12.700 mm or 0.500 in) — nothing stored (edit-op parameters, sidecars, mesh-size options) is ever rescaled, and FE Mesh panel size fields always show plain millimetres regardless of this setting, since that's Gmsh's own working unit. Opening a STEP file whose DATA section declares a length unit (e.g. INCH), or an IGES file whose Global section declares one (its own, differently-structured way of recording a unit), seeds the dropdown to that unit automatically; opening a file with no declared/recognized unit, or a mesh format (which has no unit metadata at all), always starts from mm. Moments of inertia in the Mass Properties panel are intentionally never rescaled by this setting. The selection is session-only — it resets on every new file open and is never written to a sidecar.

This dropdown is unrelated to (and doesn't drive) the Export flow's own unit conversion — see Exporting a Model, which applies a real geometric scale to the exported file, not just a display change.

Orientation Cube ​

A labeled orientation cube sits in the top-left corner of the 3D view. It mirrors the current camera direction in real time.

Click any face of the cube to snap the camera to that standard view:

FaceView
+X / -XRight / Left
+Y / -YTop / Bottom
+Z / -ZFront / Back

The cube is a 3×3 grid of hit regions, so it offers 26 views: the middle of a face snaps to that face's view, the strip along a border snaps to the edge view between two faces (45° between them), and a corner square snaps to the corner view (an isometric-style direction). The coloured axis arrow heads are clickable too: clicking one looks along that axis. Right-click a planar B-rep face and choose Look at face to view it head-on.

The same views are on the keyboard (ignored while typing in a text field, and with Ctrl, Cmd or Alt held; the optional single-letter command keys never take these):

KeyViewKeyView
1Front4Left
2Back5Top
3Right6Bottom
iIsometric (front-top-right)

Command Search and Single-Key Commands ​

Ctrl+K (Cmd+K on macOS), or / when focus is not in a text field, opens a command search over the whole interface: every Edits-panel operation, every toolbar and dropdown action, and every sidebar panel, including the ones inside Advanced. Type a few letters and the list narrows by fuzzy match (fil finds Fillet before Fill Surface); an operation also answers to its command-bar name, so union finds the Union button. Each row shows its icon, its group (the operation's category, the dropdown it lives in, or Panels / Advanced) and its shortcut, so the list doubles as the shortcut reference. ↑/↓ move, Enter runs, Escape closes and returns focus to where it was.

  • Choosing an operation opens its form exactly as clicking its button would (the Edits section is expanded if it was collapsed). A B-rep-only operation on a mesh source is shown dimmed and refuses with a status message.
  • Choosing a toolbar action runs it, the same as clicking it in its menu.
  • Choosing a panel expands it (and the Advanced group above it) and scrolls it into view.
  • With nothing typed, the most recently chosen items come first.

With cadPreview.singleKeyCommands on, these unmodified keys work when focus is not in a text field (a field, the command bar and the search itself always keep their keys):

KeyCommandKeyCommand
EExtrudeHHole
RRevolveMMove
FFilletSShell
CChamferPLinear Pattern
QMeasure mode (toggle)Shift+RRepeat the last command

Always on (no setting needed): Ctrl+Z undoes the last edit, Ctrl+Shift+Z / Ctrl+Y redo it (never inside a text field, which keeps its own undo; undo still cannot cross the save point), and ? opens the Keyboard shortcuts sheet — every bound key, generated from the same table the keys use, with bare letters shown as inactive while cadPreview.singleKeyCommands is off. The sheet is also in the command search ("Keyboard shortcuts").

A key opens the operation's form; it never applies anything. Shift+R reopens the form of the last command: the last applied operation's values are filled back in (numbers and vectors; the operands are re-selected where the model still has them), or, if you only opened a form without applying, that form. It never applies either. The keys never take the view snaps (1–6, i), are ignored with Ctrl, Cmd or Alt held and on key auto-repeat, and are off by default; the search lists them dimmed while the setting is off. Each letter is declared once, beside its catalog entry (src/webview/opCatalog.ts), and opCatalog.test.ts refuses a duplicate or a collision with a view snap.

Component Tree Panel ​

The Browser ​

The Browser section at the top of the sidebar is one tree over everything the document already has, so you do not need to open five panels to find something. It stores nothing of its own: it is rebuilt from the same models the other panels use, and each row's action does exactly what the matching panel does.

GroupRowsRow action / eye
Documentfile name and format, display unit, saved / unsaved-edit countread-only
Named Viewsthe saved view bookmarksclick to restore (same as View ▾)
OriginAxes and grid, then each saved construction planeeye toggles axes + grid; click a plane to cut with it
Bodieseach solid, with its Part colour and face counteye hides/shows it (same as the Components eye); click highlights it
Sketchesthe free sketch faces and wires, when there are anyeye hides/shows the group
Featuresthe edit history, baked rows markedeye suppresses/unsuppresses the op (not available inside the saved prefix)
Parts, Layerseach Part / layer with its coloureye hides/shows it (same as the panel)

The Browser with every group expanded: document facts, named views, origin, bodies, features, Parts and layers.

A group's own eye drives all of its rows to one state. Expand all / Collapse all and the search field in the header work across the whole tree; filtering expands the groups so matches are visible. The section is off by default (the sidebar column is already full): turn it on with View ▾ ▸ Browser. Whether it is shown is saved in the view sidecar; which of its groups are expanded is not (only Bodies is open by default). Not shown yet: tessellation quality in the Document group, per-axis or per-plane visibility, and individual sketch rows.

For multi-solid STEP/IGES assemblies or glTF scenes with multiple meshes, the component tree panel shows the model hierarchy. Click any row to highlight that solid/mesh in the 3D view (all others are dimmed). Click the same row again or click an empty area to deselect.

For STEP sources specifically, the tree reflects the file's own real assembly structure when it has one — nested "Assembly"/"Component" groups matching how the file's author organized it, instead of always flattening every solid into one list (product/component names aren't shown — they're unreadable in this build's OCCT WASM — and an assembly wrapper with no real internal structure, or a source with none at all, falls back to the flat list exactly as before). A group-header row (an "Assembly N" line) is informational only — clicking it or its eye-toggle has no effect; only a leaf ("Solid N") row highlights/hides, exactly like every row always has.

Click the search icon in the Components header to reveal a filter field and narrow the list to rows whose name matches (case-insensitive substring) — matching rows and their ancestors stay visible so a match is never hidden inside a collapsed-looking branch. Escape (or the search icon again) closes the field and clears the filter, so a hidden box never keeps hiding rows. The section stays titled Components (the source format is on the document chip). Rows show a cube for an assembly group and a box for a solid; a solid's right-hand number is its face count and an assembly's is how many solids it holds (hover either for which). Each row also has an eye-toggle, shown when you hover or focus the row (and always while something is hidden), to hide/show that solid/mesh (and its edges/points) in the 3D view — a display-only toggle, same as the Parts panel's (see below), never saved to a sidecar.

The Components tree, showing the STEP root and its solid with a face-count badge.

Measuring ​

The Measure ▾ toolbar menu lets you measure distances, edge lengths, angles, and circle/arc radii directly in the 3D view — display-only by default, and never an edit operation, but a result can optionally be pinned so it survives closing the file (see below).

  1. Open Measure ▾ and click Measure mode (orbit/pan/zoom still work normally — a measurement pick is a click without a drag, same as part selection).
  2. Pick a tool from the dropdown: Distance and Angle need two picks; Edge Length and Radius resolve from a single click.
  3. Click in the view. Distance: click two points anywhere on the model. Edge Length: click one edge. Angle: click two faces or edges. Radius: click one circular/arc edge. A line (for Distance/Angle) plus a floating label with the result appears, and stays readable while you zoom.
  4. Click Clear to remove the current result, or switch tools/toggle Measure off to start over.

Measurement precision follows the model's tessellation (the same 0.1 deflection tolerance used for display), not exact CAD geometry — fine for visual estimates, not for metrology-grade output. Distance, Edge Length, and Radius results are shown in whatever unit the view-controls Units dropdown is set to (see Units above); Angle is always degrees.

For a STEP/IGES/BREP model, a ⟟ Exact button appears next to a completed Distance, Edge Length, or Radius result (not Angle — there's no exact counterpart for that one). Clicking it asks the extension host to recompute the same measurement against the true OCCT geometry instead of the displayed triangulation — the readout updates to D_exact/L_exact/R_exact = … once it comes back. This is a real (if fast) computation, not instant like the triangulated result, and only works for CAD sources — mesh formats (STL, OBJ, …) have no exact B-rep geometry to fall back to, so the button never appears for them.

Pinning a measurement ​

A 📌 Pin button appears next to a completed measurement result on any source kind (unlike ⟟ Exact, which is B-rep only). Beside it sit three small optional tolerance fields — nom, +, and −. Fill at least nom and + (leave − blank for a symmetric ± band) and the pinned annotation records that tolerance band alongside the measured value; pinning with only nom filled still pins, but tells you no band was recorded. Clicking Pin saves the result as a persisted annotation — a "Saved" list at the bottom of the Measure ▾ panel shows every pinned measurement, with a Show action (re-displays that overlay, no recompute) and a ✕ to delete it. Annotations survive closing the file, saved to a <model>.annotations.json sidecar next to the CAD source (the CAD file itself is still never touched).

A completed 2-point measurement (Distance or Angle) now renders as an actual dimension: arrowheads at both measured points, short witness marks, and the value label — not just a bare line. A toleranced pin shows its band in the label (12.5 mm [10 ±0.05]), and if the frozen measurement falls outside its own band, both the re-displayed label frame and its Saved-list row are coloured red — a presentation choice derived from the stored facts; nothing stores a pass/fail verdict. Pinned annotations also appear as dimension glyphs (extension lines, arrowheads, value labels) in File ▾ → Export Silhouette SVG/DXF… drawings.

Unlike Markup strokes (screen-space pixels with no 3D anchoring at all), a pinned annotation stays attached to the actual entity it measured — a geometric best-effort match runs automatically whenever you apply a topology-changing edit elsewhere on the model, the same matching that already keeps Parts assigned correctly across edits. If the specific entity an annotation anchored to is later removed or fused away (a boolean, for example), the annotation degrades honestly: its row in the Saved list goes struck-through and Show disables, rather than silently pointing at the wrong geometry.

Defining Parts ​

CAD Preview lets you group geometry into named parts (the FEM sub-model-part / boundary-group concept). The Parts panel sits below the component tree in the left sidebar.

To assign geometry to a part:

  1. Open Select ▾ in the toolbar, click Selection mode, and choose a pick target: Vol (solids), Surf (faces), or Line (edges).
  2. Click entities in the 3D view to select them — they highlight blue. Shift-click to add or remove from the selection; a plain click selects just one; clicking empty space clears the selection.
  3. Click + New in the Parts panel to create a part, then click the + on that part's row to assign the current selection to it.

Define by rule (B-rep sources). Instead of pinning the faces you picked, hover a part's row and choose Rule… to keep a rule taken from the selected faces: On the selected plane (every face on that plane, either orientation), Inside the selection's box (every face whose bounding box lies inside the union box of the selection) or Same surface type and radius (every cylinder, sphere, cone or torus of that radius). The rule is re-evaluated after every rebuild with a tolerance of 1e-3 × the model diagonal, so a Part defined from an end face still holds exactly that end face after the beam is lengthened; the row shows the rule and Clear rule returns it to a plain list. A selection no single rule describes (faces on two planes, a cylinder and a sphere) is refused with the reason on the status line. Edge and vertex rules, and mesh sources, are not supported.

Each part is a compact row: a colour swatch (click to recolour), an editable name, a 1 · 0 · 0 count of its volumes · surfaces · lines (a fourth number appears only if it has points), and an eye-toggle to hide/show just that part's entities. The + (assign the current selection) and delete buttons appear when you hover the row or focus into it. Assigned entities are painted in the part's colour in the 3D view. Click the chevron to expand a part (entity lists start collapsed) and remove individual entities; click a part row to highlight all of its entities. A part's target mesh size is edited in the FE Mesh panel's Part sizes section.

The panel header's Isolate button (target icon) shows only the currently-selected part's entities, hiding everything else; click it again (or select a different part and click it again) to clear isolation. Isolating composes with the per-row eye-toggles rather than overriding them — a part you'd already hidden stays hidden after you clear isolation. Like the eye-toggles, isolation is display-only and is never written to <model>.parts.json.

Each part row has a small density field (g/cm³, e.g. steel 7.85, aluminium 2.7; empty = unset) and a Material… picker built from the same table headless set_part's material uses (steel, stainless steel, aluminium, copper, brass, titanium, ABS, PLA, PETG, nylon, water). Picking an entry sets the density and, only while the Part still has its automatically assigned palette colour, that material's suggested colour (a colour you chose is never overwritten). Only the number is saved: the name is presentation, shown while the density still equals the picked value, and typing a different density clears it. Both controls appear when the row is hovered or focused. The header's Copy BOM button (copy icon) copies the bill of materials — one row per part (name, entity counts, volume/area, density and mass in grams) as tab-separated text, ready to paste into a spreadsheet — computed live over the current model. It is enabled only for a B-rep source with at least one part defined (mesh sources have no per-part rows to compute). A part without a density leaves its Density/Mass cells empty — never zero.

Beside it, Copy hole table (plate-with-holes icon) copies the hole schedule — one row per hole size and axis direction: diameter, axis, count, the face-N/solid-N ids, and the nearest standard thread designation with its signed delta — as the same tab-separated text the generate_hole_table MCP tool returns. It needs no parts, only a B-rep source.

Parts usually survive topology-changing edits. Ops like Boolean, Fillet, and feature modeling rebuild the model's face/edge numbering, but CAD Preview automatically tries to re-match each part's assigned entities to their new numbering by geometry (same location, same area/length) right after you apply such an edit — so a part assigned to a face before a fillet elsewhere on the model typically keeps pointing at the right face afterward, with no action needed. This is a best-effort match, not a guarantee: an entity that genuinely merges or disappears (two faces fused into one by a Boolean, for instance) can't be matched to anything and is quietly dropped from the part, same as reopening a file with a stale reference. Undoing or removing an earlier op doesn't trigger a re-match (only applying a new one does).

The Parts panel with three colour-coded parts expanded to show their assigned volumes, surfaces, and edges.

Parts are saved automatically to a <model>.parts.json sidecar next to the CAD file and reloaded when you reopen it — the CAD file itself is never modified. See Parts Sidecar for the format.

Mesh formats (STL/OBJ/PLY/glTF) have no stored face/edge topology. CAD Preview segments each mesh into connected, near-coplanar facets on load, so Surf picks a flat face (a cube → its 6 faces) and Vol picks the whole object. Highly curved meshes that would split into very many facets are kept whole; Line and Point are disabled for meshes.

Organizing Layers ​

The Layers panel with two layers: a Construction layer holding two surfaces, and a hidden Centre lines layer holding three edges.

The Layers panel sits right below Parts in the left sidebar. Where a part says which entities form a finite-element sub-model, a layer says which entities are shown, locked or drawn together — the drafting meaning of "layer" (construction, hidden, dimensions, centre lines), not a second kind of part.

Each layer is a compact row working exactly like a part row: a colour swatch (the panel swatch and the layer's drawing-export colour), an editable name, a volumes · surfaces · lines count, an eye (show/hide — unlike a part's eye, this persists: a hidden layer stays hidden for drawings and agents), a lock (members stay selectable for measurement but refuse as edit operands and Transform Gizmo targets, with a named message telling you which layer to unlock), plus hover-revealed assign and delete buttons. Click + New to create a layer, then the + on its row to assign the current selection (any pick mode) to it — each entity sits on exactly one layer, so assigning moves it out of wherever it was. Unassigned entities sit on the Default layer, which cannot be deleted; deleting any other layer returns its members to Default.

Like parts, layer membership follows topology-changing edits automatically (rebound with a report, never silently repointed). Unlike parts, layers never touch meshing or solver output: a hidden layer is still meshed, and no physical group or SubModelPart follows a layer. The drawing exports (Export Silhouette SVG/DXF…, Export Technical Drawing…, Export Drawing Sheet…) can be restricted to chosen layers — one output group per layer — or left to draw every layer, which is the default.

Layers are saved automatically to a <model>.layers.json sidecar next to the CAD file. See Layers Sidecar for the format.

Inserting Standard Parts ​

The Standard Parts panel (below Parts in the sidebar) searches the hosted step.parts catalog — fasteners, bearings, connectors, extrusions, and more — and inserts a result as an ordinary STEP file:

  1. Type a query (e.g. "M6 hex bolt") and click Search.
  2. Each result shows a thumbnail beside its name, description, category, and standard designation (thumbnails load lazily after the text — a result with no image simply shows its text). Click Insert… on one.
  3. A Save dialog appears, defaulting to <part-id>.step next to the currently open document. Saving downloads the file (its checksum is verified against the catalog's own recorded SHA-256, when one exists) and opens it as a new tab; dismissing the dialog is a no-op.

The Standard Parts panel showing two search results with their Insert buttons.

An inserted part opens as its own document rather than merging into whatever model you already had open — combine it with your existing model using the ordinary Editing Geometry tools (position it with Move/the Transform Gizmo, then Boolean/Mate it in) once both are open. This requires network access to api.step.parts; if the service is unreachable, the panel shows a clear status message rather than an error.

Editing Geometry ​

The Edits panel (below the Parts panel) applies non-destructive edit operations to the model. Edits never touch the CAD file — they are saved as an ordered, replayable op-list in a <model>.edits.json sidecar and re-applied each time you open the file.

Every parameter form previews live: while a form is open, the viewer shows what that op would produce as a translucent overlay, re-computed as you type (~250 ms debounce) and coloured by intent — green where material is added, red where it is removed, blue for wire/reference results (points, lines, sketch faces), neutral for transforms and fillet/chamfer. Switching forms or changing the selection cancels the preview; only Apply commits anything to the history.

Each open form starts with a header — the op name, a dot in the preview's intent colour (green adds, red removes, blue reference, grey neutral) and a ▾ chevron. Collapsing the header hides the fields and cancels the live preview without discarding anything you typed; expanding it brings the values back and re-previews. The Apply row stays pinned below either way.

The Extrude, Revolve, Shell, and Draft forms also carry a Pin query row: pick exactly one face, choose the producing op and bucket role it came from (e.g. an extrude's end cap), and click Synthesize to name that face as a stored query. The query is attached when you click Apply (as long as the selection hasn't changed since) and survives later edits renumbering the model's faces — the same mechanism agents use headlessly, now available interactively. Edge/volume operands and multi-face picks can't be pinned yet; the row says so rather than offering a dead button.

The operation buttons are also on the ribbon's Model tab, and the panel's own button grid is collapsed behind the grid button in its header by default (see above).

The panel is organised into two top-level tabs — GEOMETRY (create new entities) and EDIT (modify existing ones) — sharing one undo/redo/Clear header and one operation-history list. The GEOMETRY tab is further split into 2D (points, lines, curves, sketch profiles) and 3D (solid primitives, holes) subtabs. Each tab shows a grid of operation buttons (icon + name); clicking a button opens its parameter form below the grid, and clicking it again collapses the form. For mesh sources the whole 2D subtab and every other B-rep-only button grey out.

The GEOMETRY tab, 3D subtab: solid-primitive creation ops (Box selected).The EDIT tab: Transform (Move/Rotate/Scale/Mirror) and Boolean (Unite/Subtract/Intersect) ops.

Left: GEOMETRY → 3D primitive creation. Right: the EDIT tab's modification ops.

To apply a transform:

  1. Open Select ▾, click Selection mode, choose Vol, and click one or more volumes (solids).
  2. In the Edits panel open the EDIT tab and pick an operation — Move, Rotate, Scale, or Mirror — and fill in the numeric fields.
  3. Click Apply. The model updates live and the operation is added to the list.

Transform Gizmo ​

Opening Move, Rotate, or Scale with a selection active also attaches a draggable 3D gizmo to the selected volume(s), centred on their shared bounding-box centre — dragging it is a live preview only (nothing is applied yet) and fills in the same numeric fields the form already shows, so typing and dragging stay in sync. Move shows a 3-axis arrow set (drag one to move along that axis only); Rotate shows rotation rings about the pivot; Scale shows drag handles. Selecting two or more volumes moves/rotates/scales them together as one rigid group, not independently. Orbit/pan is automatically suspended for the duration of a gizmo drag. Releasing the drag leaves the preview showing — nothing is added to the op list until you click Apply; switching to a different form, or clicking a different selection, discards an uncommitted drag and snaps the model back to where it was. Snap to grid and Snap to points (see View ▾ above) both apply live during a Translate drag.

GEOMETRY → 2D (all B-rep only; each is typed-in, no selection needed unless noted):

OpAction
Point / Line / ArcAppends a standalone point / straight line / circular arc you can select later (Point/Line mode). A Point typed at an existing point's coordinates asks inline whether to Collapse (reuse that point-N, no new point) or Keep independent (a second point there); the choice is recorded on the op so replay never re-asks. The same prompt appears when a Line / 3-Pt Arc / Polyline / Spline / Bezier endpoint typed as a raw coordinate lands on an existing point: Collapse snaps it there and shares that vertex (no extra point-N), Keep independent builds its own vertex (a point-N slot already shares by construction). Assign a point or straight line to a Part and it is meshed on exactly, so a load or sensor location lands on a real node (curved free edges are reported instead, since a line has only two endpoints)
Use picked points (every curve form below)Pick points in Point mode, open Line / Arc / Polyline / 3-Pt Arc / Spline / Bezier / Ell. Arc / Helix, and click Use picked points — the slots (or, for the list forms, exactly one row per point) bind to the picked point-N ids in pick order, lock behind an id chip (click × to type coordinates instead), and the curve follows those points. If ids are later renumbered the curve re-anchors by the position it was authored at (with a status warning), and is skipped with a diagnostic when no point is there any more
PolylineAppends straight edges through an editable list of points (+ Add point / − rows); Closed adds the last→first edge
3-Pt ArcAppends the circular arc through three typed points (a collinear triple is skipped)
SplineAppends a smooth curve through the point list (endpoint-exact fit)
BezierAppends a Bézier curve over the control-point list (passes through first and last only)
Ell. ArcAppends an elliptical arc — Radius X along Up, Radius Y perpendicular, trimmed Start°→End°
HelixAppends a helix: Turns revolutions of Pitch height around Axis from Base, radius Radius
f(s) CurveAppends a curve given as functions of a parameter s: type x(s), y(s), z(s) (expressions in s and the document variables, trig in degrees; the fields suggest s and the variable names) over s min…s max, sampled at Samples points (2–2000) and drawn as an endpoint-exact Spline or a Polyline. Origin / Normal / Up place the sketch (sample (x, y, z) lands at origin + x·u + y·v + z·n; the defaults are the world axes). Close into a face also draws the straight segment from the last sample back to the first and builds the flat face that loop bounds, so it appears under "Sketches" and feeds Extrude/Revolve directly (the curve's edges and the closing segment are ordinary lines). The curve stays an expression: changing a document variable re-draws it. A curve that touches or crosses its own closing segment (a full sine period) bounds regions of opposite winding with zero net area and is refused with a message; close one lobe instead, e.g. s from 0 to 0.5
Circle / Rectangle / Polygon / Ellipse / Rounded / Slot / TrapezoidSketch — appends a flat profile face you can later select (Surf mode) and feed into Extrude/Revolve/Sweep/Loft. Rectangle-family shapes take an Up direction for in-plane orientation. Circle/Rectangle/Polygon additionally offer a Plane picker: choose a saved plane-N to place the sketch from the plane plus Offset U/V (and Rotation° for rectangle/polygon), or leave it on Custom and type the center by hand — picked placement fills the coordinate fields in and locks them until you switch back
Surface (Build from selection)Select ≥3 lines (Line mode) that close into a loop and Build — assembles them into a new flat face under "Sketches"
Fill Surface (Build from selection)Select 3+ edges (Line mode) forming one closed boundary, Build — appends a smooth, generally non-planar face through them under "Sketches". It is an approximation (a quarter-cylinder boundary fills to within 0.7 % of the radius), not an exact analytic surface; use it to close a curved gap before Surfaces → Volume
Round Corner (Build from selection)Select the two adjacent segments of a sketch polyline (Line mode), enter a Radius to round the corner, or a Distance (and optionally Distance 2 along the second segment) to bevel it, then Build — the corner is replaced in place with an arc or a bevel; straight segments only
Edge Slot (Build from selection)Select one edge (Line mode), enter a Width, Build — appends a stadium slot face around that edge (length = edge length + width) under "Sketches"
Construction (guide) checkboxEvery 2D profile/curve form offers one: the built entity is marked reference-only — rendered dimmed, still pickable/measurable, and refused as a profile operand by Extrude/Revolve/Sweep/Loft/Surface/Volume

GEOMETRY → 3D:

OpAction
Box / Sphere / Cylinder / Cone / Torus / PrismAdd — appends a new body at that placement (no selection needed; all formats)
WedgeAdd — appends a right-angular wedge: base Size X×Size Y centred at Base ctr in the plane ⟂ Axis, extruded Height; the far edge narrows to Top X (B-rep only)
Hole / C'bore / C'sinkSelect target volume(s) (Vol mode), place the mouth (Mouth + Axis pointing into the material), and Cut — drills a plain, counterbored, or countersunk hole (all formats). The Standard dropdown fills Radius (and, for a tapped size, a sensible blind Depth) from the ISO metric / UNC / UNF tables — pick e.g. M6 tapped or M6 clearance instead of looking the number up; the fields stay editable afterwards
Volume (Build from selection)Select ≥4 surfaces (Surf mode) that close into a shell and Build — sews them into a new closed solid (B-rep only)

EDIT:

OpAction
Move / Rotate / Scale / MirrorEnter parameters, Apply to the selected volumes (all formats)
Unite / Subtract / IntersectSelect operand-A volumes and click Set A, then select operand-B volumes and click Apply (all formats)
Fillet / ChamferSelect edges (Line mode), enter the radius / setback, Apply (B-rep only). A fillet also takes an optional End radius: leave it blank for a constant fillet, or fill it to vary the radius from Radius to End radius along the edge — or along one connected chain of edges, interpolated by length. A radius too large for the faces next to the edge is refused (the history row shows why) rather than risking the kernel
Extrude / Revolve / Sweep / LoftSelect a profile face (Surf mode; a path edge too for Sweep, 2+ faces for Loft), set parameters, Apply — builds a new body (B-rep only). Extrude/Revolve/Sweep also take Regions to narrow a multi-region profile (blank = face as modeled, all, or indices like 0,2). Loft takes an optional captured Rail (Set rail on 1+ Line-mode edges): resampled intermediates steer the surface through the rail — exactly 2 closed sections, no thinning
ShellSelect the opening face(s) (Surf mode), enter a wall thickness (negative = walls grow inward, the usual hollow), Apply — hollows the solid(s) owning those faces (B-rep only)
DraftSelect the face(s) to taper (Surf mode), enter an Angle° (0–90), and optionally a neutral-plane Point + Normal — leave both at 0 to taper each face about its own plane — then Apply (B-rep only). Known kernel limitation: the bundled OCCT WASM build's draft engine fails on real geometry (probed — see CLAUDE.md), so the op currently reports a "did not apply — kernel limitation" diagnostic in the history instead of changing the model; it will work without changes once the upstream build fixes it
Unify facesApply (B-rep only; no selection needed). Merges adjacent faces and edges that lie on the same underlying surface across the whole model — the seam a boolean union leaves between coplanar faces. Face and edge ids are renumbered, so Parts and annotations are rebound or reported dropped. A model with nothing to merge is left unchanged and the history row is marked skipped. Typed form: unifySameDomain {} (a bare name opens this form instead)
DefeatureSelect the face(s) to remove as recognized features — fillet bands, chamfers (Surf mode) — then Apply (B-rep only). The solid heals behind the removed faces; faces that bound no removable feature are left in place
SplitSelect volumes (Vol mode), define the plane, choose which side(s) to Keep, Apply (B-rep only)
SectionSelect volumes (Vol mode), define the plane, Apply — appends the planar cross-section as a sketch face, leaving the solids untouched (B-rep only)
DrillSelect volumes (Vol mode) plus a profile face (Surf mode) or wire (Line mode), set Dir/Length, Apply — cuts the profile's regions through the volumes. Regions narrows a multi-region profile: blank (face as modeled), all, or indices like 0,2 (0 = outer boundary) (B-rep only)
RibSelect an open spine sketch (Line mode), set a wall Thickness and an Up-to face, Apply — extrudes the wall to that face (plus one thickness of embed), fuses it into the surrounding solids, and blends the junction (B-rep only)
WrapSelect a flat sketch face (Surf mode) — a holed profile (e.g. an imported letter with a counter) is supported, its hole cut out of the developed shell — pick Cylinder or Cone target, set axis/radius (half-angle for cones) + wall Thickness and variant — Standalone appends the wrapped shell, Emboss fuses it into the target volumes, Engrave cuts it out (B-rep only)
ExplodeDrag the slider (or type the factor) for a live preview — bodies spread radially from the model centre as you drag, snapping back at 0 — then Apply to commit it as an operation (all formats)
MateSelect two faces (Surf mode): face A then face B, and Apply — aligns A onto B (B-rep only)
AlignSelect volumes (Vol mode); choose an Axis (X/Y/Z), an Extent (min/center/max of each volume's own bounding box), and a target coordinate To; Apply — moves each selected volume along that one axis so its own chosen extent lands exactly on the target. Every targeted volume aligns independently, even when the whole model is selected (all formats)
Linear PatternSelect volumes (Vol mode); set a Direction, a Spacing, and a Count (total instances, including the original); Apply — appends Count − 1 evenly-spaced copies of each selected volume (all formats)
Circular PatternSelect volumes (Vol mode); set an Axis point + Axis direction, an Angle step, and a Count; Apply — appends Count − 1 copies arrayed around that axis at equal angular spacing (all formats)

Reference helpers: the op model itself accepts references instead of typed coordinates — Mirror, Split, and Section can take midplaneFaces: [faceA, faceB] (two planar, parallel faces; the op acts on the plane halfway between them) and Circular Pattern can take midaxisOf: [cylA, cylB] (two cylindrical faces or two parallel edges), each mutually exclusive with the typed-in vectors. These are authorable headlessly via apply_edit_ops (see doc/mcp-server.md) and degrade with a clear diagnostic when the referenced faces are missing, non-planar, or not parallel; mesh files only accept the typed-in coordinates. Interactively, the Planes panel's Midplane… button (below the saved-plane list) builds a new named plane halfway between two already-saved ones — the same math, computed client-side.

Header controls: ↶ / ↷ undo / redo the last operation; Clear removes all operations (back to the original model). To remove one specific operation without discarding everything applied after it, hover its row in the history list and click the ✕ that appears — unlike Undo, which only pops the most recent operation, this removes any row directly. The history is also a clickable timeline: undone-but-redoable steps appear as dimmed rows with continued numbering, and clicking any row — applied or dimmed — rolls the whole stack straight to that point in one step (clicking a dimmed row re-applies through it). The ● button on a row suppresses it: the row is struck through and replay skips it, but the operation stays in the list and ○ restores it (Parts follow the geometry, as with a removal). Double-click a row's name to rename it (Enter commits, Escape cancels). Removing a row that later operations or Parts mention asks for a second click on ✕ and names what may stop resolving. Rows inside the saved prefix (🔒) can do none of this.

The operation-history list — an ordered, individually-removable stack of applied edits.

Transforms, booleans, explode, primitives, and the hole family work on both B-rep and mesh files; everything else is B-rep only (the panel disables those buttons — and the whole 2D subtab — for meshes). Creation ops append a new body to the model; holes are the exception — they cut into the selected volumes. For primitives, center is the body's geometric centre (box/sphere/torus) or its base centre (cylinder/cone/prism/wedge, extruded along Axis) — matching how the underlying CAD kernel places them. A 2D profile sketch builds a flat face, not a body — it's meant to be picked and extruded/revolved/swept/lofted; doing so consumes the sketch into the new solid rather than leaving a duplicate flat face behind. Building a Surface or Volume needs an already-closed selection (a loop of lines, a sealed set of surfaces); an open selection is silently skipped rather than producing a malformed body — the same graceful-skip rule every op follows when its inputs don't resolve.

The operation buttons' icons are placeholder glyphs — they live in one file, src/webview/opIcons.ts, made to be swapped for real icons.

When you Export an edited model, the edits are baked into the output file. See Edits Sidecar for the format.

The Command Bar ​

A one-line command bar sits just above the status bar for typing edit operations instead of clicking through a form. A line is <command> [<json-body>]: the command is the operation's name (case-insensitive) and the body is the same JSON object an agent passes to apply_edit_ops. $sel stands for the live selection wherever an id or list of ids goes, expanded by the type of the field — a targets field takes the selected solids, edges the selected edges, profile exactly one selected face, a curve's points the selected points.

addBox {"center":[0,0,0],"size":[10,5,3]}
translate {"targets":$sel,"vec":[5,0,0]}
addLine {"start":"point-3","end":"point-7"}
  • Enter runs the line through the same validation and the same history as the panel's Apply buttons, so a typed op and a panel-built op are identical once pushed (and undo, redo, jump, save and export treat them alike). A rejected line shows the expected parameter shape and posts nothing.
  • A bare command (addCylinder, no body) opens that operation's form, like clicking its button. A line that is valid JSON but rejected opens the form prefilled with the numbers and vectors it could use.
  • Tab completes a command name, or an entity id (face-1 → face-12) from what the model currently contains; Up/Down walk the history; Esc clears the line.
  • Panel → bar: every op you build with a panel button writes its equivalent command into the bar, so the typed form can be learned from the clicks.
  • B-rep-only commands (fillet, chamfer, features, sketches, …) are refused on a mesh source with the same reason the greyed panel buttons give. If the kernel later skips a typed op (for example its ids no longer exist), the bar reports the skip's diagnostic rather than leaving a silent no-op.

Commands: addPoint, addLine, addArc, addPolyline, addThreePointArc, addSpline, addBezier, addEllipseArc, addHelix, addParametricCurve; the seven sketch profiles (addCircleProfile, addRectangleProfile, addPolygonProfile, addEllipseProfile, addRoundedRectangleProfile, addSlotProfile, addTrapezoidProfile); buildSurface (surface), edgeSlot (slot), buildVolume (volume); the primitives addBox, addSphere, addCylinder, addCone, addTorus, addPrism, addWedge; addHole, addCounterboreHole, addCountersinkHole; translate, rotate, scale, mirror; union, subtract, intersect; fillet, chamfer; extrude, revolve, sweep, loft, rib, wrap; shell, draft, defeature, unifySameDomain, splitByPlane, splitFace, section, drill; explode, mate, align, patternLinear, patternCircular. Run Describe capabilities (MCP) or see the Edits panel forms for each body's fields. The bar is webview-only: it needs no extra setup and writes nothing the panel does not.

Parametric Variables ​

The Variables table at the top of the Edits panel makes the model parametric: define named values once, reference them in any numeric field of any edit operation, and change them later to rebuild the geometry on the fly.

  1. Click + New in the Variables header. A variable appears with an auto-generated name (L1, …) — rename it inline and set its expression (e.g. 20). The computed value shows to the right of the row.
  2. In any op's parameter form, type the variable name — or an arithmetic expression like L/2 + 1 — instead of a number, then Apply/Add as usual. The op is created with the current value and remembers the expression (the history line shows it as [length = L/2 + 1]). A plain number may use a decimal comma (0,5 reads as 0.5; a comma is never a thousands separator). Inside an expression a comma only separates function arguments, e.g. max(1, 5); 1,5+2 is refused with a message naming the rule.
  3. Edit the variable's expression in the table — every operation referencing it re-resolves and the model rebuilds immediately.

The Variables table with two variables — L = 20 and the derived H = L / 2 = 10.

Expressions support numbers, variable names, + - * / ^, parentheses, sqrt/abs/min/max/floor/ceil/round, sin/cos/tan (degrees, matching the angle fields), and pi. A number may carry a bracketed unit — 2[in], 10[mm], 5[deg], 0.5[rad] (mm cm m in ft, deg rad) — and is converted to millimetres / degrees, so W = 2[in] is 50.8; plain numbers keep their meaning. A variable's own expression may reference the variables defined above it in the table (so W = L/2 works; the ▲/▼ buttons reorder, and refuse a move that would put a variable above one it references). Because the fields are free-text, they no longer have browser spinner arrows — type the value.

If an expression can't be evaluated at Apply time (unknown name, syntax error), the apply is blocked with an inline message. If a referenced variable is later deleted or renamed, affected operations keep their last computed values — a warning names the missing variable, and re-adding it restores the parametric link. The delete button's tooltip warns when a variable is still referenced (see Used by). Variables persist in the same <model>.edits.json sidecar as the operations (format); variable edits are not part of the op undo/redo history.

The Parameters table ​

The Variables table is also a parameters table:

  • Columns: name · expression · Unit selector (writes the bracket syntax, 2[in], into a plain-number expression — there is no second unit store) · computed value · comment · ▲/▼ reorder · delete. The star pins a variable to the top of the view; the stored order, which is also the evaluation order, never changes. The Filter box matches name, expression and comment.
  • Import… / CSV… / JSON… read and write Name, Expression, Comment rows. An import adds the valid rows after the existing variables and names every rejected one in the status line (bad name, duplicate, a reference to a name not defined above, unparsable expression); a row is accepted whole or not at all.
  • Apply each change as you leave its field is on by default. Turn it off to collect edits and commit the whole table in one step with Apply (or drop them with Revert).
  • Used by: a variable that something references shows a ▸ N toggle on its row (N = referencing edit fields + other variables). It lists each referencing op as #3 translate — vec[1] and then the other variables whose expression uses it. Clicking an op row selects that row in the Edit history (outlined, scrolled into view) without jumping the timeline. N is the same count the delete button's tooltip quotes ("referenced by 3 edit fields and 1 variable"). A reference is a name stored in the op, so "Used by" is exact — unlike the positional face-N ids.
  • Autocomplete: in every numeric op field and every variable expression, typing an identifier opens a suggestion list of variable names and the expression functions (sqrt, sin, pi, …). A variable's own expression is offered only the variables above it; op fields are offered all of them. ↑/↓ move (wrapping), Enter or Tab accept (a function gains its (), Esc dismisses and leaves the text as typed. With the list closed, Enter and Tab behave as before. No list appears inside a [unit] suffix or after a digit.
  • Model parameters lists every numeric field of each op in the history (#3 extrude, length), editable in place, and → var promotes a field to a new variable: the field then follows the variable. Rows saved into the source file (🔒) are read-only, and a field already driven by an expression shows it instead.

Configurations (design table) ​

The Configurations grid: a Base row plus Short and Tall, one column per variable, with Tall active and its overridden cells in bold.

Under the Variables table, the collapsible Configurations section is a grid: one row per configuration (plus a Base row for the variables as written), one column per variable.

  • Add a configuration by typing a name and pressing Add or Enter. It starts as a copy of the active configuration's overrides.
  • Pick a row with its radio button to make it the active configuration: every op field bound to a variable re-resolves, and the viewer rebuilds. Base returns to the plain variables.
  • Edit a cell to override that variable in that configuration. An empty cell (shown with the base expression as a faint placeholder) inherits the variable's own expression; the small number beside the cell is the evaluated value. Overridden cells are bold.
  • A cell whose expression cannot be evaluated is marked with a warning, and the model freezes at that variable's last good value instead of failing.
  • Suppress ops (the last column) takes history row numbers, e.g. 3, 5: under that configuration replay skips those rows, so one model can have a fillet in one configuration and not in another. The stored ops never change; the History row shows a dotted, italic cue (distinct from the ● suppress strike-through, which is yours and applies everywhere). Rows already saved into the source cannot be listed.
  • Rename a configuration by editing its name; × deletes it. Renaming a variable carries its overrides along.

Configurations are saved in <model>.edits.json (format); switching is not part of the op undo/redo history. For headless use, set_configuration defines them and the export and meshing tools take a configuration parameter (MCP server); Batch Export can write one file per configuration.

OpenSCAD Customizer ​

The Customizer section (below Edits, .scad sources only) is the interactive half of the list_scad_parameters / convert_scad MCP tools: a form over the file's Customizer parameters, so changing a dimension never means editing the source.

Parameters render grouped by their [Group] headers as number fields, checkboxes, dropdowns or text fields, each with its declared range and default. Edit the values and click Apply — the file is re-evaluated with the values as -D overrides and the geometry reloads (bundled WASM, with the usual cancellable progress for a slow conversion). Reset clears every override back to the file's authored defaults. The .scad itself is never modified; overrides persist in <model>.scad-params.json (format) and are replayed on every open, including the headless load_model / generate_mesh / export_brep paths. An override that changes a dimension can renumber edge-N / face-N ids authored against the defaults — Parts and annotations rebind where they can, and the reload says so on the status line instead of claiming verified highlights. The same values reproduce headlessly through convert_scad's parameters.

OpenSCAD sources: colours and included files ​

Two things happen for .csg / .scad documents without any action from you. color() becomes Parts: the first time a model that colours at least one solid is opened (and its Parts list is still empty), one Part per distinct colour appears — Part red, Part #0080ff — coloured to match and listing its solids, so Clash, the BOM and per-Part meshing work straight away; the status line says so, and an existing Parts list is never touched. A union of differently coloured children has no single colour and gets no Part (a warning names it), and while edit ops are replayed over the base no Parts are created, since their solid ids would shift. Included files are watched: a .scad that uses or includes another file (or import()s a mesh) reloads when that file changes, and an unchanged model is not re-evaluated by every panel and tool that needs its geometry.

Generating an FE Mesh ​

The FE Mesh panel (below the Edits panel) generates a finite-element mesh (nodes + triangles/tetrahedra) of the currently displayed model using Gmsh compiled to WebAssembly. The result is shown as a blue overlay on top of the existing geometry — it never replaces or modifies the original model. See GMSH Integration for the full technical write-up.

To generate a mesh:

  1. Pick a target element size with the coarser→finer slider (or a Coarse/Medium/Fine preset). The default is derived from the model's bounding box (diagonal / 20), and the readout above the slider shows the current size plus a rough estimate of how many elements it will produce (the preset nearest the current size is highlighted). Fine-grained options (dimension, algorithms, element shape, element order, …) live in the collapsed Advanced settings section.
  2. Click ▶ Generate (the full-width button at the top of the panel; the header shows the element count once a mesh exists). The overlay appears and the panel's status line shows Nodes: N · Elements: M · 3.2 s, or an error message if generation fails. Below the status line, a quality summary reports the minimum and mean element quality (Gmsh's minSICN metric, 0–1, higher is better) plus a small histogram of the distribution — useful for spotting a generate that technically succeeded but produced a lot of sliver elements. An order-2 generate additionally reports an invalid-element count (negative Jacobian determinants — analytically inverted curved elements). If any elements scored below 0.20 (for a 3D mesh), a Worst button appears next to Clear and lights up automatically, highlighting those elements in bright red — visible even where they're buried inside the model, so you don't need to clip or cut away anything to find them.
  3. Click FE Mesh in the toolbar to show/hide the overlay without discarding it; click Worst to show/hide just the worst-element highlight; click Clear in the panel to remove everything.

While a mesh is generating or exporting, the panel shows Cancel beside Generate. It cancels work for the current document/request; another open CAD document's queued or active mesh job is unaffected.

The FE Mesh panel: coarser→finer size slider, presets, per-part sizes.A generated tetrahedral mesh overlaid on the model.

The FE Mesh panel and a generated 3D tetrahedral overlay (Nodes: 2975 · Elements: 12952), colour-scoped by part.

FE Mesh controlAction
Deviation (gap icon beside Generate)Generates with the current options and measures how far the mesh boundary strays from the CAD surface — geometric fidelity, which element quality can't show (a flattened fillet, a bridged gap, a dropped face). The boundary is coloured by each vertex's distance (viridis, 0 → worst), and the status line summarizes the worst deviation, p95, the fraction of the CAD surface within tolerance, the worst CAD face, and any extraneous surface. Set the tolerance under Advanced settings (Deviation tolerance, blank = 1/1000 of the model diagonal). Sampled against the CAD's own fine tessellation — an estimate, not a certified maximum (it can only under-report, most visibly for a thin spike whose tip is the worst point — a 10-unit needle measured about 3% low against an independent implementation). Headless: measure_mesh_deviation
Coarser→finer sliderThe primary control: sets the target element size (Mesh.MeshSizeMax), log-scaled between bbox-diagonal/5 (coarsest) and /200 (finest). The readout shows the size and an estimated element range, computed from the displayed model's real volume and surface area in a model calibrated against real Gmsh runs (±25% on the calibration corpus); hover it for node count, a rough memory range, the confidence (calibrated, rough when the size is coarse relative to the part, uncertain with Part sizing/grading, hex-dominant or fTetWild) and the assumptions. A warning appears when the estimate exceeds ~1M elements, or your Element budget (advisory) from Advanced settings — it warns, never blocks. An open (non-watertight) mesh shows the size only: a volume count can't be estimated for it
Coarse / Medium / FineOne-click presets: element size = bbox diagonal / 10, / 20 (the default), / 50
Saved presets(Sits beside Engine, with Apply · Save… · Delete on the PRESET label's own line.) Named, reusable option bundles (global settings only — Part sizing stays in the document): a picker over your own cad-preview-mesh-presets.json (beside the model, shared by every model in its folder) plus the built-in starters (coarse-preview, balanced, fine-detail, robust-repair). Apply writes the preset's options — sizes converted from its authored unit into mm — as the document's settings (nothing is generated); Save current… records the current options (prompts for a name); Delete removes your own preset (built-ins show no Delete button). Fields the preset's engine ignores are reported, not silently dropped; preset names describe density intent, never a mesh-quality guarantee. The same library is visible to list_mesh_presets / apply_mesh_preset headlessly. A preset may also carry an optional stlExport block (targetCellSize, chordalFraction, angularDeg, in the preset's unit) — the mesh-aware STL tolerance that export_tessellated_stl's preset parameter applies; the three built-in Gmsh starters carry one matching their target size
Part sizesOne size input per defined Part (visible once parts exist) — the only place a Part's target size is edited (shown to three significant figures; hover for the exact value); blank inherits the global size. A Grade toggle beside each part reveals a distance-graded band (Wall size / Far size / Near dist / Far dist): elements stay at the wall size within the near distance of the part's own entities, grow to the far size at the far distance, and stay there beyond it — for meshing toward a wall, unlike the flat size which only refines inside the part. A Struct toggle reveals a divisions input (nodes per meshed edge): a regular region meshes as an exact mapped grid instead of unstructured tets (a 3×4×5 box at 6 divisions is exactly 125 hexes); a non-regular region fails loudly at generate time, never silently unstructured. A Layer toggle (shown only for a part that lists edges, since the layer grows along them) reveals an anisotropic boundary layer (Wall / Ratio / Thickness / Quads) grown along the part's edges on a 2-D mesh: the first row is the wall height and each further row is Ratio times thicker until Thickness is spent, and the row count is shown beside the inputs. After a generate, that line also reports the rows read back from the generated nodes (minimum and median over sampled wall stations, plus wall coverage), and turns into a warning colour when a station fell short of the plan. Other mesh algorithms than Automatic, Delaunay, Frontal-Delaunay and Frontal-Delaunay for Quads are refused with a message, and a 3-D generate ignores the layer with a warning. B-rep sources only
EngineGmsh (default), fTetWild, Octree (hex-dominant), Octree-mesh (graded tets / hex) or Mesh-CAD (B-rep surface). Mesh-CAD triangulates a B-rep's exact faces without a CAD kernel (set Dimension to 2; shared edges make the surface watertight), sized by Size max plus the angle/curvature controls below; Parts and physical groups are not preserved. Octree-mesh fits conforming graded tetrahedra (or an all-hexahedra mesh) to the tessellated skin of any 3D source, sized by a largest leaf edge and a number of levels; Parts and physical groups are not preserved either (see the engine notes). Octree builds a conforming hexahedra + pyramids + tetrahedra volume from the tessellated skin of any 3D source (B-rep or mesh) — sized by its max level, not the size slider; Parts and physical groups are not preserved (the status line says so, with the snap counts and element mix), and Gmsh-only settings are greyed (a quadratic order, a non-simplex shape, periodic pairs or a size field are refused at Generate). fTetWild is an alternative volume mesher for a dirty mesh-format 3D source (holes, self-intersections, non-manifold edges) that Gmsh's own boundary reclassification rejects or silently produces no elements for. Under fTetWild, Size min / 2D-3D algorithm / Element order / Element shape / Optimize / STL angle are all greyed (unused); the size slider still applies, via fTetWild's own envelope/target-edge-length settings under Advanced, plus three optional flags: manifold surface (force a manifold boundary), coarsen (fewer, larger tets), and no interior filter (skip interior/exterior filtering — returns a hull fill, not the part interior; inspection only). Requesting fTetWild for a B-rep source or a non-3D dimension falls back to Gmsh automatically
Advanced settings (collapsed)The raw Gmsh options below — expand to reveal them (shown here). The export row (format · unit · Export) always closes the panel, below this section
Dimension1D (edges only), 2D (surface triangulation), or 3D (volume tetrahedralization)
Size min / maxBounds on generated element size (Mesh.MeshSizeMin/Mesh.MeshSizeMax); Size max is the same value the slider drives, shown numerically (clearing it restores the bbox-derived default)
2D algorithm / 3D algorithmThe Gmsh meshing algorithm to use for each dimension
Element shapeTriangles / Tetrahedra (default), Quads / Hexahedra (recombines the mesh into quadrilaterals in 2D / hexahedra in 3D), or Hex-Dominant (3D) (a mixed tet/hex mesh via Gmsh's RTree recombiner — not exportable to Kratos MDPA, use a different export format)
Element orderLinear (1) or quadratic (2) elements — quadratic adds mid-side nodes (the overlay still draws the corner geometry)
OptimizeDefault (Gmsh's generate-time optimizer), Off, Netgen (better min/mean quality but a denser mesh — and it linearizes quadratic meshes, warned, never silent), or High-order elastic (untangles curved quadratic elements; order 2 only)
ConformalFragment touching solids before meshing so they share interface nodes (B-rep multi-solid only, default on; single-solid output is identical either way; mesh sources ignore it). Parts resolve after the renumbering, so physical groups and Kratos SubModelParts survive per body
STL angle (°)Surface-classification angle for mesh/STL sources (disabled for B-rep documents, which never reclassify) — only used by engine: Gmsh
Octree max levelDeepest octree refinement level (1–6, default 4): the root grid is split this many times near the skin. Higher is finer and much larger. Only used by engine: Octree
Octree snap to skinMove leaf nodes onto the skin (default on). Refused snaps are counted in the status line, never forced. Ignored (and greyed) by the all-hexahedra transition. Only used by engine: Octree
Octree transitionConforming (hex + pyramid + tet) (default) or All hexahedra (hanging nodes, MPCs): the latter leaves the hanging nodes in place and ties each to its parent corners by multi-point constraints, which only the Kratos MDPA export carries (other formats warn that they are lost). It never snaps. After a generate, the links button beside Generate (amber) draws the slave→master segments and the status line counts the hanging nodes and constraints. Only used by engine: Octree
MPC variablesKratos variables the hanging-node constraints tie, comma separated (default DISPLACEMENT_X, DISPLACEMENT_Y, DISPLACEMENT_Z; one Constraints block per variable and master count). Only used by the all-hexahedra transition
Refinement regionsOne geometry-free local size region per line, in mm: sphere cx cy cz radius size [thickness], box x0 y0 z0 x1 y1 z1 size [thickness], cylinder cx cy cz ax ay az radius size (axis = half-length vector), optional trailing # name. Inside a region the mesh is requested at size, growing back to the global size over thickness. Greyed under the octree, mesh-CAD and fTetWild engines, and ignored for mesh sources. A line that does not parse is explained under the field and nothing is saved until it does. See Refinement regions
MDPA ranksKratos MDPA export only (greyed for other formats). 2–64 splits the domain cells over that many MPI ranks and writes <name>_<rank>.mdpa per rank plus <name>.partition.json (global node/cell ids, an InterfaceNodes SubModelPart per rank); empty or 1 writes one file. The save dialog names the stem, and existing sibling files are confirmed before they are replaced. Not Kratos' native pre-partitioned layout — see Partitioned MDPA
MDPA partitionerAuto (KaHIP) (default), KaHIP or Space-filling curve — KaHIP cuts markedly fewer interface nodes at the same balance. Live once the rank count is 2 or more
Octree-mesh outputTetrahedra (graded) (default) or Hexahedra (all-hex). Only used by engine: Octree-mesh
Octree-mesh max size (mm)Largest leaf edge; 0 is automatic (one eighth of the model's bounding-box diagonal). Only used by engine: Octree-mesh
Octree-mesh levelsRefinement levels below the max size (1–4, default 2); the smallest leaf edge is max size / 2^levels, and each level is much larger. Only used by engine: Octree-mesh
Mesh-CAD min angle (°)Refine until every triangle's smallest angle is at least this (0–60); 0 keeps the unrefined mesh. A sharp model corner can make the bound unreachable — the status line then names the faces below it. Only used by engine: Mesh-CAD
Mesh-CAD smoothing passesPasses of guarded smoothing (0–100). Only used by engine: Mesh-CAD
Mesh-CAD curvature deviation (mm)Size each region so a chord sits at most this far from the surface; 0 is off. Only used by engine: Mesh-CAD
Mesh-CAD cells across gapsSize each region so at least this many elements span its local feature size (a wall's thickness); 0 is off. Only used by engine: Mesh-CAD
Mesh-CAD gradingLargest size growth between neighbours, `
fTetWild envelope (eps)fTetWild's envelope size, as a fraction of the model's bounding-box diagonal — smaller stays closer to the input surface (slower). Only used by engine: fTetWild
▶ GenerateRun Gmsh now with the current options and show the result as an overlay
CancelWhile Generate/Export/Remesh is active, cancel only the current document/request's queued or running meshing work
Export format <select>Pick which format 📤 Export writes — Kratos MDPA (Elements + Conditions) (the default), Kratos MDPA (Geometries), Gmsh Mesh (.msh), Gmsh Mesh v2/Legacy (.msh2), Gmsh Geometry (.geo_unrolled), VTK, MED, CGNS, XDMF, I-DEAS Universal (.unv), Abaqus (.inp), Nastran Bulk Data (.bdf), SU2, INRIA Medit (.mesh), STL Mesh, Diffpack (.diff), OFF, VTK XML Unstructured (.vtu), HDF Mesh Format (.hmf), AVS UCD (.avs), COMSOL Mphtxt (.mphtxt), Netgen (.vol), FLAC3D (.f3grid), Well-Known Text (.wkt), Flux (.pf3), GiD Postprocess (.post.msh), libMesh (.xda), Tecplot binary (.plt), FEBio plot (.xplt), INRIA Medit binary (.meshb), MSC Marc post file (.t19), Abaqus results (.fil), Ansys MAPDL results (.rst), LS-DYNA d3plot (.d3plot) or Nastran OP2 results (.op2). Both Kratos MDPA modes preserve named Parts as Kratos SubModelParts and support linear or quadratic tetrahedra/hexahedra/triangles/quadrilaterals. MED/CGNS/XDMF and the trailing meshio-bridged formats (VTU through Nastran OP2 results, GiD Postprocess and libMesh) are all bridged through meshio++ (this Gmsh build has no writer for any of them) — MED preserves named Parts as named MED groups, XDMF also writes a companion .h5 file alongside the .xdmf, and GiD writes its .post.res sibling.
Export unit <select>A real geometric scale applied to the exported file's geometry before Gmsh sees it — mirrors the model Export command's own unit picker (see Units). Defaults to mm (native, no conversion); Size min/max and any per-part mesh size or grading band are automatically rescaled to match, so relative mesh density stays the same regardless of the chosen unit. Only affects 📤 Export — ▶ Generate's overlay always meshes at native mm, since it has no exported file for a unit to matter to.
Handoff manifest checkboxUnder the export row. When ticked, Export also writes <output>.handoff.json beside the saved mesh: the source file's hash and edit-history fingerprint, the effective options, unit and scale, the engine and kernel versions, the hashes of every written file, and how each Part landed in the mesh (physical groups, SubModelPart sizes, and which Parts are empty or resolved to nothing, plus surfaces in no group and overlaps). A status line summarizes the coverage. The check_handoff_manifest MCP tool tells you later whether the manifest still describes the model. Costs one extra meshing pass.
📤 ExportMesh with the current options (at the chosen export unit) and save the result in the format picked above, via a Save dialog (independent of whether ▶ Generate was already clicked — it always (re)generates fresh)
⚠ WorstOnly shown after a 3D generate with at least one element below quality 0.20 (auto-shown then, since it's a warning). Toggles the worst-quality-elements highlight in place, without discarding it
ClearRemove the mesh overlay and the worst-elements highlight (the original model is unaffected either way)
Refinement sweepCollapsed by default. Enter up to 8 mesh sizes in mm (4, 2, 1) and click Run sweep to mesh the current geometry at each size with the panel's other options — the interactive compare_mesh_refinement. A table shows each run's nodes, elements, time and min/mean element quality (a failed size is its own row, with its error); Copy TSV copies the same tab-separated table the MCP tool returns. Tick Write each mesh (.msh) to a folder to be asked for a folder and get one <name>-size-<size>.msh per run. Cancel (greyed out until a sweep is running) stops a long sweep: the runs already finished are kept and shown, the size that was being meshed is simply left out, and the status line says the sweep was cancelled — so a short table is never mistaken for a complete comparison. The note under the table says what the numbers do not show: finer meshes and better element quality do not prove the FE solution has converged — that needs a solver. The panel's own options are never changed.
Mesh opsOnly shown for a meshio++-imported source (VTK/MED/CGNS/…). One operation per Run op… — Clean, Decimate, Smooth, Subdivide, Refine, Agglomerate, Convert cells, the MMG operations (Adapt, Level set), Optimize tets, or — new in meshio++ 18.1 — Untangle, Prepare metric, Morph, Convex hull, Delaunay (x-y), Cylindrical / spherical components, Estimate normals, Reconstruct surface, Geodesic distance, UV parameterization and Laplace-Beltrami eigenmodes — with only that op's parameters shown (a 0 in a size/count row means automatic). Geodesic distance, UVs and eigenmodes are written as point data, so the result opens with Colour by field. Writes a NEW file via a Save dialog (same format as the source); the open document is never modified. The per-step applied/skipped report renders in the section's status line
Mesh ops · Remesh (MMG)…Remesh a linear triangle/tetra mesh. Adapt to field (original FE file, tetrahedra, a scalar point array) refines where that field varies and carries the field over conservatively; Adapt strength sets how hard. Original FE mesh file uses the raw meshio source; Generate with current settings generates fresh from edited geometry first (it does not reuse a displayed overlay). Hausd defaults to 0.005 of the bbox diagonal; Min/Max edge use source units (mm for generated CAD); Gradation defaults to 1.3 in the panel. Writes a new .med and .parts.json, never the source or an existing output. Named cell regions survive; result fields and point/side groups are dropped with warnings. Hex/quad/pyramid/quadratic meshes are refused, not linearised. Existing manually assigned Parts must have named cell-region counterparts; otherwise the remesh is refused before writing. MMG is not a repair or guaranteed quality-improvement step.
Per-part mesh-size inputs mirrored in the FE Mesh panel.The export-format picker: Kratos MDPA, Gmsh, VTK, Abaqus, and more.

Left: per-part local sizing. Right: the mesh export-format picker (Kratos MDPA is the default).

MMG remeshing controls: input mode, Hausd fraction, edge sizes and gradation.

Mesh options are saved automatically to a <model>.mesh.json sidecar next to the CAD file, and an editable <model>.geo Gmsh script is regenerated alongside it on every change — see Mesh Options Sidecar for the format. The .geo file is one-way: hand-edits to it are never read back by the extension — use the FE Mesh panel to change options, not the generated script. Neither file modifies the source CAD file.

B-rep vs mesh sources: for STEP/IGES/BREP files, the model currently shown (including any applied edits) is re-exported to STEP and handed to Gmsh directly. For STL/OBJ/PLY/glTF files, Gmsh has no volume topology to start from, so the displayed triangle soup is first reclassified into surfaces at sharp-angle boundaries (classifySurfaces, default 40°) and rebuilt into a closed volume before a 3D mesh can be generated.

Mass Properties ​

The Mass Properties panel after Compute on bull.stp: volume, area, centre of mass and the moments of inertia about the centroid.

The Mass Properties panel (below the FE Mesh panel) computes volume, surface area, length, center of mass, and moments of inertia for the whole model or a single selected entity.

  1. With nothing selected, click Compute for whole-model properties. To inspect one entity instead, enter Select mode, pick exactly one volume, surface, or edge, then click Compute — selecting more than one entity shows a guidance message instead of a (possibly misleading) combined result.
  2. The panel shows whichever fields apply: a volume/solid gets Volume, Area, Center of mass, and Ixx/Iyy/Izz; a single face gets Area only; a single edge gets Length only.
  3. Tick Show centre of mass in view to drop a ring marker at the computed centre of mass (in the model's own frame, so it follows the up-axis rotation). It is a scene overlay, never pickable, redrawn on every new result and removed by unticking or by any model rebuild. For a mesh selection that is not watertight the point is the area centroid, not a volume centre.

For STEP/IGES/BREP files this runs in the extension host via OpenCascade.js's BRepGProp; for STL/OBJ/PLY/glTF files it's computed entirely in the webview from the displayed triangle mesh (no moments of inertia for mesh sources in this first cut). Volume/Area/Length/Center of mass are labeled and shown in whatever unit the view-controls Units dropdown is set to (see Units above) — switching it live-rescales an already-computed result with no need to click Compute again; Ixx/Iyy/Izz are always shown raw, unaffected by that setting.

Clash ​

The Clash panel (below Mass Properties, B-rep sources only) checks Parts against each other for real volumetric overlap via exact B-rep booleans — the interactive counterpart of the check_interference / check_interference_all MCP tools.

  1. Pick two Parts in the dropdowns and click Check for a pairwise result, or click Check all to test every Part with assigned solids against every other in one call.
  2. Each result row reads A × B → overlap <volume> or no overlap. Pairs the AABB pre-filter decided without a boolean carry an AABB-screened note; genuinely touching Parts (shared face, zero volume) correctly report no overlap. Volumes follow the Units dropdown like Mass Properties, live-rescaled on change.
  3. The optional Clearance field (in mm; empty = overlap only) turns on near-miss reporting: a pair that does not overlap then also shows its exact gap — no overlap · clearance 0.1 mm · within limit when the gap is at or under the value you typed, the gap alone when it is larger, and no overlap · beyond clearance limit for a pair the bounding-box pre-filter proved is farther away than that. Touching Parts report a clearance of 0. The gap follows the Units dropdown; the field itself is always millimetres. A value that is not a number ≥ 0 is refused with a message rather than ignored.

Results are session-only and clear on every model rebuild, since re-tessellation may renumber the ids they name.

B-rep Health ​

The B-rep Health panel after Check on bull.stp: a valid per-BRepCheck verdict, topology counts and the per-solid signed volume.

The B-rep Health panel (Advanced ▸ Analysis, STEP/IGES/BREP sources only) runs OpenCascade's own validity checker (BRepCheck) on the model as currently edited. It reports facts, not a verdict of its own, and repairs nothing.

  1. Click Check. On a large model this takes a few seconds (about 9 s for a 2.3 MB STEP file).
  2. The summary line gives OCCT's whole-shape verdict (valid per BRepCheck or INVALID per BRepCheck), the number of flagged subshapes, the number of open-boundary edges and how many wires they chain into (free boundaries — each closed wire is a hole's rim).
  3. Below it: solid/shell/face/edge counts, one row per solid (shells, how many are open, and the signed volume — negative flags a reversed solid that topology checks can miss), and one row per flagged subshape with the checker's named statuses — for example face-13 — UnorientableShape or shell-0 — NotClosed. Hover a face, edge or solid row to highlight it in the view.

shell-N ids are local to this report; nothing else in CAD Preview names shells. A model that passes is not guaranteed to mesh in Gmsh — the checker and the mesher test different things. The same report is available headless as the check_brep_health MCP tool.

Surface Tools ​

The Surface Tools panel (Advanced ▸ Analysis, STEP/IGES sources only) holds two one-shot operations over the open model's exact faces, both powered by meshio++ and neither touching the CAD file — the interactive counterparts of the project_to_cad and grow_prism_layers MCP tools. They read the file as saved, so pending edits are not baked.

The Surface Tools panel after Grow layers…: the project and prism-layer controls, with the result card listing 756 wedges, the achieved against planned thickness and the least scaled Jacobian.

ActionWhat it does
Project mesh…Pick a surface mesh that carries cad:face (a mesh_cad_surface .msh/.vtk/.vtu), then a new file. The mesh's points are snapped onto this model's NURBS faces (edge points onto the edge curves); Max distance leaves farther points where they are. The result card lists projected / on-edge / too-far / failed / outside-trim counts and the largest move. An untouched mesh_cad_surface skin moves ~0.
Grow layers…Meshes the model's exact surface at Surface size, grows wedge (prism) layers from it (First height, Layers, Growth ratio, Grow inward) and writes a new .vtu/.vtk/.msh of the layers plus the base skin and outer cap — the interior is not meshed. The card lists wedges, shrunk/collapsed points, achieved against planned thickness and the least scaled Jacobian. This is not the Gmsh Part boundary layer (2-D, in the FE Mesh panel); to feed layers into an FE mesh, use the Prism layers group under engine Mesh-CAD in the FE Mesh panel.

Mesh Health ​

The Mesh Health panel (Advanced ▸ Analysis, native STL/OBJ/PLY/glTF sources only) reports whether a triangle mesh can be sewn into a solid — the interactive counterpart of the check_mesh_health MCP tool. It repairs nothing itself.

The Mesh Health panel: a one-component report with free/non-manifold edges, degenerate faces and the required sewing tolerance, with Promote to B-rep… and Fit to CAD… enabled and Repair (robust)… correctly disabled.

  1. Click Check Healability. Each connected component gets one row-group: free/non-manifold edge counts, degenerate face count, the required sewing tolerance (or did not close), healed area/volume delta vs. the raw mesh, plus meshio++'s oriented signals — Inconsistently wound pairs (adjacent triangles wound oppositely, invisible to the edge counts), Inverted faces (shown only when non-zero), and triangle quality.
  2. Tick Auto-decimate (session-only, off by default) to run the check over an mmgs-coarsened copy (~1,000 triangles, bounded by a Hausdorff distance) instead of a mesh above the 50,000-triangle ceiling; the report states the triangle counts, the Hausdorff bound and the volume change actually applied.
  3. Tick Merge facets (session-only, off by default) before Promote to B-rep… to build one planar face per near-coplanar facet instead of one per triangle: a faceted CAD-like mesh promotes in about a second instead of tens of seconds and the STEP has real faces (a cube is 6, not 12). A facet is merged only when it is genuinely flat and bounded by one outer loop plus holes; a curved patch keeps its triangles, so a curved surface is never flattened. Check Healability still reads per triangle.
  4. Promote to B-rep… enables only once a report shows at least one closed component; Repair (robust)… enables only once a report shows at least one component that did not close. Both write a new file via a save dialog and never modify the open document; re-run Check Healability on the result before acting further.
  5. Fit to CAD… shares Promote's closed gate but goes one step further: instead of sewing the triangles themselves, it segments the surface, fits a primitive per patch and replaces them with exact analytic faces (planes/spheres/cylinders/cones/tori, plus fillets/blends/chamfers/threads and freeform B-splines where nothing else fits) — the interactive counterpart of the fit_mesh_to_cad MCP tool, needing the full WASM artifact's OpenCASCADE and a closed, consistently wound skin (the report's Inconsistently wound pairs row is the early warning). Always STEP (no format/unit pickers); the status message reports N faces (… planes, cylinders …), volume ratio, max deviation, and a solid that failed OpenCASCADE's own check is flagged even though it is written.

Region Fit ​

The Region Fit panel (Advanced ▸ Analysis, native STL/OBJ/PLY/glTF sources only) fits a plane / cylinder / sphere to a mesh region — the interactive counterpart of the fit_mesh_region MCP tool. It emits no ops by itself.

  1. Click Pick seed, then click a surface. The panel grows a region outward from the nearest triangle and reports the triangle count (plus capped when it hit the size cap), the simplest candidate, warnings, and one row per fitted plane/cylinder/sphere with its residual.
  2. Save plane stores the fitted plane as a named construction plane (<model>.planes.json); Add cylinder / Add sphere push the corresponding creation op onto the edit history. Buttons enable only when that candidate fitted.

Passages ​

The Passages panel (below Primitives, B-rep sources only) is a pre-meshing check for narrow channels a mesh could fail to resolve even with good element quality. Set cells (how many elements you want across a passage, default 3) and click Analyze. It finds two shapes, from the exact CAD faces:

  • an annular gap between two coaxial cylindrical faces (a rod in a bore) — width is the radial difference, and the faces must overlap along the axis (merely coaxial cylinders at different heights are not a passage);
  • a slot between two parallel flat faces facing each other — width is their distance, and they must genuinely overlap.

Only gaps of empty space count — the two faces of a wall are never reported. Each row shows the width, roughly how many cells the current size puts across it (from the smallest Part size on either face, else the FE Mesh global size) and the size that would give your target; hovering a row highlights both faces. Under-resolved rows (warning colour) offer Apply local size, which creates or updates a Part named Passage face-A/face-B on the two faces with that mesh size — an ordinary Part, so it persists, rebinds, and shows in FE Mesh › Part sizes. These are estimates; generate a mesh to confirm. Headless: analyze_passages, then set_part.

Printability ​

The Printability panel (below Passages; any source with triangles — a B-rep is measured through its fine tessellation, a mesh with its pending edits) reports facts about how a model would print; it never says "printable". Choose a build direction (up), optionally an overhang limit in degrees from vertical (blank uses the process limit, else 45), a bundled process (FDM, SLA, SLS, MJF — starter figures from typical design guides, each naming its source, not standards) and a plate, then click Check:

  • per body: closed or open (free and non-manifold edge counts), volume, and sampled wall thickness (minimum, 5th percentile, median; inward rays from deterministic surface samples — an estimate at that resolution that can miss a feature smaller than the spacing) with a count of samples under the process minimum wall;
  • for the chosen direction: overhang area (faces inclined more than the limit; exactly at the limit is not counted), flat plate-contact area, a support-volume upper bound, build height, footprint and whether it fits the plate; the other five directions list their overhang area;
  • rules it cannot measure (minimum hole size, bridge span, trapped powder or resin, warping, finish) are listed as Not checked, never as passed.

The overhang faces of the chosen direction are coloured in the view (yellow); Clear removes the colouring, and so does an edit. The same report is available headless as the check_printability MCP tool.

Draft ​

The Draft panel (below Printability; any source with triangles) reports facts about draft and undercuts along a pull direction; it never says "pass" or "fail". Pick the pull (the way the tool leaves the part), a min° threshold (default 1°) and, for a mould parted at the silhouette, two-sided. It lists:

  • the range of wall draft and the projected area along the pull;
  • walls with less draft than the threshold, and (one-sided) walls tilted away from the pull — each with its face-N (B-rep) or facet-N id, draft angle and area; hovering a B-rep row highlights the face;
  • undercuts: faces shadowed along the release direction. This is sampled (rays from points just off each wall), so a sliver smaller than the sample spacing can be missed; the sample count is shown.

Faces nearly perpendicular to the pull are parting faces and are not findings. The view is coloured by draft angle (viridis, clamped to ±15°); Clear removes it, and so does an edit. The same report is available headless as the check_draft MCP tool. The Draft edit operation (a different thing) is currently blocked by the kernel — see its row in the operations table.

Primitives ​

The Primitives panel after Recognize on the 3×4×5 block.stp: one solid recognized as a box with a zero fit residual.

The Primitives panel (below Region fit, B-rep sources only) classifies each solid as a box, sphere, cylinder, cone, or torus — the interactive counterpart of the recognize_primitives / decompose_to_primitives MCP tools.

  1. Click Recognize for a read-only per-solid report: face inventory by surface type, the candidate primitive with its key dimensions, and the fit residual (absolute and as a fraction of the solid's size) — or an honest "not a recognized primitive" for anything that doesn't match a signature exactly (e.g. a filleted box), never a guess.
  2. With at least one solid recognized, Apply as edits pushes one parametric creation op per recognized solid (each dimension bound to a named variable) onto the edit history — undoable, removable op-by-op like any hand-applied edit. Export… writes the same ops as a brand-new STEP/IGES/BREP file (format + unit picks, then a save dialog — the source file is untouched). Save macro… stores them as a reusable parameterized macro in the folder's cad-preview-macros.json.

The report is session-only and clears on every model rebuild, since re-tessellation may renumber the solids it names.

Exporting a Model ​

Pick File ▸ Export… (or press Ctrl+E) to convert the open model and save it as a different file. The list of offered target formats depends on the file you opened:

You openedYou can export to
STEP, IGES, or BREPits own format (save in place, see below) plus the other two B-rep formats, plus STL, OBJ, PLY, glTF, and 3MF
STL, OBJ, PLY, glTF, 3MF, Collada, AMF, USD, VRML, or FBXthe other mesh formats only (Collada, AMF, USD, VRML and FBX are never export targets)

The format you opened is never offered as an export target — except as a same-format B-rep pick, which is a save in place, not an export. Picking your own format (STEP→STEP, IGES→IGES, BREP→BREP) asks for an explicit modal confirmation and then writes the edited model back to the open file itself: the op list is baked into the file (via a temp sibling + rename, with a one-deep <model>.bak written on the first save of the session), the sidecar keeps the full history with the baked prefix marked, and replay continues after it. A re-emitted file preserves assembly structure and part names but not entity numbering, authoring metadata, or per-part colour; it is saved at the file's own declared unit. Undo cannot cross the save point. Mesh sources (STL/OBJ/PLY) can also be saved in place this way; glTF and meshio/CAD-text sources cannot (no same-format writer). A save that is interrupted mid-way is recovered on the next open for mesh and B-rep sources — see Known Limitations. Picking any target format opens a second quick-pick asking for an export unit — "Native (mm) — no conversion" is first and pre-highlighted, so pressing Enter immediately exports unchanged; picking cm/m/in/ft instead applies a real geometric scale to the exported file (not just a display change — the model reopens at the new size). This applies to every target, STEP and IGES included — both correctly relabel their own declared length unit to match the converted geometry, not just scale the numbers while leaving the header saying millimetres. Pressing Escape on this step also exports natively rather than cancelling. A native Save dialog then lets you choose the destination — it defaults to the source file's folder with the new extension. glTF export always produces a single binary .glb file (no separate .bin/texture references to manage). 3MF export writes geometry only (one object per solid, no colours or materials); the export unit is applied to the coordinates and written as the file's unit, so the part keeps its real-world size in a slicer.

B-rep targets are converted by OpenCascade.js's own writers, so STEP ↔ IGES ↔ BREP round-trips preserve true CAD geometry, not just a tessellated approximation. Mesh targets are generated from the triangulated geometry already shown in the viewer — there is no way to turn a mesh file (or a tessellated B-rep) back into precise CAD surfaces, which is why mesh sources can't export to STEP/IGES/BREP.

Mesh-aware STL. Exporting a B-rep source to STL adds one more (optional) quick-pick after the unit: As displayed (the viewport's tessellation, the default and what Escape picks) or Mesh-aware…. A downstream volume mesher cannot recover curvature an STL already lost, so mesh-aware asks for the downstream cell size (in the export unit) and a chordal fraction (default 0.1): the tessellation's chordal tolerance is their product, with a 20° angular limit, independent of the viewport. The status line previews the triangle count before the save dialog, and after writing reports the sampled chordal error actually achieved (centroid and edge midpoints of a sample of triangles, measured against the exact CAD face) — an estimate, not a certified maximum. A tolerance that would produce more than two million triangles is refused before anything is written. Planar faces are never over-refined; the physical tolerance is the same whichever unit you write.

Exporting a Silhouette SVG ​

Pick File ▸ Export Silhouette SVG… (or Export Silhouette DXF… for the DXF variant; both also exist as CAD Preview: Export Silhouette … commands) to write a 2D outline drawing of the model as an .svg or .dxf file.

For a drawing that shows what is behind the part, pick File ▸ Export Technical Drawing… instead. That one runs hidden-line removal: it draws interior feature edges as well as the outline, and renders anything occluded as a dashed line — so a through-hole's far rim appears dashed rather than missing. It shares the same view and unit picks, and writes SVG or DXF (where hidden geometry goes on a HIDDEN layer you can toggle in a CAD tool). It is still a single view — treat it as a review or illustration artifact and measure anything you need to be sure of, though any measurement you've pinned (see Measuring) still bakes in as a dimension glyph. This is separate from the Export… flow above — an outline is a drawing, not a 3D model, so it never appears in that quick-pick's target list.

  1. Pick a view. The first entry is Current view — the angle you are currently looking at — followed by Front, Back, Top, Bottom, Left, Right, and Iso. Pressing Escape here cancels the export.
  2. Pick an export unit. The same quick-pick every other export shows, defaulting to native mm; Escape here still exports (at mm), it doesn't cancel.
  3. Choose a destination in the native Save dialog (the menu item you picked decides SVG vs DXF and the default extension).

It's an outline, not a dimensioned 2D technical drawing — there is no hidden-line removal. Back-facing geometry isn't drawn, but neither are interior feature edges that don't lie on a silhouette (a hole seen face-on draws as a circle; the same hole seen edge-on draws nothing). OpenCascade's hidden-line machinery is entirely unavailable in the bundled WASM build, so the outline is derived from triangle adjacency instead — which is also why this works for mesh files, not just B-rep. Use it for review notes, documentation figures, and laser/plotter outlines; use the Measurement tools for any dimension you need to be sure of.

Works for STEP/IGES/BREP (with your edits baked in, from the current tessellation) and STL/OBJ/PLY/glTF (from the raw file — edits are not baked in, since mesh edits can't be replayed outside the viewer). meshio-only sources (VTK/VTU/MED/CGNS/Exodus/XDMF/MDPA/Gmsh Mesh/Abaqus/I-DEAS Universal/SU2/INRIA Medit/Nastran) are rejected.

Exporting a Drawing Sheet ​

Pick File ▸ Export Drawing Sheet… (or CAD Preview: Export Drawing Sheet…) to place several views of the model on one sheet — the ordinary output of a drafting workflow, rather than one file per view.

A settings form opens with every choice on one page:

  • Template — pick a saved or built-in template (iso-a3-first, asme-a3-third, front-fit-1to1) to fill the form; Save as template… stores the current settings (asks for a name) in cad-preview-sheet-templates.json beside the model, shared by every model in that folder and by the export_drawing_sheet MCP tool.
  • Views — any of the named views, in any combination.
  • Projection — first-angle (ISO, default) or third-angle (ASME).
  • Paper — Fit sizes the sheet to the views; A4 through A0 (landscape) use the scale below or, on Auto, the largest ISO 5455 standard scale (50:1 down to 1:1000) that fits, warning rather than silently clipping if nothing fits.
  • Scale — Auto, a standard ratio, or Custom (any ratio like 3:4).
  • Title block — title (default the file name), drawn by, drawing number, revision and material; each field adds a cell only when filled.
  • Sections (optional, JSON) — Named sections: a list of {id, label?, view, origin, normal, flip?}; each names the parent view it is marked on and is drawn as its own SECTION <label> view looking along the plane normal, with the cut loops hatched at 45°. In-place cuts: an object keyed by view name ({"front": {"origin": […], "normal": […]}}); the view draws the clipped half-space itself. A section whose plane misses the model is refused by name instead of drawing an empty view.
  • Hole callouts — centre cross on every hole and one leader callout per hole group (4× Ø5 THRU) in the views looking down the hole axis; B-rep sources only, off by default.
  • Format — SVG or DXF.

Export… then asks for a destination. There is no unit step: a sheet's scale ratio is only meaningful against the model's own millimetres. The form remembers your last settings for the session.

The default four views — Front, Top, Right, Iso — are laid out orthographically: first-angle projection (the default, and the ISO convention) places the top view below the front view and the right-side view to its left; third-angle (ASME) mirrors both. Each view is a technical drawing like the single-view export above — hidden edges dashed (SVG) or on a HIDDEN layer (DXF) — inside a title block naming the model, the scale ratio, the projection method, the date, and the views shown.

A pinned annotation is drawn once, in whichever orthographic view shows its measured line at true length rather than foreshortened — an edge running front-to-back reads true in the Right view and appears nowhere else on the sheet. Works for the same source formats as the single-view exports above.

A section cuts the same tessellation the views already draw — no separate exact-curves pass. Each named section keeps the half-space on one side of its plane (the negative-normal side by default, the positive side with flip), draws the retained model looking along the plane normal with the cut boundary hatched, and marks the cutting plane with arrows and the label (e.g. A—A) in the parent view. SVG carries the cut in a SECTION group per section view (plus a SECTION-MARK group in the parent); DXF puts cut, hatch, arrows and label on a SECTION layer, which joins the reserved layer names so a user layer cannot collide with it. Sections and layer filters cannot be combined in this version.

The MCP server exposes the identical capability as export_drawing_sheet (plus save_sheet_template / list_sheet_templates) — both paths resolve settings through the same code, so identical settings produce identical sheets. See doc/mcp-server.md.

The SVG output is a single self-contained <path> with no external references, at 1 SVG user unit = 1 model unit and a physical size in millimetres — so a drawing exported from a native (mm) model prints 1:1 in any vector tool. The DXF output is minimal model-space ENTITIES: chained collinear outline runs become LWPOLYLINEs and unmatched singletons stay independent LINEs. One caveat: the outline depends on consistent triangle winding, so a mesh with mixed winding (as some exporters and hand-edited files produce) draws spurious interior lines.

Batch export ​

Run CAD Preview: Batch Export… from the Command Palette to export many files at once without opening them: pick the files, a target (STEP, IGES, BREP, a one-view SVG/DXF technical drawing, or an SVG/DXF drawing sheet), a destination folder, and what to do when an output already exists (skip, add a numeric suffix, or overwrite — an input file is never overwritten). Files run one at a time with a cancellable progress notification; a file that fails becomes a failed row and the rest still export. A report panel lists every file with its status, output, how many edits were baked in (B-rep sources bake their whole edit history; a mesh source's drawing bakes its pending edits through the same engine the viewer replays with), and any error or warning. The same batch is available headlessly as the batch_export MCP tool.

Connect an agent ​

VS Code agent mode lists the bundled MCP server automatically. For another client, run CAD Preview: Connect an agent…, choose the client, and paste the copied snippet into its config (see MCP Server).

Preparation report ​

Run CAD Preview: Preparation Report… to write a one-stop summary of a model's preparation facts: report.json plus a self-contained report.html (no scripts, no network — safe to mail or archive). It uses the focused tab (saving its sidecars first), or asks for a file when none is focused, then asks for a destination folder. Sections cover source identity (file hash and edit fingerprint), the effective mesh options, edit replay, mass properties, BOM, hole table, mesh health, narrow passages, the budget estimate beside the actual generated mesh, and — through the generate_prep_report MCP tool — deviation, a handoff-manifest check and snapshots. Every section says whether it ran, and if not, why; nothing is silently left out. Mesh-format sources' inspection facts describe the raw file (their pending edits are not baked into them); the meshing sections bake those edits first.

Comparing Models ​

Run CAD Preview: Compare Models… from the Command Palette to diff two STEP/IGES/BREP, STL, OBJ, PLY, or glTF/GLB files (any combination) solid-by-solid — useful for checking what actually changed between two versions of a model. If a CAD Preview tab is focused when you run the command, its file is used as model A automatically and you're only prompted for B; otherwise you're prompted for both.

A results tab opens beside the editor showing:

  • Matched solids (present in both, paired up by bounding-box-centroid proximity and volume similarity) — each row shows its centre displacement and volume delta, so you can tell a solid that just moved slightly from one that was heavily reshaped, rather than trusting a single "changed" verdict.
  • Removed solids — present only in A.
  • Added solids — present only in B.

This is a display-only report (no 3D view, no merge) — to actually look at both models side by side, open each in its own tab and use VS Code's split editor layout. STEP/IGES/BREP, STL, OBJ, PLY, and glTF/GLB are all supported, in any combination; the meshio-only formats (VTK/VTU/MED/CGNS/Exodus/XDMF/MDPA/OpenFOAM/Gmsh Mesh/Abaqus/I-DEAS Universal/SU2/INRIA Medit/Nastran) are the one remaining exception, since they never expose a triangle array outside their own WASM module. For a STEP/IGES/BREP file, the comparison reflects its currently-applied edits (its .edits.json sidecar, if any); for an STL/OBJ/PLY/glTF file, edits are not baked in (there's no way to replay a mesh edit outside the viewer) — a warning banner says so if the file has pending edits, and the comparison runs against the raw file as-is.

Known Limitations ​

  • No texture support for OBJ. MTL material files are not loaded; a default grey material is applied.
  • 3MF is display-only on the host. A .3mf opens, can be edited with the mesh-legal ops and exported (STL/OBJ/PLY/glTF), and a 3MF export is available from every source. But the host has no triangle parser for it, so Compare Models, Mesh Health, Promote to B-rep, Repair, silhouette/drawing export, headless meshing and headless edit baking refuse it by name (export it to STL/OBJ/PLY/glTF first), and it cannot be saved in place. The file's colours are shown (a Part colour replaces them) and written back on a 3MF export, and the Units dropdown is set from the file's declared unit (the geometry itself is scaled to millimetres on load).
  • Collada (.dae) is display-only on the host and import-only. It opens and can be edited with the mesh-legal ops and exported to STL/OBJ/PLY/glTF/3MF, but there is no Collada writer, and the host has no triangle parser for it, so Compare Models, Mesh Health, Promote to B-rep, Repair, drawings, headless meshing and headless edit baking refuse it by name. Flat material colours are shown (a Part colour replaces them); textures are ignored. The file's <unit> scales the geometry to millimetres and sets the Units dropdown, and the file's up axis is not applied (vertices are shown as authored, like STL/OBJ — use the Up axis setting if it appears lying down).
  • AMF (.amf) is display-only on the host and import-only. Like Collada it opens, can be edited with the mesh-legal ops and exported to STL/OBJ/PLY/glTF/3MF, but there is no AMF writer and the host has no triangle parser for it, so the host-side tools refuse it by name. Plain and zipped AMF both open. Material and mesh colours are shown (a Part colour replaces them); the file's unit already scales the geometry to millimetres and sets the Units dropdown (a micron or unknown unit leaves it on mm).
  • USD (.usd, .usda, .usdc, .usdz) is display-only on the host and import-only. Like AMF it opens (ASCII, binary crate and zipped packages), can be edited with the mesh-legal ops and exported to STL/OBJ/PLY/glTF/3MF, but there is no USD writer and the host has no triangle parser for it, so the host-side tools refuse it by name. The stage's metersPerUnit scales the geometry to millimetres and sets the Units dropdown (an unlisted unit leaves it alone); a Z-up stage stays Z-up. Flat colours (primvars:displayColor, a material's diffuse colour) are shown and a Part colour replaces them; textures, animation and skinning are ignored.
  • VRML (.wrl) is display-only on the host and import-only. Like USD it opens, can be edited with the mesh-legal ops and exported to STL/OBJ/PLY/glTF/3MF, but there is no VRML writer and the host-side tools refuse it by name. VRML declares no unit, so the geometry is shown as authored (millimetres). diffuseColors are shown and a Part colour replaces them; textures, lights and Inline are ignored.
  • FBX (.fbx) is display-only on the host and import-only. Like VRML it opens (ASCII or binary, version 7000+), can be edited with the mesh-legal ops and exported to STL/OBJ/PLY/glTF/3MF, but there is no FBX writer and the host-side tools refuse it by name. The file's UnitScaleFactor scales the geometry to millimetres and sets the Units dropdown (an unlisted unit leaves it alone); a Z-up file stays Z-up. Diffuse colours are shown and a Part colour replaces them; textures, lights, cameras, animation and skinning are ignored.
  • No glTF animations. Animation playback is not implemented — only the first frame (bind pose) is shown.
  • No BRep-embedded geometry in glTF. Only triangulated mesh primitives inside glTF are rendered.
  • Compare Models, mass properties and measurement read the meshio-only formats' boundary surface; Mesh Health and promotion do not. VTK/VTU/MED/CGNS/Exodus/XDMF/MDPA/Gmsh Mesh/Abaqus/I-DEAS Universal/SU2/INRIA Medit/Nastran still never hand their volume cells to JS, but meshio++ does expose the boundary surface it extracts for display. compare_models, get_mass_properties and measure now read that welded triangle surface and mark the result surfaceDerived (volume only when the boundary is watertight; an open surface reports area/bbox with volume: null). Mesh Health, Promote to B-rep and Repair still refuse these formats by name because they need the source's own triangle soup, not a derived boundary (export to STL/OBJ/PLY/glTF first if you need those).
  • Compressed glTF is decoded for geometry only. A .gltf/.glb using KHR_draco_mesh_compression or EXT_meshopt_compression opens in the 3D view and works with Compare Models / Mesh Health / Promote to B-rep / Silhouette SVG; its positions and triangles are decoded, never its colours, normals or UVs. The decoders load only when such a file is opened.
  • Mesh Health and Promote to B-rep cap out at 50,000 triangles. Both build one OCCT face per triangle and sew them, so a larger mesh is refused with an actionable error rather than exhausting the WASM heap. Most likely to come up with glTF, a rendering-oriented format whose real-world files are routinely far larger than hand-authored STL/OBJ/PLY — tick the panel's Auto-decimate checkbox (coarsens to ~1000 triangles first with a bounded surface deviation; the report says so, never silently), or use the Mesh Health panel's Repair (robust)… button (fTetWild-based, no equivalent triangle-count ceiling), if you hit it. Repair (robust)… writes a NEW watertight STL file — tetrahedralize the source with fTetWild, keep the resulting volume mesh's own boundary — enabled once Check Healability shows at least one component that did NOT close; re-running Check Healability / Promote to B-rep on the repaired output then typically succeeds where the original could not.
  • Silhouette SVG/DXF draws an outline only. For hidden lines (occluded edges dashed) use File ▸ Export Technical Drawing…, and for several views on one sheet with a title block use File ▸ Export Drawing Sheet… — see Exporting a Silhouette SVG and Exporting a Drawing Sheet.
  • Large assemblies are slow. STEP/IGES files above ~50 MB may take several seconds to tessellate. Tessellation runs in a forked kernel worker process, one shared by every open document — there is no streaming.
  • One-time WASM startup. The first B-rep file open triggers OpenCascade.js initialization (~300 ms on a typical machine). Subsequent B-rep files open faster because the kernel is memoized.
  • Source CAD file is written only by an explicit save. Ctrl+S (or Save All / auto-save) on a dirty tab bakes the unbaked op tail into the open STEP/IGES/BREP/STL/OBJ/PLY file itself (confirmed until the session's first bake, then on the dirty dot alone; temp-file + rename, one-deep .bak, history watermark — undo cannot cross the save point, and File: Revert File drops back to it). Export to another format writes a new, separate file; picking the source's own format in the Export flow saves in place behind its own modal confirmation. Part definitions are saved to a <model>.parts.json sidecar; pinned measurements to a <model>.annotations.json sidecar; and edit operations to a <model>.edits.json sidecar — the original geometry is otherwise always left untouched. Edits are non-destructive and replayable, and are baked in on save or Export.
  • An interrupted save is repaired on the next open. A save writes the geometry and then advances the edit history's save watermark. If the process stops between those two writes, the file is baked while the history is stale — and reopening would replay the same edit over geometry that already contains it. Every source with a same-format writer — STEP/IGES/BREP and STL/OBJ/PLY — is protected by a <model>.save-journal.json transaction marker: on the next open the save is either completed (the file provably holds the saved geometry) or discarded (the file was never rewritten), and if it matches neither — a half-written file, or something else editing it in the meantime — nothing is guessed and you are asked whether to restore <model>.bak. A recovered B-rep save can leave Part/annotation/layer highlights pointing at pre-save entity ids (re-emission renumbers them); the recovery message says so, and the highlights should be re-checked. See Exporting a Model.

Released under the GPL-3.0-or-later License.