* Add Open in Slicer for the selected bodies (#2595) One command saves the selected bodies as a 3MF in millimetres (an STL if the 3MF can not be written) and opens it in the last slicer used. It is on the File menu, the Mesh tab and the body right-click menu. The first time, or when that slicer is gone, it asks which one with the same list Save as mesh uses. The Save as mesh dialog is unchanged. * Send slicer files to a private per-user folder (#2595) Files sent to a slicer used to go to a shared folder in the temp directory. They now go to the user cache folder (mode 0700 on Unix), under a name that stays the same for the same design and bodies, so sending again replaces the file and the slicer can reload it. The file is written to a temporary name and renamed into place. If the name can not be replaced, as on Windows while the slicer holds the file open, the copy gets a number and the status line says so. Files older than seven days are deleted at startup. The first send tells the user that sending again replaces the open file, and so does the tooltip of Open in Slicer. Send to slicer in Save as mesh uses the same path. * Open files in a running slicer (#2595) PrusaSlicer, SuperSlicer, Bambu Studio and Cura are started with --single-instance, so a slicer that is already open loads the file instead of opening a second window. OrcaSlicer has no such flag and is left alone, and macOS bundles are single-instance already. PrusaSlicer 3.0 ships a launcher entry that already carries --single-instance-on-url; it keeps that one and never gets a second flag. It is a preference, on by default, and scripts can pass single_instance. Preferences also gets a button to forget the slicer Open in Slicer remembers. * List slicers the desktop entries say open 3MF or STL (#2595) On Linux the scan of desktop entries now also lists a program that claims 3MF or STL in MimeType, even when it is not in our table of known slicers. The table still supplies names, ids, icons and flags for the ones it knows. Entries marked NoDisplay or Hidden are skipped, and so are terminal programs and SolidCraft itself. The existing size and count limits stay. The Windows scan now looks inside a Prusa3D folder, where PrusaSlicer installs itself. * Only start slicers the user can see or chose (#2595) file.save_mesh took a slicer parameter that could be any executable path, so a script or an MCP client could run any program. The slicer now has to be one of the detected slicers (by id or name) or a program the user already added under Custom, which the app hands to the engine from its settings. The same rule holds for file.open_in_slicer. The agent chat already asked before any file command; its question now also names the slicer. * Review fixes for Open in Slicer (#2595) The file name hash keeps all 64 bits instead of 32. When Open in Slicer has to fall back to an STL, the status line now says so. The engine test that sets the chosen programs clears them again. The unused "could not make the folder" message is gone from the catalogs, and the docs say plainly that finding slicers by file type is Linux only. * Keep unsaved designs apart and hide Open in Slicer in the browser (#2595) Two unsaved designs with the same name and body names got the same file in the slicer folder. Every document now has a uid that is new for each New, Open and Recover and stays the same for the life of the document, and the file name uses it (with the process id) until the design has a path. At the first Save As the name changes to the path-based one, so the slicer gets one new file; the old one goes with the seven-day sweep. The browser can not start a program, so Open in Slicer is now disabled there: the engine command reports why, the File menu item and the body and component right-click items are greyed, and starting it anyway says so on the status line instead of opening a dialog. The Save as mesh dialog keeps its browser guard for the open flow too. * Put the Session doc comment back on Session (#2595) The new NEXT_UID static had landed between the doc comment and the struct it describes. * List every install of a slicer, and test the first Save As (#2595) Detection kept one entry per known slicer, so a system PrusaSlicer 2.9 and a Flatpak 3.0 beta showed up as one. Every distinct install is listed now. The same program found twice, through a desktop entry and through PATH, is still one. When a slicer has several installs, each gets an id made from where it was found (prusa#comprusa3dprusaslicer) that stays the same on every run, and a name worked out from its command and location without running anything: PrusaSlicer (system), PrusaSlicer (Flatpak, beta), PrusaSlicer (AppImage). A lone install keeps its plain id and name. The plain id or name still picks the first install, so a remembered "prusa" keeps working. Single-instance flags and the other per-slicer behaviour go by the id without the install part. Also a test that the first Save As changes the file name once and later sends keep the name that comes from the saved path. * Start PrusaSlicer 3.0 first and send the file once it is ready (#2595) PrusaSlicer 3.0 starts through slic3r-app-launcher, which ignores a file given at a cold start whatever the flag, and loads it over D-Bus when an instance is already running. A small table in slicers.rs names such slicers by their command; today it holds that launcher, so PrusaSlicer 2.9 and OrcaSlicer are untouched. Ready means a name starting with com.prusa3d.prusaslicer.BroadcastListener. is on the session bus, read from busctl --user list with no new dependency. If busctl is missing or fails, nobody waits and the file is sent as before. When the slicer is not ready, Open in Slicer starts it without the file and looks every 200 ms for up to 10 seconds. The wait runs on another thread, so the window stays live, and the status line says Starting PrusaSlicer. Once the name appears the file is sent with the same arguments as before. If the limit passes, the status line says the slicer did not respond, that the file is already saved, and to send again. A second click during the wait only repeats the Starting line, so there is one launch and one send. Scripts calling file.open_in_slicer wait inside the command by default, or pass wait: return or no. * Always show the slicer picker for Open in Slicer (#2595) File > Open in Slicer, the Mesh tab entry and the right-click entry now always open the picker, with the slicer used last time pre-selected, so the usual case is one click or Enter. Scripts and file.open_in_slicer with a slicer still send without a dialog. The preference button that forgot the remembered slicer is gone: it could no longer make anything ask again, and it did not clear the pre-selection either. The guide says how it works now. * Leave Bambu Studio out of the single-instance list (#2595) Open files in a running slicer now adds --single-instance for PrusaSlicer, SuperSlicer and Cura only. Bambu Studio is started as it is, so it may open a second window. A test pins that, for the detected slicer and for one the user picked as a custom program. The guide and the preference tooltip no longer name Bambu Studio as getting the flag. * Sort the English catalog and leave features.md to the generator (#2595) cargo xtask lists wanted one message moved in en.json after the rebase. docs/features.md is written by the Generated files workflow on main, so the row I had added by hand is gone from this branch; the workflow adds Open in Slicer when it lands. * Drop the unused folder message from the Vietnamese catalog (#2595) Vietnamese arrived on main after I removed ui.save_mesh_temp_failed from the other catalogs, so the check that every translated id exists in English failed on it. Nothing uses the message any more. * Say so when a started slicer can not be found again (#2595) After Open in Slicer had started a slicer on its own, a failure to look it up again to send the file just returned, so the status line stayed on Starting and then nothing. It now says the slicer was started, that it can not be found to send the file, and where the file is saved. A test covers it. * Say why a failed busctl ends the wait (#2595) If busctl fails partway through a wait, the wait stops and the file is sent, the same as when busctl can not be run at all. That is on purpose: a file sent too early is dropped quietly and costs one more send. A comment in wait_for_ready says so. * Keep the readiness wait inside its limit (#2595) wait_until asked the probe before checking the deadline, including right after a sleep, so a yes could be accepted after the limit, and busctl ran with no timeout, so one look could outlast the whole wait. The deadline is checked first now, the probe is told how much time is left, and busctl is given at most two seconds (less near the end of a wait) and is killed after that. A hung busctl counts as not telling, which sends the file as before. This covers the first look on the UI thread too. Tests cover a slow probe, the shrinking time left, and a fake busctl that hangs, answers and fails. * Send the file that was already written once the slicer is ready (#2595) When the wait for a started slicer ended, the UI ran file.open_in_slicer a second time. That exported the design again on the UI thread, from whatever the session looked like by then, and could write a numbered copy while the timeout message still named the first file. The wait now keeps the slicer it found and the answer of the first call, and opens the file that was written. A test changes the design during the wait and shows the same file is sent, is not written again, no command runs, and no second file appears. * Send with the single-instance flag after starting a slicer (#2595) After Open in Slicer has started a launcher that needs a start first, the file has to reach the instance that is now running, so the send that follows a start carries --single-instance whatever the preference says. A normal send, to a slicer that was already up, still follows the preference. A comment in both places says why. The test uses a plain launcher command with the preference off: the normal send has no flag and the send after a start has it. * Find the remembered slicer again after an uninstall (#2595) With two installs the remembered key is prusa#prusaslicer. Remove the other install and the lone one is plain prusa, which matched neither the exact key nor the plain-key fallback, so the picker fell back to the first slicer in the list, maybe a different one. The key is now also matched by the part before the #, so the same slicer is pre-selected. A test covers the lone install, a plain key with two installs, an exact key, and that another slicer is never picked. * Say a second click did not send anything while a slicer starts (#2595) While a started slicer is being waited for, another click showed Starting <the first slicer> even when it had picked another slicer or other bodies. It now says it is still waiting for that slicer and that nothing else was sent. The test checks the wording and that it names the slicer being waited for. * Label macOS installs by their application (#2595) An open -a command resolved its program to /usr/bin/open, so every application on a Mac was labelled system. Labels for open commands now come from the .app path: /Applications is system and a folder under home is shown as ~/Applications. A test with two PrusaSlicer.app bundles and a made-up home checks both labels and that the ids differ. * Pick the same place for a program found twice every time (#2595) When two desktop files, or a desktop file and PATH, pointed at one program, the first one read was kept and its file name went into the id, so the ids could change with the order the folders were read in even though nothing was installed or removed. The place that sorts first now speaks for the program. A test with two aliases and a second install lists them with the folders in both orders and gets the same ids and commands. * Treat flatpak run spelled two ways as one install (#2595) flatpak run ... and /path/to/flatpak run ... with the same arguments had different install keys, so one invocation could be listed twice. The launcher is now compared by its file name and the arguments are kept, so both give the same key, while a different branch is still its own install. The test uses a real launcher file so the full-path entry is not skipped as stale. * Say plainly that a late yes is accepted on purpose (#2595) The comment on wait_until said a slow probe could not accept a yes that came too late. That was not what the code does: no probe is started after the limit, but one already running may finish and its yes is taken, because the file should still go. The comment now says so, with the bound: the limit plus that last probe, which in production is given the time left and so ends by the limit. A test has a probe that runs past the limit and says yes, and checks it is accepted and the wait stays within limit plus the probe time, and that a probe that says no is not followed by another. * Give reading busctl the same deadline as running it (#2595) After busctl exited, the output was read with a join that could wait forever if a process it left behind kept the pipe open. The reader now hands the text back over a channel and the wait uses the time that is left of the same limit, and gives up with nothing otherwise. The polling sleep no longer overshoots the time left either. A test uses a fake busctl that backgrounds a sleep with its output inherited and exits at once: the call returns within its limit. * Tell two different flatpak launchers apart (#2595) Comparing only the file name of the launcher made /opt/tool-a/flatpak run ... and /usr/bin/flatpak run ... with the same arguments one install even when they are different programs. The launcher is now found the way any program is, through PATH and with links followed, and its name stands in only where it can not be found. The test has flatpak on PATH and the same file by its path as one install, a different executable of the same name as a second, a different branch as a third, and the fallback when there is no PATH. * Keep waiting when busctl is slow, send only when it is missing or fails (#2595) Since the busctl timeout, a slow busctl came back as "can not tell", and that was read as send now. One slow look, likely just when a cold PrusaSlicer 3.0 is starting under load, ended the wait and sent the file early, which gets dropped. The look now says why it has nothing: slow means not up yet, so the wait goes on to its limit and then reports that the slicer did not respond; a missing or failing busctl still means it can not be told and the file is sent. The comments name only those two cases. Tests use fake busctl programs: one that hangs keeps the wait going to the limit and times out, a missing one sends at once, one that says not up and then fails sends, and one that names the slicer is ready. * Do not open an empty path when the answer had no file (#2595) If the answer kept for a started slicer had no path, the send ran open with an empty string. The tick now says there is no file to send and starts nothing. The test gives a wait with no path, checks the error line, and that the fake slicer was never run. * Drop the unused folder message from the Danish catalog (#2595) Danish arrived on main with ui.save_mesh_temp_failed, which this branch removed from English because nothing uses it any more, so the check that every translated id exists in English failed. The entry is gone from the Danish catalog too. * Always start the 3.0 launcher with the single-instance flag (#2595) A hand run showed that starting the PrusaSlicer 3.0 launcher with no file while a 3.0 is already running forwards to it and exits when the flag is there, but opens a second full window when it is not. A slow first look at the bus can start the launcher twice, and with the preference off the flag was stripped. A slicer that has to be started first now keeps the flag on that start whatever the preference says, the same reasoning as the send after a start; when nothing runs the flag does no harm. A normal send with the preference off still has none. The test covers the launcher entry, a bare launcher command, a normal send and a slicer that is not on the table. * Swap --single-instance-on-url where it does not hand the file over (#2595) A hand run on the PrusaSlicer 2.9.6 Flatpak showed that its entry's --single-instance-on-url does not forward a file to the running instance: every send opened a new one, three after a cold start and two sends. With plain --single-instance the second process finds the first, forwards, and exits. So when a command carries --single-instance-on-url and the slicer is not on the wait table, that is, not the 3.0 launcher, it is replaced by --single-instance, with the file markers handled as before, when the preference is on, and stripped when it is off. On the 3.0 launcher the flag does forward and stays. The comment on the rule says why. A test uses the real 2.9.6 and 3.0 Flatpak command lines. * Cut long slicer names to fit the picker (#2595) With installs listed on their own, a name like PrusaSlicer (Flatpak, master) is wider than its row. The list painted it with no limit, so it ran out of the popup, and the closed selector wrapped it onto a second line. The list now lays the name out on one line that ends in an ellipsis when it does not fit, the selector truncates, and the full name is in the tooltip when it was cut (for a program the user added, ahead of the delete hint). A test checks that a long name is cut to one line within the width and a short one is left alone. * Keep the 3.0 launcher's flag on a send whatever the preference says (#2595) A hand run with the single-instance preference off showed the 3.0 launcher needs its flag on a send too, not only on a start: a cold start with a file and no flag loaded nothing, and a send to a running 3.0 left a launcher process that never forwarded. A slicer on the wait table now gets the flag on every launch, as it already did when started alone. The doc comment says why. The preference text, in English, Japanese and Chinese, and the guide now say that PrusaSlicer 3.0 is the exception to turning this off. The tests that took the flag away with the preference off now expect it, for the launcher entry, a bare launcher command and the 3.0 Flatpak line; the test of the send in the UI checks the first send carries it. * Keep the install-flag swap to PrusaSlicer, found by what it is (#2595) The swap of --single-instance-on-url for --single-instance ran for any program whose command had the flag, so another program with that flag would have been rewritten. It is now a PrusaSlicer rule: it applies to a slicer recognised as PrusaSlicer by its identity, its id or, for a program the user picked, the name it goes by, never by a version number. The single-instance table uses the same identity check. A test keeps the flag of a program that is not PrusaSlicer, strips it when the preference is off, and swaps it for a picked program named PrusaSlicer. * Do not promise a hard bound in the wait comment (#2595) The comment on wait_until said the production probe ends by the limit. Its clock starts after the process is spawned, and killing and reaping the process and scheduling are not bounded by it, so it is a polling and read budget with process overhead on top. The comment says that. Comment only. * Tell PrusaSlicer 2 from 3 by the installed version (#2595) PrusaSlicer 2 and 3 need different handling, and the rules had to guess from the command line. Each install of PrusaSlicer now carries its major version, read when it is detected from the release at the top of its appstream metadata and never by running the program: a Flatpak's own files (the branch the command names, else the current one), or the metainfo file beside a desktop entry. PrusaSlicer older than 3 is sent the file with --single-instance in place of --single-instance-on-url, which does not hand a file over there, and is not waited for. PrusaSlicer 3 and newer keeps the launcher's flag, is started on its own, waited for, and sent the file. When the version can not be read, the command decides as before: the slic3r-app-launcher launcher counts as 3, and any other entry is left as it is. A program that is not PrusaSlicer is untouched whatever its flags. Tests use metadata fixtures, no Flatpak: 2.9.6, 3.0.0-alpha10, a branch that is not installed, a system package, an unreadable version with and without the launcher, and a non-PrusaSlicer program with the same flag. * Make the busctl wait tests steady under load (#2595) One of them failed once in a full run at its first assertion and passed alone. A script that has only just been written can fail to start (text file busy) while another test forks, and that reads as a busctl that is missing. The fake programs are now started once, patiently, with a flag they answer at once, before a test relies on them; a hanging one uses exec so it is killed cleanly. The time bounds that could trip on a busy machine are several seconds now, and the checks are on outcomes: not ready and keeps waiting, send now, and the number of looks. Thirty runs alone, thirty of the whole slicers filter and thirty of the busctl tests under eight busy loops all pass. * Say what the busctl reader thread leaves behind (#2595) The thread that reads busctl output is left to end when the pipe closes. A busctl that answered but left a process holding its stdout reads as slow, and every poll of such a bus can leave one blocked reader behind until that process lets go. A comment next to the thread says so. Comment only. * Make Save as mesh a plain export (#2595) Save as mesh asked for the bodies and the format, and could also send the file to a slicer with a checkbox and a slicer list. Sending now has its own command, so the checkbox, the list and the Send label are gone from this dialog: bodies, format, a file dialog, Save. The two settings that only the checkbox used, save_mesh_send and save_mesh_slicer, are no longer kept; a settings file that still has them loads, and they are ignored. The strings only they used are gone from the catalogs. The slicer list stays in the dialog of Open in Slicer. file.save_mesh keeps its slicer parameter for scripts. The guide describes the two as separate. * Rename Open in Slicer to Send to Slicer (#2595) The command sends a file to a slicer rather than opening one, and a later Send to App can share the name. File menu, Mesh tab, right-click menus, the dialog button, status lines, the guide and the English, Japanese and Chinese strings now say Send to Slicer, and the command id file.open_in_slicer is file.send_to_slicer. It is new in this change, so there is no alias. The remembered slicer is stored as send_slicer, and the question the agent chat asks says send to. Module and type names stay as they are. * Put the send targets in one table and one send pipeline (#2595) The known slicers, the list of slicers with a single-instance flag and the rule for slicers that must be started and waited for were three separate things. They are one table of targets now: id, label, kind, names and program files, accepted formats and the default, how the file is handed over, the single-instance flag, the version from which PrusaSlicer's on-url flag hands a file over, and the wait-for-ready rule. Programs that are not in the table are slicers with the same defaults. The kind is slicer, the only one so far; its comment says it is where a later Send to App would plug in. The send itself is one function: write the bodies, find the target, start it and wait if its rule says so, hand the file over, report. Detection and resolution take a kind. Nothing changes for what already worked. file.save_mesh keeps its slicer parameter, now through the same function, with its export arguments and result unchanged: a cold PrusaSlicer 3 is started and waited for before the file goes, and it accepts the program path of a detected slicer as file.slicers lists it (file.slicers gives it as program). A path is accepted only when it is the program of exactly one detected target, and that target's own command and arguments run; a launcher several entries share, such as /usr/bin/flatpak, is refused as ambiguous, and any path nobody listed or chose is refused. The parameter and the code say it may be deprecated later in favour of file.send_to_slicer or a Send to App workflow, which is a possibility and not a plan. Tests go through file.save_mesh itself for the two reported failures, a listed AppImage path and a cold PrusaSlicer 3 that is waited for, with the slicers and the busctl made up; and they check that the result and the file are what an export without a slicer gives, and that a path that is not listed, or is shared by two entries, writes nothing. The pipeline has its own tests: a cold launcher waited for, one already up, wait return, and one that never comes up. A test of save_mesh that shared a file name with another is given its own. * Keep an AppImage's id through an upgrade (#2595) When a slicer has several installs, each gets an id made from where it was found. For an AppImage that was its file name, which carries the version, so upgrading it changed the id and the remembered choice missed. The id is now made of the slicer's id, the fact that it is an AppImage and a short hash of its folder, so a new version that replaces the old one in the same folder keeps the id, and the same AppImage in another folder is its own install. Two versions kept side by side in one folder are two installs: while both are there, their file names are added to their ids to tell them apart. A remembered id is found again only when it names exactly one install. An id that was one AppImage and is now two versions, or a plain id that is now several installs, is treated as not remembered, so the picker starts from the first entry as on a first run and does not quietly pick another install; a single install left of the same slicer is still found, as before. Installs that are not AppImages are told apart as before. An id from before this change no longer matches. Tests cover an upgrade, two versions side by side, a second folder, and the picker's side of each case. * Add Open with EasyPrint to Send to Slicer EasyPrint is Printables' slicer in the browser. It's a plain text row in the Send to Slicer dialog, backed by a new file.open_in_easyprint command. The command exports the bodies as a 3MF (or STL), hands them to the host to upload to slicer.printables.com/import, and the app opens the link that comes back. The desktop app uploads with ureq and the browser build with fetch, so this is the one send target that works on the web. The model leaves the machine, so the command refuses without consent: true, the dialog asks once and remembers, and an AI agent's request asks first. A model over 100 MB is refused before anything is sent, and a failed upload shows as an error with the reason. The chat's privacy note said SolidCraft sends nothing anywhere, which isn't true any more, so it now says that about the chat. New strings are in every catalog. * Place Send to Slicer on the Insert Files panel
17 KiB
Import and export
| Format | Read | Write |
|---|---|---|
.solidcraft (versioned container or JSON design) |
yes | yes |
.json (self-contained plain JSON design) |
yes | yes |
| STEP (AP203, AP214, AP242) | solids, assemblies, units, names, colours | AP242 with names, colours and the component tree |
| IGES 5.3 | manifold solids, or trimmed surfaces sewn into solids | manifold solids with names and colours |
FreeCAD .FCStd, Rhino .3dm |
solids, through cadmpeg | |
| 3MF, STL, OBJ | mesh bodies | yes |
glTF 2.0 (.glb, .gltf with its .bin files) |
mesh bodies in the node tree's components, materials as appearances | |
| 3MF with face tags | as 3MF | each triangle tagged with its B-rep face, exact coordinates |
| URDF | the assembly as a robot: links with meshes, mass and inertia, joints from the assembly's joints | |
| DXF, SVG | into a sketch | sketches and flat patterns as DXF |
| PNG, JPEG | canvases and decals |
Opening a STEP, IGES, FreeCAD or Rhino file creates a design with the file's bodies as a base feature, and inserting one adds them to the current design. Later features build on them. Mesh bodies render, measure, move and export, but solid features need B-rep bodies.
Every imported file (STEP, IGES, 3MF, STL, OBJ or glTF) comes in as a new component named after the file, so its bodies never mix with yours. A STEP assembly keeps its structure: its parts and sub-assemblies become nested components, and a part used several times is one component with an occurrence for each place. The imported assembly is rigid: moving its component moves everything, and its parts don't move on their own. Right-click the component and choose Unlock Contents to joint the parts one by one. Undo removes a whole import in one step.
A glTF file (.glb, or .gltf with the .bin files it names in the same folder) comes in the
same way: its nodes become nested components, a mesh placed several times is one component with
an occurrence for each place, and the whole import is rigid until you unlock it. glTF is in metres
with Y up, so the import is scaled to millimetres and turned to stand Z up (up: "z" keeps the
file's axes). Each material's base colour, metal and roughness become the body's appearance, so
the model looks right in Render; textures, skins and animations are not imported. Scaled or
mirrored nodes are baked into their meshes. Compressed meshes (Draco, meshopt) are refused with a
message: save the file uncompressed first.
A 3MF project saved by a slicer comes in too, also when it keeps its objects in model parts of
their own inside the file (the 3MF Production extension): each object on the build plates becomes
one mesh body, with all its volumes merged where the slicer placed them. Each body is named after
its object: the name in the model, else the one the slicer keeps in its project settings (Bambu
Studio's Metadata/model_settings.config), else Mesh1, Mesh2, … Other slicer-only data
(modifier, negative and support volumes, painting, per-object settings) is not read: modifier and
negative volumes come in as ordinary solid geometry.
File → Export STL… and File → Export 3MF… (also Save As Mesh on the Insert tab's Files panel, and in the right-click menu of a body) open the same dialog with that format chosen; it saves the selected bodies as STL, 3MF or 3MF with face tags (below) to a file you name, and you can change the format there.
File → Send to Slicer (also on the Insert tab's Files panel, and in the right-click menu of a
body) is the short way: it saves the selected bodies as a 3MF in millimetres, without face tags,
and sends it to a slicer. It always asks which one and pre-selects the slicer you used last time,
so the usual case is one click or Enter. The list shows the slicers found on the computer with their
icons; Custom… adds any other program to the top of the list (kept between runs; hover an entry
you added and press Delete to remove it). On Linux only, a program whose desktop entry says it opens
3MF or STL files is listed even if SolidCraft doesn't know it by name. Windows and macOS list the
known slicers only, found in the usual program folders and on PATH. It falls back to an STL if the
3MF can't be written. Scripts and the control channel use file.send_to_slicer
with a slicer, which sends without a dialog. They can start only the slicers in the
list and programs you added under Custom…; any other program path is refused, and an AI agent's
request to save a mesh or send it to a slicer asks first and names the slicer.
The list also has a plain EasyPrint (Printables, in the browser) row, and it is the one entry
that works in the browser build too. EasyPrint is Printables' slicer, so choosing it uploads the
selected bodies (a 3MF, 100 MB at most) to Printables' servers and opens the model there in your
browser. The link expires after 30 minutes, and you need to be signed in to Printables. SolidCraft
asks once before the first upload and remembers that you agreed. Nothing is uploaded unless you pick
this row. SolidCraft isn't affiliated with Printables. Scripts use file.open_in_easyprint, which refuses unless it gets consent: true,
and an AI agent's request to use it asks first.
Files sent to a slicer go to a folder only you can
read: ~/.cache/solidcraft/slicer on Linux, ~/Library/Caches/SolidCraft/slicer on macOS and
%LOCALAPPDATA%\SolidCraft\slicer on Windows. The name stays the same for the same design and
the same bodies, so sending again replaces the file the slicer has open: use the slicer's
Reload from disk to pick it up. If Windows won't let the file be replaced because the slicer holds
it, the new copy gets a number (name-2.3mf) and the status line says so. Files older than seven days are
deleted the next time SolidCraft starts.
If the slicer is already running, PrusaSlicer, SuperSlicer and Cura load the file in
the open window instead of starting another one (they are started with --single-instance).
Other slicers, Bambu Studio and OrcaSlicer among them, are started as they are, so they may open a
second window. You can turn this off in
Preferences → General, except for PrusaSlicer 3.0, whose launcher can't hand a file to a running
window without it. Scripts pass single_instance: false to file.send_to_slicer or file.save_mesh.
PrusaSlicer 2 and 3 behave differently, so SolidCraft reads which one an install is from the
version in its installed metadata (a Flatpak's own files, or the metainfo file of a package); it
never runs the program to ask. PrusaSlicer 2 is sent the file with --single-instance. PrusaSlicer 3
is started on its own first, SolidCraft waits up to ten seconds for it to be ready, and then sends the
file. If the version can't be read, the command decides: the slic3r-app-launcher launcher is
treated as PrusaSlicer 3, and any other entry is left as it is.
Closed mechanical meshes with a constant cross-section can be rebuilt as solids with the
mesh.convert command (command palette, scripts and the control channel).
File → Export 3MF… with the format changed to 3MF with face tags (file.export or file.save_mesh with format: "3mf-tagged") writes an
ordinary 3MF in which every triangle also names the B-rep face it lies on and every coordinate
is the tessellation's own double-precision value. Meshing and analysis tools can use the tags to
keep a model's faces (for loads, boundary conditions or feature edges); other 3MF readers ignore
them. The format is specified in docs/formats/3mf-face-tags.md.
File → Export URDF… (file.export with a .urdf path) writes the assembly for robot
simulators and ROS: a link per component placement with its mesh, mass and inertia, and the
assembly's joints, so the parts move in the simulator as they do in SolidCraft. The meshes are
written as STL files next to the .urdf. See docs/urdf.md.
Saves are atomic: a crash at any moment leaves either the old file or the new one, and the
replaced version is kept as <file>.bak. A save keeps the file's permissions, and a design
opened through a symbolic link (a synced folder linked into a project) is saved into the file the
link points to, so the link stays. Unsaved changes in every open tab are autosaved to separate
entries in a recovery folder, and the next launch offers to recover each design. Saving or
closing one tab removes only its recovery entry. Older design files are upgraded on open, and files
from a newer version are refused with a message.
Save writes a container by default; files named .json keep JSON on subsequent saves.
Save As offers a container or plain JSON on
desktop; Save As JSON is also available in the File menu on desktop and web. Choose
the .json extension for plain JSON. Export Design as JSON writes an independent,
self-contained copy without changing the current file. Scripts can explicitly pass
"format": "json" to file.save or file.save_as even with a .solidcraft name.
Opening detects the format from the contents, so existing JSON designs keep working.
Containers favor fast reading and saving by default. Scripts can request
"compression": "compact" when file size matters more than CPU time.
Containers deduplicate identical STEP, mesh and image payloads. Mesh coordinates retain
their original double precision; this is not a conversion to STL. Geometry still rebuilds
from the timeline when opening. When a saved display preview is available, desktop Open
shows it while checking geometry; editing starts after the complete rebuild. Cancelling
keeps the current design. Previews cannot be edited or measured. Scripts can opt in to a disposable geometry
cache with cache: true on warm saves; saving never rebuilds cold geometry just to
create one. Ordinary saves and browser downloads default to cache: false. An additional display-mesh cache remains opt-in
with preview: true. Unsupported or mismatched caches are ignored. Browser opening
still rebuilds synchronously.
Warm saves with explicit cache: true can also include a small PNG thumbnail. It travels with the file, so
Home can show the recent design without reconstructing it when no local picture
is available. Missing or incompatible pictures use the usual thumbnail generation.
The default cache: false omits the portable thumbnail too.
The repository's docs/design-container.md describes limits and encoding details.
Designs are saved in the newest format this version of SolidCraft knows ("format": "solidcraft/N"
in the file). It opens designs of every older format and upgrades them as they open; a design
from a newer version asks you to update instead of being guessed at. Development (nightly) builds
save a format number no release reads yet.
Sketch patterns retain their live direction-line references and placement settings when saved. Older patterns keep their fixed vector directions and original placement defaults.
Upgrading keeps what a design meant. Designs keep recorded body transfers, explicit 3D curve coordinate spaces and certified face bindings, alongside semantic configuration styles, associative free-form offsets and cylindrical emboss selections. Older ownership maps, geometry and expressions retain their saved meaning. Legacy 3D curves with retained associations receive their producer's coordinate-space marker without changing any saved points; detached curves retain world coordinates. A lost cross-component association refreshed by an older build may need an explicit relink or refresh when its source returns. Loading never guesses a numerical correction. Association records, coordinate arrays and placements are bounded and checked.
Design properties
File → Design Properties… (also under Document Settings in the Browser, and on the
top component's right-click menu) records who made a design and what it is: project, part
number, revision, author, organization, description and licence. OK applies the changes as
one undo step. Scripts and agents use the same commands: document.metadata reads them,
document.metadata.set (key and value, or values: {key: value, …}) and
document.metadata.remove (key or keys) change them. document.inspect and the MCP
inspect_design tool include them.
Exports carry them where the format has a place for them:
| Format | Written |
|---|---|
| STEP | author and organization in FILE_NAME, description in FILE_DESCRIPTION (on one line) |
| IGES | author and organization in the global section |
| 3MF | project as Title, author as Designer, description as Description, license as LicenseTerms |
The part number and revision have no standard place in these formats and stay in the design.
In the file they are one optional top-level metadata object; a design without properties has
none, and is saved exactly as before:
"metadata": {
"project": "ACME enclosures",
"part_number": "ACME-1042",
"revision": "B",
"author": "Jane Doe",
"organization": "ACME",
"description": "Vented case for Raspberry Pi 4",
"license": "CC-BY-4.0",
"com.acme.description": { "shop_id": 88123 },
"org.printfarm": { "slicer_profile": "PETG-0.2", "internal_id": 4 }
}
- SolidCraft's keys are unprefixed and hold text. Every other unprefixed key is reserved for later SolidCraft versions.
- Other tools keep their data under one reverse-DNS key of their own (
com.example.tool: two or more dot-separated labels of letters, digits,-and_), holding an object. A tool writes only under its own key and leaves the others alone. - SolidCraft keeps every key as it is through open, edit and save, in both the container
and plain JSON, and never checks what another tool wrote: a well-known key holding something
other than text (
"author": 42) is kept, reads as absent, and never stops the design from opening. Typing over it in Design Properties replaces it. - Other tools' keys are never exported; Design Properties lists them read-only.
- Keep values small:
document.metadata.setrefuses text over 4 KiB, an object over 64 KiB or nested more than 32 levels deep, and more than 256 keys or 256 KiB in all. Binary data belongs in a file next to the design.
Design properties are first saved in format 21. Inserting or linking another design doesn't
copy its properties; Save As keeps them. A command script of the design
(timeline.export_script) sets them in one step.
Designs in shared or synced folders
A design kept on a network share or in a synced folder (Dropbox, OneDrive, Syncthing, Google Drive) can be open on two computers at once. The desktop app watches for two writers:
- Open elsewhere. While a design is open, SolidCraft keeps a small lock file next to it,
.~<file>.lock(computer, user, SolidCraft version, when it was opened), refreshes it every minute and removes it when the design closes. Opening a design that another SolidCraft has open says so: Open Read-Only (look at it; Save asks for a new file), Open Anyway or Cancel. A lock not refreshed for three minutes (left by a crash or a lost connection) is ignored. Lock files never show in Open or the recent lists, and a design in a folder you can't write to still opens. - Changed on disk. SolidCraft remembers the file's size and modification time when it opens or saves a design. If another program or computer wrote the file since, Save asks first: Save a Copy (the default), Overwrite or Cancel.
Scripts, the CLI and the control channel never get these questions. doc.open opens the design
and reports open_elsewhere in its result (read_only: true opens it read-only), and file.save
over a file that changed on disk fails with an error until it is given overwrite: true.
Saving in the web app
In Chrome, Edge and Opera, Save As opens your browser's save dialog and writes the
.solidcraft file to the folder you choose, and Open opens the browser's open dialog.
Save then writes back to that same file. The browser remembers the file, so after you
reload the page the first Save asks once for permission to write to it, and carries on.
A design you open from your computer saves back to the file you opened.
In Firefox and Safari, which can't write to a folder, Save and Save As keep designs in this browser's private storage instead, and the app says so. Clearing the site's data can remove those designs. Download a copy to keep it outside the browser or move it to another device.
To get a copy as a download in any browser, choose File → Download design. It holds the current design, including its unsaved changes. In File → Files in this browser… (and in Firefox and Safari, File → Open), Download open design does the same, and Download beside a stored file gives its last saved contents. Whether a folder chooser appears depends on your browser's download settings.
Use Open from computer… in the same window to open a design from your computer; where the browser can write to a folder it saves back to that file, otherwise it works on a copy kept in the browser. Use Rename beside a stored file to change its name. Autosave and recovery stay in the browser's private storage in every browser. Exports such as STEP and STL are downloaded.