Files
solidcraft-upstream/docs/control-protocol.md
T
bherbruckandClaude Opus 5.5 7318b98b8d Preferences: one Fusion-style dialog from the gear at the top right and Ctrl+, (Cmd+,)
Pages: General (theme Dark/Light/System, UI scale, units of new designs, autosave, display
precision, the agent cursor), Navigation (Fusion, SolidWorks, Inventor, Alias and Tinkercad
mouse presets, reverse zoom, zoom to the cursor, orbit centre, view-change animation on/off and
speed), Keyboard Shortcuts (the old Shortcuts window as a page: search, rebind, conflicts),
Design (default modelling orientation Y up / Z up for #3, and the offer to stand up a Y-up STEP
or IGES file: a quarter turn about X as one undoable Move), Graphics, Sketch, Selection.
Changes apply at once; Cancel or Esc puts back what was there when the dialog opened, Apply
keeps them, OK closes, Restore Defaults resets the page. The theme, ground shadow and agent
cursor settings are now kept between runs. File and Help › Keyboard Shortcuts open the page;
the unused docked Preferences dialog is gone. Control: ui.prefs {open, button, stand_up, …}.
Scenario s45_preferences; tests for Cancel/Apply/defaults and standing a Y-up file up.

Fixes #36

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RjGw4gA5ZvKrU8MQP6sBgT
2026-10-10 05:09:03 +00:00

7.4 KiB

Control protocol

solvecraft --control PORT (or SOLVECRAFT_CONTROL_PORT=PORT) listens on 127.0.0.1:PORT for JSON lines. Each request is one line {"id": …, "method": "…", "params": {…}}; each reply is one line {"id": …, "ok": true, "result": …} or {"id": …, "ok": false, "error": "…"}. Requests are handled on the UI thread between frames; pointer and keyboard input is injected as real egui events, one pointer event per frame.

Engine

Method Params Result
engine.execute command: command id, params: JSON object the command's result
engine.script commands: [{command, params}, …] results, stops at the first error
engine.commands — every command: id, label, tab, panel, icon, shortcut, params doc, enabled
document.inspect measure?: bool parameters, timeline (errors, timings), bodies, sketches, selection

Commands never open dialogs when run this way. solvecraft-cli commands prints the same registry.

UI

Method Params Notes
ui.inspect — UI state, viewport rect, camera, active tool/dialog, hover, renderer, timings, live preview (active, busy, error, ms, bodies it stands in for)
ui.set any UiState field (tab, perspective, showGrid, hiddenBodies, dark, …) dark: false switches to the light theme
ui.view view: front, back, top, bottom, left, right, iso, home, fit; animate (bool, default false: snap)
ui.start command like a toolbar click: starts the sketch tool or dialog for the command
ui.click x, y (screen points), button?: left/right/middle, shift?, ctrl?, double? real pointer input
ui.move x, y
ui.drag x0, y0, x1, y1, button?, shift?, ctrl?, steps?, hold?, hold_end? press, move, release; a left drag on the model is a box selection (left to right: window, right to left: crossing); hold keeps the button down that many frames first (a right press-and-hold opens the marking menu, releasing over a direction picks it), hold_end that many frames at the end (a sketch drag shows its snap)
ui.menu x?, y?, target?, close? with a point: open a context menu there as a right-click would (the viewport's marking menu, or a browser menu with target: {"type": "body", "name"} | {"type": "sketch", "id"} | {"type": "component", "id"}); without: the open menu (radial and items: id, label, shortcut, enabled)
ui.menuPick item: id or label runs an item of the open menu, as clicking it does
ui.rename text?, commit? (default true) finishes the rename box a Rename item opened
ui.selection the selection, the open dialog's inputs, the hovered item, and the active sketch's drawn dimensions (dimensions: id and text centre, dimension_selected, dimension_editing)
ui.worldToScreen points: [[x, y, z]…] world points their screen points
ui.triad screen points of the 3D sketch move triad's handles (x, y, z, plane)
ui.sketchToScreen points: [[x, y]…] in active-sketch coordinates their screen points (null when behind the camera)
ui.dialogInput index what clicking an input's Select chip does: picks go to that input
ui.at world: [x,y,z] | sketch: [x,y] | plane: "XY" | axis: "Z" | dimension: "d1" (its value text) | handle: "arrow" (a manipulator handle, see scenario::publish_handle) the screen point (a scenario step's target; see crates/ui-egui/src/scenario.rs)
ui.handles prefix? the handles drawn last frame, [{name, x, y}]: toolbar buttons and drop-down items toolbar:<command id>, panel labels panel:<tab>:<panel>, browser rows row:<label> and fold arrows fold:<label>, manipulator handles
ui.editFeature feature (id or name) what double-clicking a timeline item does: rolls back to it and opens its dialog filled in (sketches open in sketch mode)
ui.scroll x, y, delta? wheel zoom at the cursor
ui.key key (egui key name, Enter, Escape…), cmd?, shift?
ui.text text typed text
ui.screenshot path? PNG of the window (needs a presented frame)
ui.render path, width?, height? CPU render of the model with the current camera (no window needed)
ui.confirm accept?: bool answers the Delete confirmation (shown when a delete takes other features with it or makes some fail); without accept, what it lists
ui.documents action?: new, switch, close, quit; index?; force? (close without asking); answer?: save, dont_save, cancel (answers "save changes?") the open designs (tabs): names, unsaved flags, the active one, a pending "save changes?" prompt, whether a quit is asking
ui.home open?: bool; sample?: index the start page: show or hide it, open a built-in sample (once built); returns open and the recent designs
ui.shortcut command, key?, replace? a command's key, or rebinds it ("" clears; a key another command uses is refused unless replace)
ui.help item?: about, shortcuts (read only), report (copies diagnostics) the version, commit, build date and diagnostic text
ui.prefs any of default_units, nav (fusion, solidworks, inventor, alias, tinkercad), zoom_reverse, zoom_to_cursor, orbit_cursor, look_at_anim, look_at_speed, msaa, length_decimals, angle_decimals, theme (dark, light, system), ui_scale, up_axis (z, y), offer_stand_up; open (a page: General, Navigation, Keyboard Shortcuts, Design, Graphics, Sketch, Selection) opens the Preferences dialog there; button: ok, cancel, apply, defaults; stand_up: true stands up a just-opened Y-up file sets preferences (kept between runs), returns them all plus dialog (the open page or null), dark and stand_up_offer
ui.capture action?: capture, revert, cancel answers the Capture Position question (asked when a command starts or the design is saved while components were moved without capturing); without action, what waits
ui.window action: minimize, maximize, restore, toggle, close what the title bar's caption buttons do (on Windows and Linux the application bar is the title bar); close asks about unsaved designs first
ui.agent_cursor show (bool), speed: instant, normal, slow, follow_camera (bool); all optional the settings. While the cursor is shown, engine.execute first glides to the command's toolbar button and to each pick, then runs and replies; later requests wait their turn. "animate": false on a call skips it
ui.resize width, height
app.quit force? (quit without asking about unsaved designs) quitting (the window closes), asking (a "save changes?" prompt is open)

MCP

solvecraft-cli mcp --connect 127.0.0.1:PORT bridges an MCP client (an AI agent) to this channel; see mcp.md.

Example

{"id":1,"method":"engine.execute","params":{"command":"sketch.create","params":{"plane":"XY"}}}
{"id":2,"method":"engine.execute","params":{"command":"sketch.rectangle.two_point","params":{"p0":[0,0],"p1":[40,30]}}}
{"id":3,"method":"engine.execute","params":{"command":"solid.extrude","params":{"distance":20}}}
{"id":4,"method":"engine.execute","params":{"command":"solid.fillet","params":{"edges":[[0,0,10]],"radius":3}}}
{"id":5,"method":"ui.screenshot","params":{"path":"/tmp/part.png"}}