Source preservation only. The manifest now records the actual published index README rather than the initial selection note, and states its pre-Git-normalization hash scope. Original product proposals are unchanged. The canonical index links all branch checkpoints; validation and owner handoffs remain pending.
Canonical preservation of W10, W21, X4 and supporting W38 source proposals. Sources remain deliberately unregistered and unexecuted, with real bases and alternatives documented in proposals/drawings/README.md. Existing contributor claims and PRs remain authoritative; this is not a merge-ready integration candidate.
Use fresh local mutable counter values instead of mutable references to
constants. Enumerate the ten original copied shapes after asserting their
independent fixture count. Preserve all production code, numeric expected
values, refusals and the other tests. No lint suppression or ignored tests.
The prior 6b39ee36 attempt stopped at test-target Clippy, before native
test builds/runs and wasm. Both test-only hunks were independently reviewed;
normal formatting passed without changing bytes. New results are pending.
Remove an unused private Interval helper and collapse the copied-vertex
guard to an equivalent let-chain with identical evaluation order and error.
Preserve all 28 geometry and 10 bounds tests, limits and assertions.
The prior a7f3c78b development attempt stopped at Clippy; IO/engine tests
and wasm were not reached. Both corrective hunks were source-reviewed,
and normal formatting passed. Tests for this new source remain unrun.
Add the reviewed minimal X1 bulged-path producer, conservative bounds,
ordered layered bulge reader, ELLIPSE/TEXT imports and two original
cross-feature cases. Preserve legacy entry points; retain the two explicit
source-supported fixture migrations, not a blanket assertion relaxation.
Include the three original owner corrections from #8534 to unblock wasm,
without adopting later main or transferring upstream validation.
Normal cargo fmt --all passed. 223 declarations in the nine IO files
(333 including 110 unchanged UI/web declarations) are not test results.
This WIP has not been compiled or run; full CI and caller migration remain.
Earlier-source native passes do not qualify this candidate.
Co-authored-by: Ray <raykholo@users.noreply.github.com>
Add explicit alignment production and bounds callbacks, immutable layer-zero
initialization/import, and a reviewed fix retaining its metadata through copies.
Preserve the existing POINT and real writer-reader roundtrip foundation.
156 test declarations across four modules, including 26 additions against the
published parent; this source has NOT been formatted, compiled or run. Separate
source reviews do not qualify runtime. The older-source X2 gate remains separate.
Source-only reviewed four-file increment. 18 added test declarations, not executed. Published at the user's request; current X2 validation continues on unchanged older source.
Stacked on the owned X2 model. Includes 52 declared tests, not executed. Legacy projection and the remaining importer contract are pending; not ready for merge.
* claim: #7861
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch dimensions in a colour of their own in every theme (#7861)
Dimension lines, arrows and values were drawn in the same near-black or
near-white as fully constrained sketch curves, so they were hard to tell
apart in a busy sketch. Every theme now sets its own dimension colours:
plum on Light, orchid on Dark, and a hue from each built-in theme's own
palette. Colour vision tests measure them against the curves, the
construction lines and the conflict red, and a new test holds every
shipped theme to readable, distinct dimension colours.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Closing from the background: the window comes forward to ask about unsaved changes
Right-click › Close on the taskbar while another app had the focus showed the
"save changes?" prompt in a window that stayed behind, so the close seemed to do
nothing. When the close stops at the prompt, the window now asks for the focus
(unminimizing it first) and for the user's attention, which is what native
Wayland offers (xdg-activation) and marks the taskbar entry where the system
refuses the focus. A window already in focus is left alone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* X11: ask the window manager to raise the window as a user action
winit's Focus asks as an application, which KWin's focus-stealing prevention
refuses for a window last used before the switch away: it raised nothing and,
since the attention request was then skipped, did not even mark the taskbar.
On X11 send _NET_ACTIVE_WINDOW with source 2 (a direct user action) through
x11rb, which restores a minimized window too; native Wayland keeps the
attention request. Moves the logic into come_forward.rs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Come forward once the taskbar menu is gone, without flicker
KWin raised the window, then the taskbar's closing context menu handed the
focus back to the previous app 2 to 11 ms later, so the window only came
forward some of the time; asking again made it flicker. Ask 120 ms after the
close instead, and again only when the focus is taken back within 50 ms of a
request (a slow menu) or never came. A later switch away is the user's own and
the window stays behind.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch: show center and midpoint alignment guides
Add transient per-viewport horizontal and vertical guides with a sketch preference. Related tests, native UI, WASM and supplementary Linux checks pass. Full CI remains blocked by unchanged Windows engine dead-code Clippy errors.
* Sketch: complete alignment-guide translations
* Tests: compile chosen-program mutex only on Unix
* claim: #6020
* Measure: number the two picks in the view (#6020)
"1" and "2" beside the measured line's ends, as the panel lists the
picks, each just past its end so it stays off the line and its length.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* claim: #6975
* Create Sketch: the grid previews the plane under the pointer (#6975)
While Create Sketch waits for its plane, the grid now lies on the origin or
construction plane under the pointer, so the sketch's plane shows before
the click. A plane's browser row under the pointer lights that plane in the
view, and previews it the same way.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Create Sketch: the planes at the top of the History preview too (#6975)
main now draws the Origin rows at the top of the vertical History as well
(history_rows). Hovering a plane there lights it like its row in the browser:
the plane is handed over the same way the hovered bodies are.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Links: update linked designs that changed while the assembly's tab was away
A design coming back to its tab compares the modification time and size of its linked
design files with what they were when it went to the background, and runs Update Links
on the worker thread for those that changed: saved in another tab, another window or by
a checkout. Preference update_changed_links (Design, Linked designs) turns it off.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Docs: the assembly guide says linked designs update when their tab returns
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Links: Vietnamese strings, now that vi is a complete catalog
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Links: remember what each design last read, so an update that fails is tried again
A design's linked files are now recorded as the design last read them: when Update Links reads a
link without an error, or when the link is first seen. Coming back to the tab reads again what
differs, one link per run, so a file that cannot be read holds up no other and is tried again
the next time, and a part changed while the assembly was shown is taken up on its next return.
Closing several tabs together reads nothing on the way, and the web build, which keeps no file
times, looks at nothing.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Links: no read during a save-changes prompt, records for adopted designs and missing files
A tab shown to answer "save changes?" reads no link before its save, and Cancel there or on a
batch lets the tab left in front read what it skipped. A design opened in place of the blank one
records its links at once, a link whose file is missing is recorded as missing so it is read when
the file comes back, and a read queued for a design that is no longer shown by the time it starts
is dropped. More changed links than can wait at once are read on the next return.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Assemblies: edit a linked component in place, with the assembly around it
Activating a linked component opens the design it comes from in a tab of its own, with the
rest of the assembly drawn faded around it where the component sits, so the part's own history,
browser and commands are at hand. Finish Editing in the browser saves the part, goes back to the
assembly and closes the part's tab when the edit opened it; coming back to the assembly updates
the changed link (or Finish Editing runs Update Links when that is turned off). Update Links in
the part also reads the assembly's links again, off the window thread, and redraws the assembly
around the part; a result for an assembly changed meanwhile is dropped with a message.
The part is an ordinary tab, so saving, autosave, recovery, closing and quitting treat it as any
other design. The model scene's ghost triangles are now drawn (only the preview's were).
Preference edit_linked_in_place turns it off. Builds on the update of changed links when an
assembly's tab returns.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Edit in place: a double-click opens it, Activate only activates
A double-click on a linked component edits it in place, in the browser as on a face in the
view (around the placement clicked), and the component's menu gets Edit in Place. Activate and
the browser's radio button make it the active component as they always did, so a linked
component can still be activated without opening its design.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Double-click on a component's face: edit a linked one in place, activate any other
A double-click on a face in the view activates the component the body belongs to, at that
placement, as a double-click on its row does; a linked one is edited in place. Bodies of the
design itself or of the active component are left alone, and so is a double-click with a
modifier. One preference, double_click_components, covers both gestures.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Edit in place: fixes from review
An edit whose part never opens is forgotten instead of starting on a later open of the same
file, and the web build starts the edit after its synchronous open. Finish Editing goes on only
for a save into the linked file and in the part's own tab, waits for a "changed on disk"
answer, and says so when the part was saved elsewhere. Showing the assembly no longer drops a
refresh, which now records the links it read so coming back reads nothing twice. The faded
assembly is worked out once per picture, a bad mesh index skips its whole triangle, and the
last two status texts come from the catalogs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Edit in place: Finish Editing on the MODEL tab and the mouse's back button
Finish Editing is a command now, component.finish_edit, with its schemas: the app window
answers it, anywhere else nothing changes, as with view.fit. While a linked design is edited in
place the MODEL tab ends with a FINISH EDITING panel, pinned at the right as FINISH SKETCH is,
with a new left-arrow icon; the browser keeps only the line naming the assembly. The mouse's
back button, or a keyboard's Back key, finishes too unless a dialog, tool or sketch is open.
The assembly guide describes editing in place.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Edit in place: fixes from the second review
Saving the assembly waits while it reads its links for the part
Finish Editing waits for dialogs, tools, sketches and prompts open in the part, also when run as a command
The assembly reads the edited part explicitly when it does not check its links on its own
The FINISH EDITING panel cannot be customized
A tab opened by an edit stays that edit's to close when the part is entered again
The mouse's back button finishes editing; the keyboard's only while no text field has focus
Refreshing the assembly keeps its selection and active component
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Edit in place: fixes from the third review
component.finish_edit run as a command opens nothing and answers with an error when it cannot finish; engine.batch and engine.script refuse it
A double-click or Edit in Place while a dialog, tool or sketch is open in the assembly keeps it open
What changes in the assembly while it reads its links without changing its document stays: picks, activation, components dragged but not captured
Showing the assembly while it reads its links starts no second read
A new or opened design in the part's tab ends the edit
Finish Editing keeps the part's tab open while its agent chat runs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Edit in place: Update Links run as a command in the part refreshes the assembly too
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Edit in place: the ribbon button reads Finish Edit, as Finish Sketch does
A bare "Finish" took the catalogs' surface-finish translation (German "Oberfläche").
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Edit in place: fixes from the fourth review
Finish Edit waits while the part's agent is in the middle of its turn, instead of keeping the tab of any chat open
A part saved while its assembly refreshes is read again when the edit finishes: the refresh records the files as it found them
A double-click on a linked component while something is open in the assembly says so instead of activating it
Commands are recognised in any spelling the engine takes, so engine.batch and engine.script cannot slip Finish Edit through
Update Links run as a command refreshes the assembly from the control channel only, not for the app's own reads
Moves of components the refresh removed are not carried over, and a refusal is one message
The command description and the guide say that the command does not save the part
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Edit in place: the command is Finish Edit, as Finish Sketch is
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Edit in place: a command to start it, and fixes from the full review
Added component.edit_in_place {component, path?}: the double-click and Edit in Place in the menu run it, and as a command it asks nothing, so a network location not allowed yet or a design open elsewhere makes it fail instead
Finish Edit asked by the part's own agent finishes once that agent's turn ends; the button still waits for the turn
The parameters and design properties panels, with text typed and not applied yet, hold up entering and finishing an edit
A refresh is not taken up under an undo, a queued command or a feature still being computed in the assembly
Refusals say what holds them up (a refresh, a file being read or written) instead of nothing
The browser strip names the assembly that is refreshing, not any assembly
The schema description says the command does not save the part
Korean strings for every new message
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Edit in place: fixes from the second full review
The MCP batch tool refuses component.edit_in_place and component.finish_edit as steps instead of sending them one by one
Edit in Place asked while the assembly's agent is in its turn opens the part once the turn ends; a double-click then is refused rather than stopping the agent
Both commands answer queued: true when they wait for the agent's turn, and stopping the agent forgets what it queued
An undo, a queued command or a delete check running in the design holds up entering and finishing an edit
A refresh redraws every edit of the assembly, the parts in other tabs too
In an assembly never saved, a double-click activates the linked component as before
The control and MCP docs list the new batch and script refusals, and the commands list every way they fail
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Edit in place: fixes from the third full review
The MCP batch tool refuses the two commands however a step names them, an execute step too
A queued Edit in Place is checked fully when asked for, a second one is refused, and both queued requests wait for what runs in the design instead of being dropped
Disconnecting the agent forgets what it queued, as stopping it does
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Edit in place: help text for the Finish Edit button
main now requires help for every toolbar command (#7134), and Finish Edit
is one. Written in every language the branch's catalog carries.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Add unit tests for fill.rs's two ray-casting predicates, which
fill_general.rs relies on to decide which parts of a surface a loop
covers: a point inside/outside a square and an L-shaped (non-convex)
loop, documenting how inside_loop resolves points on an edge or
vertex; and segments that cross, stay apart, touch at an endpoint,
or are collinear with and without overlap for segments_meet.
Closes#38
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
* Render: one sRGB transfer function, geom::srgb, for the hand-written copies (#7161)
Twelve places wrote the sRGB encode or decode formula out by hand (doc's glass
recipe, engine's render library, glTF import, the PNG film, photographic and
presentation output, the ray tracer's image, the stage, the surface decoder,
texture normalisation and the theme's contrast check). They now call
solidcraft_geom::srgb: encode/decode on 0..1 values, encode8/decode8 for 8-bit
codes. render::surface::srgb8_to_linear stays as a re-export, so its callers
are unchanged.
Same results: the module's tests compare it bit for bit with the formula as the
callers wrote it, over all 256 codes and a dense sweep of linear values plus
the knees, out-of-range values, infinities and NaN, for each way the callers
clamped (after encoding, before it, or not at all), and decoded values either
side of the decode knee, where no 8-bit code falls.
One change: glTF import encoded in f32. Over every f32 in 0..1, 218 of
1,065,353,217 values sit on a rounding tie that f32 got wrong and now give the
neighbouring code (f64 gives the nearer one).
Left as they are: render/job/image.rs (replaced by exact tables in its own
change), the tests and the starter-pack validator that keep their own copy as
an independent reference, and the WGSL shaders (they can't call Rust).
Fixes#7161
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Tests: the sRGB codes of five callers that had no test of their output (#7161)
The glass swatch, a library surface applied as a plain appearance, glTF's
fallback for a colour channel that isn't a number, a stage panel and an empty
pixel in presentation output each get an assertion with their sRGB codes worked
out by hand. A planted wrong conversion at each is now caught by that test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Editing a body Mirror from the history showed Object Type Features (the dialog starts from
the timeline selection, the Mirror itself, which turns pattern objects into features) and
dropped its saved Join. The editor now sets the objects back to Bodies for a body Mirror or
Pattern and restores the Mirror's combine.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #8226
* Theme: paste a theme into the Import tile (#8226)
Ctrl/Cmd+V on Preferences > Interface imports a copied theme file, the tile's
menu offers Paste theme, and a theme file dropped on the tile imports instead
of opening as a design. Pasted text that is not a theme keeps its usual
meaning. The tile reads "file or paste".
* Theme paste: the new strings in Arabic, Hebrew and Vietnamese too (#8226)
* Dutch: translate the Render workspace's 706 messages (batch 2)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Dutch: the 27 Render messages main gained since (the Appearances panel)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Hungarian: translate the Render workspace's 706 messages (batch 2)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Hungarian: the 27 Render messages main gained since (the Appearances panel)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Galician: translate the Render workspace's 706 messages (batch 2)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Galician: the 27 Render messages main gained since (the Appearances panel)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Malay: translate the Render workspace's 706 messages (batch 2)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Malay: the 27 Render messages main gained since (the Appearances panel)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Norwegian Bokmål: translate the Render workspace's 706 messages (batch 2)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Norwegian Bokmål: the 27 Render messages main gained since (the Appearances panel)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* feat: Fewer ambient occlusion samples while the view moves (#7165)
- Draw AO with 8 samples while the view moves, 32 once it has held still for 0.25 s
- Share the settle tracking with adaptive supersampling and repaint when it settles
- ui.screenshot waits for the full-quality frame (at most half a second)
- Test the sample choice, the uniform slot, and ghosting at both counts
* fix: Set the AO sample count in the scale test frame
- scale_tests.rs, new on main, builds a gpu::Frame without ao_samples
- Use AO_SAMPLES, as the other still-frame builders do
---------
Co-authored-by: P'tite Tête <asso@ptitetete.com>
* Logo: an option to draw it in the theme's accent
Preferences > Interface > Logo: Theme's colours (the default, as before) or
Accent, three shades of the theme's accent. ui.prefs logo_accent.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Logo: US spelling for the option's label, as upstream's English catalog uses
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Translations for the new options in the five complete catalogs (ar, he, ja, vi, zh)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #6032
* View angles: an optional readout under the orientation gizmo and a command to set them (#6032)
View > View angles (also Preferences > Graphics, or view.angle_readout) shows the
view's pitch, yaw and roll in degrees and Ortho/Persp under the axis view or cube,
updated live; off by default. Clicking the readout opens a small card to type
exact angles, which runs view.apply.
The angles are the camera's own (Camera::view_angles / with_view_angles), about
the up axis: yaw is the heading of the line of sight (Front 0, Left 90, Right -90,
Back 180), pitch the eye's elevation (Top 90, Bottom -90), and a positive roll
turns the model clockwise. view.apply documents this convention and now refuses a
pitch past +/-pi/2.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #4451
* fix(#4451): the body colours travel with the OBJ file
An OBJ export left the colours of the design behind: the mesh file named no
material, so every body arrived in the reader's default grey. The export now
writes a material library (.mtl) next to the mesh file, with a material per
body, and the mesh file names that library with mtllib and picks the material
of each body with usemtl. Every path is checked before anything is written,
the library goes first and the mesh file last, so a mesh file never names a
library that is not there. Only the colour of the body travels: the colours of
single faces and the transparency of a body stay out, as a face colour needs
material groups inside one object. The export that writes one file stays
material-free, so it never names a library it does not write.
A mirror's or pattern's copy of a face carries its seed's construction certificate, and on a
combined body that certificate finds the seed's face. Capturing an attachment never checked
that the certificate finds the picked face, so a sketch on the mirrored half sat on the
original. The capture now falls back to the face's exact history name when the certificate
names another face, as docs/face-attachments.md describes.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #7587
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Naming: name a long history of one body from the oldest up (#7587)
A body's face names are worked out from the names of the body it was made
from, recursively. After a few hundred features in a row whose names
nothing asked for, reordering or moving an earlier feature re-evaluated
them all and the first name lookup (a fillet at the end) recursed once per
feature, overflowing the 1 MB window-thread stack on Windows. Name every
16th earlier version oldest first so no lookup recurses through more than
16 of them; the names are the same.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The version right of Report a bug had a fixed 60 points, enough for v0.25.1 but not for a
release candidate such as v0.21.0-rc.1, whose text was painted over the button. The bar now
measures the version and reserves its width plus a gap, never less than before. The extra
width comes only from the document tabs' spare room: where they would keep less than one tab,
the version is hidden, as on a narrow bar.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #8104
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Windows: an export over a file another program holds briefly waits for it, up to 2 s (#8104)
write_atomic replaces the target with one rename. Windows refuses that rename
(os error 5 or 32) while another program, such as an antivirus or indexer
scanning a file written a moment earlier, has the file open without delete
sharing. The retry gave up after 127 ms of waiting, which a scan under load
outlasts, so render.export and library.export over an existing file failed
with "Access is denied".
The retry now keeps doubling its wait up to 250 ms, about 2 s in all. If the
file is still held after that, the error says the file is in use by another
program, followed by the system's own message. The old file stays whole
throughout, as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Saving over a read-only file fails at once, and says why Windows refused
Access denied (os error 5) is also what Windows gives for a read-only or
ACL-denied target, which waiting does not fix. A read-only target is now
refused without the 2 s wait, with "the file is read-only". Any other refusal
that outlasts the wait says another program may have the file open or it is
read-only, followed by the system's own message.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch dimension: a slot's two caps measure the overall length (#7464)
* Sketch dimension: slot caps need tangent sides, not just shared lines (#7464)
* Sketch dimension: slot caps measure the centre-to-centre distance (#7464)
---------
Co-authored-by: Dane Doubell <2853036+danedoubell@users.noreply.github.com>
* claim: #8097
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Insert design: file names get the width the size and actions leave (#8097)
The web app's "Files in this browser" window listed stored files in an egui
Grid sized from cell content: the wide action buttons took most of the fixed
window width, the name and size columns were cut, and the rest was empty.
Add ui_kit::name_list on the kit's table grid: the name column takes the
width the other columns leave, the other columns are as wide as their widest
cell (measured as drawn, a changed measurement discards the frame), and a
name is cut with an ellipsis and its full text on hover only when it does
not fit. The window is a little wider (still capped at the browser width).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #5694
* Redefine: the reply reports the features it rebuilt (#5694)
timeline.redefine builds the edited feature and the features after it in its trial session;
the session's own evaluation then only takes those results from what the trial kept, so the
reply reported that second evaluation and read as "nothing was rebuilt" although the geometry
changed. Report the trial's rebuild, plus anything the session still had to compute itself, as
timeline.edit reports the same change; `restored` keeps counting only genuine reuse of earlier
results.
Tests: redefining_reports_the_features_it_rebuilt fails before the change with
{"feature": 2, "recomputed": 0, "restored": 2} and passes after it;
redefining_computes_the_later_features_once still guards the single computation (the reply
doubles if the kept results are lost).
* claim: #5240
* Preferences: reverse vertical pan direction (#5240)
Add "Reverse vertical pan direction" beside the horizontal setting in
Preferences > Navigation. pan_reverse_y defaults off, so older settings
files load unchanged, and flips the vertical part of two-finger pan on
every canvas through navigation::handle, independent of the horizontal
setting. Labels in en, ja, zh, de, and ko.
* Reverse vertical pan: strings as a pending locale file, with Arabic, Hebrew and Vietnamese
* Control protocol: pan_reverse_y in the sorted ui.prefs list (#5240)
* Italian: translate the Render workspace's 706 messages (batch 2)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Italian: the 27 Render messages main gained since (the Appearances panel)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Drawing sheets: sizes, the ISO 5457 border and its marks (#3875) (#5108)
* Drawing sheets: sizes and the ISO 5457 border (#3875)
A sheet is a paper size and an orientation: ISO A0 to A5, Letter, Legal,
ANSI A to F, architectural A to E1, or a custom size (finite, above 0 and
at most 5,000 mm a side), landscape by default and either way for every
size.
A border profile is the rule for its border, and layout() works it out
for a sheet: the frame inside the margins, and the reference fields with
their labels. The ISO profile has a 20 mm filing margin on the left and
10 mm elsewhere, a 0.7 mm frame, fields of 50 mm out from the centre
lines with the corner fields taking what's left, rows lettered from the
top without I and O (AA, AB and on past Z), columns numbered from the
left, and an A4 sheet labelled on its top and right only. A profile can
divide the frame equally instead, or have no grid.
The records sit in geom, next to the section records, so saving,
rendering and export can all use them. Nothing is saved or drawn yet:
there is no command and no format change. Margins that leave no drawing
space, values that aren't finite, fields outside 1 to 1000 mm, more than
200 equal fields and unknown JSON fields are refused.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Drawing sheets: ISO 5457's centring and trimming marks (#3875)
A drawing border profile now also holds the grid reference border and the
centring and trimming marks, either of which can be left out. The ISO
profile follows ISO 5457:1999 (read in its identical adoption,
IS 10711:2001):
- the grid reference border 5 mm outside the drawing border on every
side, with the field labels between the two;
- four centring marks on the sheet's centre lines, from the grid
reference border to 10 mm inside the drawing border;
- at each corner a trimming mark of two overlapping 10 x 5 mm
rectangles, in the margin outside the grid reference border.
The labelled sides are now a rule worked out for each sheet: ISO 5457's
(the top and right of A4, all four sides of the others) or fixed sides.
So one profile labels every size correctly. validate() checks what
doesn't depend on the sheet, for when a profile is read; layout() calls
it, then checks the rest against the sheet.
Refused:
- a grid reference border that isn't finite, is negative or is wider
than a margin;
- a centring mark that isn't above 0, would reach past the sheet's
centre lines, or is on a drawing border those lines miss;
- a trimming mark that isn't above 0, is wider than it is long, is
wider than the margin left outside the grid reference border, or is
longer than half the sheet's shorter side.
The grid rule's JSON is now externally tagged ("none",
{"from_centre": {"field_mm": 50}}), as the sheet size is, so an unknown
field is refused in every form; the internally tagged "none" ignored
one. Nothing is saved yet, so the shape change costs nothing.
The names follow the glossary, and ISO 5457's "drawing sheets": the
module is drawing_sheet (was sheet, which means sheet metal elsewhere
here), DrawingBorderProfile and SheetLayout (were BorderProfile and
BorderLayout), sheet_border and drawing_border (were paper and frame),
and Sheet::size_mm (was paper_mm). The first part of S1 is still a draft,
so the renames are made here rather than in a commit of their own.
The extra centring marks ISO 5457 asks for on sheets larger than A0
aren't here yet. Still nothing is saved or drawn.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Drawing sheets: a lighter built-in border, the filing margin a choice (#3875)
Cameron's decisions after seeing the sheet in the app, 2026-10-23:
- No trimming marks in the built-in profile: the filled corner blocks
take a lot of ink. trimming_mark_mm stays, so a profile can still ask
for them.
- Centring marks stop at the drawing border: from the grid reference
border to the drawing border, never into the drawing area (on A4 they
crossed the tables). centring_mark_mm keeps its meaning, how far a
mark reaches inside the drawing border, and now takes 0, the built-in
value, as grid_border_mm already takes 0 for "on the drawing border".
A mark of no length (a reach of 0 with no grid reference border) is
refused.
- Margins of 10 mm all round. ISO 5457's 20 mm filing margin on the
left, for drawings that get bound, is a named choice: filing_margin,
off when not given. On, it stands in for margins.left_mm, which is
kept, and still checked, for when it's off again. The 20 mm is
FILING_MARGIN_MM, and validate() and layout() both take the margins
from one helper, laid_out_margins().
A3 landscape is now 400 x 277 at (10, 10), with eight 50 mm columns.
With the filing margin it is 390 x 277 at (20, 10) again, the same as a
20 mm left margin set by hand. A profile with all three choices on
(ISO 5457's full border) lays out exactly as the built-in profile did
before, and the tests keep the old hand values for it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Drawing W23a workshop plate oracle fixtures (#4950) (#4962)
* test(oracle): reviewer-aligned parameterized workshop plate fixtures (#4950)
Independent analytic W23 expected values and seven verified models,
including four R10 corners and the 2 mm tangent top chamfer.
Document accepted Drawing states and commit regenerated docs/oracle.md
from successful workflow run #29 (90/90 pass).
* fix(oracle): correct W23 chamfer centroid and clarify drawing session ownership
* Paper scene: plain records of what is drawn on a sheet (#4890 A1) (#5513)
geom::paper holds what a sheet shows as plain data: groups of strokes and fills, each group tagged
with the object that drew it (an opaque id) and its kind, with one validation pass over a bounded
record count. Seg2's checks and the coordinate rule move into profile.rs so section and paper share
them, and drawing_sheet takes its side and line limits from paper, so each limit is written once.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Drawing sheets: centring marks across the whole margin (#3875) (#5554)
The built-in sheet's centring marks now run from the trimmed sheet's edge to the drawing border.
From the grid reference border, as before, a mark was the same length as a grid field mark, and on
A3 it sat on one (the sheet's centre falls on a field edge), so it no longer read as a mark once the
grid reference border's outline is left out (Cameron, after the prototype on #3875).
- DrawingBorderProfile.centring_mark_start: sheet_edge (the built-in's, and when not given) or
grid_border (ISO 5457's figure 5); ISO's full border in the tests asks for grid_border.
- A mark still needs a length: one that stops on the drawing border needs room outside it on the
side it starts from, every margin for sheet_edge, the grid reference border for grid_border.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Drawing sheets: no centring or trimming mark longer than the largest sheet's side (#3875) (#6410)
A border profile capped every length at 5000 mm (the largest sheet's side) but two: a
centring mark's reach inside the drawing border and a trimming mark's length. A file could
give either a huge whole number, such as 18446744073709551615. The read's number check passes
whole numbers, so the profile took it as 1.8e19 mm, and saving wrote it as a decimal the next
read refused: a drawing that saved but didn't open again. K2's fuzz test of drawing files
(#3890) found it.
Both now take the cap the margins have, `MAX_SIDE_MM`, in `validate`, so the file is refused
when it's read. Layout's own checks against the sheet are unchanged, so a 5000 mm mark still
validates and is refused only on a sheet too small for it.
Tests: the hostile-profile test refuses 5000.000001 mm and 1.8e19 mm for each mark, and takes
5000 mm on its own; 4 planted faults (each cap removed or made one short) are all caught.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Bill of materials types: phantom, reference, purchased and inseparable (#3874) (#5107)
* Bill of materials types: phantom, reference, purchased and inseparable (#3874)
A component's bill of materials type is a part property (`bom_type` in its metadata, set with
component.metadata.set and read by component.metadata, as part_number is), so older releases
keep it and no format change is needed. It isn't a design property: the Design Properties
dialog doesn't list it and document.metadata.set refuses it. A word this build doesn't know
reads as normal and is kept.
assembly.bom counts by type: a phantom has no row and what it holds counts where it is placed,
in the same rows as the same parts there; a reference counts nowhere, and nothing inside it
does; purchased and inseparable components are one row, and in parts only an inseparable
component's purchased parts are listed too. Rows of one part number but different types are
different rows. Rows and children carry bom_type. With no types set the rows are as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Glossary: bill of materials and bill of materials type (#3874)
The bill of materials types are a new idea, so the glossary gets their
term, with the five types, and "normal" gets its fourth meaning in the
table of words with more than one meaning.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Bill of materials types: a plain line for each (#3874)
The guide's table and the glossary entry say what each type is for in
plain words: something you make, something just to group, something to
use as a reference, something you buy, something made as one piece.
The names stay the five standard ones.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Bill of materials: kits fold together only when their contents' types match (#3874)
With group_children, two kits with one part number share a row when what they hold reads the
same. That reading (kit_key) compared each child by part number and count, but not by its bill
of materials type, though rows themselves are told apart by type. So a kit holding a made bolt
and a kit holding a bought one, both KIT, folded into one KIT x2 row listing the first kit's
bolt twice: the other type was lost, and which one depended on creation order. 0xlillith found
it in review of #5107.
kit_key now includes each child's type. Equal part number, type and contents still fold.
Test: two KIT kits each holding a BOLT fold into one row of two; once one bolt is purchased they
are two rows, each listing its own bolt. Planted fault (the type left out of the key): caught.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Drawing files: a drawing saved as a container or as plain JSON, never read as a design (#3890) (#7098)
* File format families: drawings share the design format's versioning (#3890)
The format code works for a family of files: its own tag, its own numbers and
its own folder of change files, with one reading of a file's format, one
refusal of a file a newer build saved and one upgrade for every family. Designs
(solidcraft/N) are one family and keep everything they had: FORMAT and FORMATS
are as they were, and pending_names, format_string, version_of and read mean
what they did. Drawings (solidcraft-drawing/N) are the other, with format 1 as
their first record and each later change one file in
crates/doc/src/drawing/format/pending/, as design changes are. Nothing saved
changes.
A design tag never reads as a drawing's or the other way round, and a file that
names no format is a design from before format strings, never a drawing. Every
refusal names its family; a design's read word for word as before. A design
listing a pending change this build doesn't have is now refused as a newer
build's before its content is checked, as a newer number already was, so a
newer build's fields never read as damage. The list reader is
take_names(v, key, noun), the shape #4811 uses for its own list.
For the maintainer, the release step changes: scripts/fold-format-pending.sh
now folds every family that crates/doc/build.rs lists (FAMILIES), each into
its own next number: designs into crates/doc/src/format/v<N>/ as before,
drawings into crates/doc/src/drawing/format/v<N>/ from 2. With nothing pending
in a family it says so and moves nothing. Its test folds a drawing change into
v2 beside the design changes. docs/releasing.md, CONTRIBUTING.md and AGENTS.md say so.
build.rs lists each family's changes from its own folder, each folder's
versions contiguous from its own base.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Drawing record: format 1 of the drawing family (#3890)
A drawing is now a record of its own (crates/doc/src/drawing.rs): its name,
the designs it draws (sources: an id and the design file's path, relative to
the drawing's folder as a linked design's is), its properties under the design
metadata's rules, its units (each design's own, or one length unit of the
expression unit table), its content language (a BCP 47 tag) and its sheets.
A sheet holds its paper, a copy of its drawing border's profile, the
resolution its pictures are rendered at (300 dpi unless it says, 25 to 2400),
the component it documents and whether it is left out of the sheet count or
of printing. This is format 1 of the drawing family; each later kind of
record adds one file in crates/doc/src/drawing/format/pending/.
The rules: one id counter for every object, raised above the largest id when
a file is read and never rewound; every reference is an id that must resolve,
or the file is damaged and refused, never patched; 1 to 1,000 sheets, at most
64 sources, names of 1 to 256 bytes, paths of 1 to 4,096 bytes without NUL.
The same drawing always saves as the same bytes.
Drawing::from_json reads at most 16 MiB of text, then runs the family's format
check, the number check, the upgrade, the typed record (unknown fields refused
everywhere) and Drawing::validate. The number check is now one function every
family calls, Family::check_numbers, told which field holds other tools' data;
a design's refusals read as before.
Nothing saves or opens a drawing yet: the container is the next step.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Drawing ids: random and collision-free from format 1 (#3890)
A drawing's objects now take random ids, the form jyviko chose on #3890 from
#1017: no counter, so objects added on two branches never share an id and
never edit one line. The allocator is one module for drawings now and designs
later (#626 Phase 3), crates/doc/src/ids.rs: an IdSource gives nonzero u64s by
splitmix64 from a seed, each checked against the ids in use and drawn again
when it is one, or when it is 0. A run never draws a number twice, so at most
one draw per id in use comes before a free one, and a seeded run over a file
the same seed made still gets a new id. IdSource::seeded gives the same ids
for tests and replays; IdSource::from_entropy seeds a session from the
standard library's hash keys, the time and a count, with no new dependency.
In a file an id is 13 characters of Crockford base32, lowercase
(0000000000001 to fzzzzzzzzzzzz): ids::encode writes it and ids::decode reads
only that spelling, case-sensitive, so an id has one form to search for.
Anything else, 0 included, makes the drawing damaged. The drawing loses
next_id (a file that holds one is refused), and Drawing::new and allocate_id
take the IdSource. Messages name ids as the file writes them. The component a
sheet documents stays its design's own number until designs move over.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Drawing record: a drawing too large to reopen isn't saved (#3890)
from_json reads at most MAX_DRAWING_BYTES (16 MiB), but to_json had no limit. Other tools'
properties are kept as they came, and indenting them can grow a drawing read in at about 4 MB
past 16 MiB (two million zeros in one array), so the next read refused what the save had just
written. 0xlillith found it in review of #3891.
to_json now returns a Result and refuses JSON longer than a read takes ("drawing too large to
save (at most 16 MiB)"), so every drawing saved opens again. Nothing writes drawing files yet, so
the error reaches the future file writer before it publishes anything.
Test: that case is refused, and a smaller compact drawing saves and reopens unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Drawing files: a drawing saved as a container or as plain JSON, never read as a design (#3890)
K3 of the drawing storage plan (W11.2): the file a drawing is saved in. Nothing writes one from a
command yet; opening and saving come with K5.
- A drawing container is a design container holding `drawing.json` (Deflate, as `document.json`)
in place of `document.json`, and `container.json` says so:
{"content": "drawing", "format": "solidcraft-container/1"}. A design's `container.json` is as it
was, with no `content`. The plain JSON form is the drawing's own text. Both give the same bytes
every save, so a drawing can live in version control as text.
- `io::file_kind` tells a design from a drawing by `container.json` or by the JSON's format. The
design reader refuses a drawing ("this file is a SolidCraft drawing, not a design: open it as a
drawing") and the drawing reader a design; plain JSON only asks when it isn't a design, so
reading a design costs nothing more. A container's content this build doesn't know, or a drawing
container that also holds a design's `document.json`, is refused.
- `read_drawing` (bytes, either form), `read_drawing_file` (one seekable handle, the design
reader's checks) and `write_drawing_container`; `DRAWING_EXT` (`scdwg`) and
`is_drawing_path` beside the design's.
- The container writer's set-up and finish, the file opening and the container sniffing move into
one function each, which designs and drawings both use. The container's own checks (budgets,
entries, the ZIP directory) are the design container's, word for word.
Tests: a drawing round-trips through both forms byte for byte, and the container holds exactly
`container.json` and `drawing.json`; `file_kind` on both forms of both kinds; each reader refuses
the other's file; unknown content, a stray `document.json`, a missing drawing, an unknown
container version and a damaged drawing refused; both forms from disk, and a directory refused;
mutation fuzz of both forms (an error or a drawing, never a panic).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Paper scene: SVG of a sheet, its lines, arcs, cubics and fills (#4890 A3a) (#7032)
The first export of a paper scene, as SVG first (jyviko and 0xlillith on #4890), from the research
in the plan's §16. A3b adds ellipses, hyperbolas and Béziers above degree 3, which this refuses as
"not supported yet".
- `geom::paper::Prepared`: writers read a scene through `Scene::prepare`, which checks it first, so
none reads one unchecked. Clipping goes there too, once (A8).
- `geom::paper::arc_pieces`: an arc's ends in pieces of at most a quarter turn, the last its own end.
A half turn drawn from its ends is ill-conditioned: rounded to the nanometre (six decimals of a millimetre) its
centre moves up to 1e-2 mm, where a quarter turn's stays under 1e-6 mm.
- `io::paper::paper_svg`: one user unit a millimetre, the size in mm on the root; y flipped by
writing the height less y, so the file's numbers are page coordinates and an anticlockwise arc
has sweep flag 0; lines `L`, arcs `A` (never large-arc), cubics `C`, parabolas `Q`; fills
black and even-odd; one path per item; groups as `data-object` and `data-kind` (object ids can't
be XML ids). No background, so cutters see only the marks.
- Numbers are whole nanometres (six decimals of a millimetre): no exponent, no "-0", the same bytes every time. They are the
writer's own state too, so a joint is written once and a closed path ends exactly on its first
point. Numbers are written straight onto the document, with no text made per number. A width
under half a nanometre is written as one, and a piece too short for the grid is merged into the
next, both counted in the report; a path's last piece has no next, so it is kept however short
(unless it has no length). Output over 64 MiB is refused.
Tests: the A3 drawing border's whole document; quarter, 270°, clockwise and whole-turn arcs, and a
circle of two halves giving the same four quarters; the quarter-turn boundary both sides; cubics and
parabolas, and the conics refused; the number format; a closed path whose start and end round
apart; a sliver merged; an open path's short last line and arc kept, and one of no length dropped; read back through our own SVG reader (lines exactly, the arc as an arc);
the scene's JSON mutated into the writer (never a panic).
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Drawing W17a: view definitions as data (#4816) (#4817)
* claim: #4816
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Drawing views as data: named views, scales and the map onto paper (#4816, W17a P1)
What a drawing view is, before anything draws it (geom::drawing_view):
- NamedView: the six principal views and the isometric views from the four upper corners, each
an exact basis (a Plane whose normal points back at the viewer), never taken from a camera,
whose pitch clamp would tilt a top view, with no -0 to write out; `iso` reads as the
front-right one.
- basis_looking: any other view from its direction, an up and an origin (paper x = direction x up,
paper y = x x direction), refusing a zero direction, an up along it, or an origin that isn't
finite or within 1e12 mm. W07's
{direction, up, origin} view reads through it.
- Scale: paper : model as two positive numbers, read and written as "1:2", from 1:100000 to
1000:1; Scale::fit picks the largest ISO 5455 preset (50:1 to 1:10000) at which a view fits a
space (W07's "fits the page" default), compared as size x paper <= room x model so an exact
fit at a reduction isn't lost to rounding.
- ViewFrame: a view's basis, scale and place on paper, the point of its plane at `centre`
landing at `position_mm`. Its paper map moves the view's curves and their accuracy onto
paper (paper error = scale x model error); `boundary` maps a box of its plane onto paper from
its four corners.
In geom, where Seg2 lives:
- Similarity2 and Seg2::similar (new): scale, turn and shift a segment exactly, an arc staying an
arc. Similarity2::new refuses a scale that isn't above 0 and works the turn out once, not per
point;
- Seg2::map_controls: a segment's control points mapped by any function. It was doc's
sheet::map_controls; its four callers in doc use the method now, unchanged.
CurveAccuracy::scaled gives a curve's accuracy at another scale.
Tests: each named view's basis and viewer side, and the same bases from a direction and up; a 50 mm cube in every principal view at 1:1 and
1:2, and in isometric (edges 50 x sqrt(2/3), outline 50 x sqrt(2)); the view's centre on its
position (W07's 100 x 20 front view on A3 at 2:1); scales read, written and refused; the fitted
scale, an exact fit at 1:5 and ISO's smallest; no -0 in any named view's axes; an origin refused by
its own message; hostile bases, centres and positions; every segment kind moved by a similarity,
and the similarities refused; scaled accuracy.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Drawing views: the bottom view pinned, square near parallel, bounded scales and boxes (#4816)
From DevonHope's review of #4817:
- The tests pin all six principal views exactly. The direction-and-up test uses literal ups,
so a bottom view turned half round now fails it.
- basis_looking squares x to the direction before normalising. Near the 1e-9 threshold the
normal was up to 8.8e-9 rad off; now it is about 1e-16.
- Scale::fit fits nothing for a size or room that is negative or not finite. A size of 0 still
sets no limit that way.
- Each side of a scale is 1e-6 to 1e6, so a scale writes at most 49 characters.
- ViewFrame::boundary returns a Result. It refuses a box that is inside out or not finite on
paper, instead of dropping a corner.
- paper_map, boundary and seg_to_paper say which coordinates they take.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Drawing W18 S2: title blocks as data (#3875) (#3877)
* Title blocks: what a cell shows and where its text goes (#3875)
geom::cell holds a title-block cell's content: parts of text and fields
(component, drawing, sheet, revision or prompt, by key, with an optional
unit and decimals kept as written for the resolver to check), at most 64
parts and 4096 bytes of text, with no control character but a line break.
Beside it are a resolved value (live, overridden and drifted, or missing,
with "-" for each part that couldn't be read and why), an override with
the value it replaced, and the projection a drawing's views use (first or
third angle), which the title block's symbol will read.
A fit warns and never cuts: a count of characters, not bytes, with its own
count for text in Chinese, Japanese, Korean or Yi characters.
The cell text rule places a label and a value: the label's baseline 1 mm
plus its cap height below the cell's top, the value box under it inset by
1 mm, one line centred in the box, and wrapped or several lines down from
its top at the font's pitch, with a warning when the last falls below the
box. Wrapping by width, width warnings and glyphs a font lacks need the
text measured, so they wait for the paper scene's text.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Title blocks: definitions and their place on a sheet (#3875)
geom::title_block holds a title block's definition: its size (1 to 1000
mm a side), its outline and cell line widths, and up to 64 cells. Each cell
has a snake-case name of its own, a rectangle measured from the block's
lower-right corner, an optional one-line label with its cap height, and a
value: text from cell content, with its cap height, alignment and fit, or
the projection symbol, which reads the drawing's projection and is never
fixed in the block.
validate() checks a definition on its own, as when one is read: cells
inside the block and not overlapping (touching is fine), label and value
cap heights that fit, and the content's own limits. layout() puts the
block on the drawing border's lower-right corner of a sheet, refusing one
that doesn't fit, and gives each cell's rectangle, its label's place and
its value box by the cell text rule. On A3 landscape a 180 x 54 mm block
runs from (230, 10) to (410, 64); it fits A5 landscape and every larger
sheet, but not A5 upright.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Cell text: a value box lies within the paper's coordinates (#3875)
place_lines took any finite, ordered value box, but centring one far out (min x 1e308, max x
1.1e308) overflows: the anchor came back infinite, with no warning. 0xlillith found it in review
of #3877.
The paper scene already bounds every coordinate it holds by MAX_COORDINATE_MM (twenty times
the longest sheet side). That limit is now public, and place_lines keeps its box inside it, so
its anchor is always finite: one rule for what a paper coordinate may be.
Tests: the hostile-box test refuses both overflow cases from the review and a box just past the
limit on each side, and places lines in a box the size of the whole paper limit. The fuzz test's
numbers now include values past the limit (1.1e5, ±1e308).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Title blocks: padding from the lines' inner edges, one label rule, nothing unread (#3875)
A cell's text is padded from the inner edge of its lines: each side of a cell moves in by half
the line drawn on it (the outline's on the block's edges, a cell line's inside), so a wide
outline never covers the text. Before, the padding ran from the line's centre.
The label's one-line rule lives with the cell text rules (`cell::check_label`), where parts
lists will find it. The cell types nothing read yet (`Override`, `Resolved`, `State`,
`Problem`, `Reason` and `MISSING`) wait for the resolver that will read them.
The tests check the exact thresholds inside the lines (a label that leaves no room, a value
that just fits), pick each of the lines, alignment and wrapping on their own so every
combination comes up, and lay fuzzed blocks out on every standard sheet, either way, with and
without the filing margin.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Title blocks: the drawing border's line, descenders, room where the block sits (#3875)
From DevonHope's review of #3877:
- Each cell's lines are clamped to the inner edge of the drawing border's line. That line runs
along the block's right and bottom edges, and along any other edge it reaches, as when the
block fills the border. Aabb2::intersection, the 2D twin of Aabb3's, does the clamping.
- layout checks each cell's room again where the block sits (place_cells, which validate runs
at the origin), so rounding at the real corner can't turn a cell inside out.
- The cell text rule counts descenders, at 3/10 of the cap height (ISO 3098's lettering B).
They may reach into the padding under a value box, never past it to a line. place_lines
warns Height when they would, and validate asks it whether one line fits.
- The module doc no longer links to a private function.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Drawing W06a: exact orthographic hidden lines for the analytic subset (#2455) (#2457)
* Drawing W06a: exact orthographic hidden lines for the analytic subset (#2455)
crates/geom/src/hlr (L0): prepare once per body, then hidden_lines for a
scene of placed bodies. Edges and face silhouettes are projected from their
exact curves, split where projections cross, and each piece is classified by
rays toward the viewer against the trimmed faces: visible, hidden or
explicitly uncertain, never guessed. Planes, cylinders, cones, spheres and
tori are supported whether stored as analytic records, revolutions or spline
patches; freeform faces are reported and make what they could hide
uncertain. Screen geometry is Seg2 with the W01a accuracy vocabulary: exact
lines and conic arcs where the projection is exact, bounded chords
otherwise. docs/drawing-hlr.md holds the contract and its limits.
* geom/hlr: handle the brep records main gained in #3093
Main's exact model storage (#3093) added Curve::SurfaceIntersection,
Surface::PlaneThrough and Surface::PlacedRevolution, and the HLR matches on
the records no longer compiled. A plane through three points is a plane: it
is trimmed and intersected as one, and a box stored that way draws the same
bits as one with axis-stored planes. The procedural intersection curve and
the placed revolution (whose affine placement may scale it unevenly) are
refused by prepare as not supported yet, until the kernel adapter handles
them. The records' positions move with the body's centre like the others.
* geom/hlr: pad plane faces' boxes, so a plane record off by rounding still hides
Plane faces took the box of their boundary chords unpadded, flat along the
normal. When the plane record and its edges disagree by rounding (a unit cube's
top plane one ulp above its edges, a 100 mm cube's 1e-11), the ray toward the
viewer reached the plane just outside that box, the slab test culled the hit,
and the back edges came out Visible with no doubt. Every face's box is now
padded by 1e-6 of its size, as curved faces' boxes already were. Reported by
DevonHope on #2457.
* geom/hlr: limits from review: trim samples, scene extent, crossing pairs, surface groups
From DevonHope's review of #2457:
- Options::trim_samples must be 1 to MAX_TRIM_SAMPLES (4096).
- Placed bodies must stay within MAX_COORDINATE (1e12 mm, as section and brep
bound stored numbers). Placements 1e200 apart made the scene's size infinite
and every tolerance taken from it with it, so everything came out Visible.
- The crossing search counts its candidate pairs before it starts and meets
each pair only in the first grid cell the two segments share, instead of
keeping a set of the pairs met, which could outgrow wasm's 4 GB before the
500 million pair budget tripped. The same pairs in the same order.
- prepare groups faces on one quadric or torus by comparing each face with
the faces of its kind whose size agrees to the tolerance (sorted), not with
every face; at most 50 million comparisons. prepare_cancellable polls a
cancel callback between edges, faces and comparisons.
* geom/hlr: boundary chords on a grid, and no fine pass over coincident curved chords
The cost items of DevonHope's review of #2457:
- Every ray hit asked how far it lies from its face's boundary by scanning all
of the face's chords (4096 per circle; a plate's face with 256 holes has
about a million). The chords are now bucketed on a uniform grid over the
boundary's box, cells at least as long as the longest chord, and the test
looks only at the cells around the point. It finds exactly the chords the
scan finds.
- Chords of two curved curves that lie on each other end to end (aligned holes
seen along their axes) were cut into 16 by 16 pieces and intersected, to
find crossings that are only noise within the curves' tolerance. They are
now skipped; visibility changes where a coincident run starts or ends, and
chords that only partly overlap still find those.
- Fixtures: a plate with holes, coaxial cylinders seen along their axis, and
300 placements.
* geom/hlr: pad boxes by the body's coordinates too; coincident chords only to rounding
Review of the previous commits found two holes:
- The padding grew with the face's size, but rounding grows with where the body
was modelled: a 0.001 mm cube 1e7 mm out, its top plane one step (1.9e-9 mm)
above its edges, still had its hit culled. The pad now adds 1e-12 of the
body's offset.
- Two chords were taken for coincident when their ends lay within each other's
sag, which in a coarse scene can hide real crossings: an arc can bulge across
a line 1 mm off its chord twice. Only chords that coincide to rounding are
skipped now; identical projections, the costly case, still are.
* geom/hlr: coincident curves meeting at shared sample points do not cross
Timing the review's scenes showed that skipping coincident chords was not
enough: chords of two coincident curves also touch end to end at every sample
point they share, and each touch was a crossing to refine and split at. 64
coaxial cylinders seen along their axis gave 8.3 million crossings (19 s), 64
cylinders one behind another along the line of sight 4.2 million (214 s;
before these commits the crossing budget refused it). A touch at a shared
sample point is now dropped when the two curves also coincide, to rounding, on
both sides of it. Where a coincident run starts or ends one side differs, and
the touch is kept.
* geom/hlr: the own-face margin allows for rounding where a body was modelled
The far 0.001 mm cube of the previous commit showed a second effect of records
rounded at large coordinates: its top plane, one ulp (1.9e-9 mm) above its
edges, hid the top edges themselves, because a ray leaving an edge met its own
face 3.2e-9 mm along, past the own-face margin of 1e-6 of the face (1.4e-9).
The margin now also covers 1e-14 of the body's offset (about 45 ulps of its
coordinates), which only matters for parts far smaller than their distance
from the origin.
* geom/hlr: no parity test for a hit on the boundary, where it is uncertain anyway
* docs/drawing-hlr: the fixtures of the review's second round
---------
Co-authored-by: MateuszSychXpl <151755914+MateuszSychXpl@users.noreply.github.com>
---------
Co-authored-by: CamWight <91156642+CamWight@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Benny1496 <163034806+Benny1496@users.noreply.github.com>
Co-authored-by: Mateusz Sych <151755914+SychPL@users.noreply.github.com>
Co-authored-by: MateuszSychXpl <151755914+MateuszSychXpl@users.noreply.github.com>
* claim: #7495
* Flat Pattern: show the whole flat size (#7495)
Each side of the flat size and the thickness get their own row (Flat size X, Flat size Y,
Thickness), so the panel's width no longer cuts the value off after its first side.
* Flat Pattern: the flat size labels in Arabic, Hebrew and Vietnamese (#7495)
* Reject newly collapsed angular reference lines (#8148)
Reject angular solutions that collapse a reference relative to its input length. Cover constrained drags and already-short references. Engine replay and full CI validation remain in progress.
* Cover live and non-live angular drags (#8148)
Adds a replay of #8148 for live and non-live drags. The integration test compiles locally, but the Windows GNU test executable exits with STATUS_ACCESS_VIOLATION before running tests, including with --list/--help. Kept in the draft PR pending validation on a working native runner.
* i18n: add Bosnian (bs), Croatian (hr) and Serbian (sr) catalogs
Full translations of all English messages, the pending ones included,
kept in step with one shared terminology:
- Bosnian (Bosanski): ijekavian, Latin script.
- Croatian (Hrvatski): ijekavian, Latin script, Croatian standard terms
(točka, kut, brid, ploha, značajka, spremi, izbornik, sučelje).
- Serbian (Српски): ekavian, Cyrillic script; brand and file names,
acronyms, units, key names and placeholders stay in Latin.
The Browser panel is "Stablo modela" so it is never confused with a web
browser, and the History panel is the timeline ("Vremenska traka"), not
the undo history, per the glossary.
Registers the three catalogs, adds them to the new-language test, a
pending attribution file, and a terminology note in docs/localization.md.
* i18n(bs, hr, sr): translate the 195 messages added on main since the catalogs were made
Covers the navigation bar customisation, the render stage, the new
command help texts and the Assistant start-up preferences, so the three
catalogs again carry every English message.
* claim: #7181
* MCP save: a folder the agent opened or saved a design in stays in scope (#7181)
The agent's default folders held the open design's folder only while that
design stayed open there, so after a new design, an opened STEP file or a
save-as elsewhere a save beside the earlier design was refused, while the
same save through the control port worked.
The MCP server now keeps, for the rest of its run, the folder of every
design file a call opened or saved (open, save, and doc.open, file.save,
file.save_as through execute or batch, path given in full) and, headless,
of every design open in its session. Such a call only succeeded because the
folder was already in scope, so naming a path adds nothing. The folders get
the open design folder's terms (inside home or temp, not hidden, hidden
entries unreachable), at most 16. Bridged, they go to the app as
design_folders, which it judges the same way.
The save tool description and docs/mcp.md say how to add a folder with
--allow-path.
* MCP scope: a design's folder is the folder its file resolves to (#7181)
A link in another folder to a design inside the agent's folders passes the
scope check, which follows it, but its own folder was what got remembered
(and, for the open design, what counted while it was open). Take the folder
of the file as the check resolves it.
* Control test: a hidden file the export format accepts (#7181)
---------
Co-authored-by: MateuszSychXpl <151755914+MateuszSychXpl@users.noreply.github.com>
* claim: #7635
* Keep the drag cursor on the manifold of an ill-conditioned sketch (#7635)
A construction line collapsed to zero length makes its angle row's entries
on that line ~1e7 while the same row's entries on the measured line are ~1.
Dragging an endpoint of the midpoint-pinned line then failed two ways: the
null-space rank analysis, which scaled rows only, hid the small (physically
meaningful) entries below its pivot tolerance and let the measured line
rotate freely; and the cursor's refinement, which demanded a TOL-tight
correction the ill-conditioned system cannot deliver, gave up entirely and
let the drag leave an unsolvable step placed - breaking the perpendicular
and angle constraints.
Equilibrate the null-space Jacobian on both sides (columns by their largest
entry, then rows) so one row's entries share a scale, and let the cursor
keep working from a pose the solver itself calls solved (residual below
ACCEPT) when refinement stalls above TOL, accepting corrected trials at the
same tolerance the final validation uses.
---------
Co-authored-by: Dahak Builds <dahakbuilds@users.noreply.github.com>
* agent chat: OpenCode Go preset with per-model endpoints
One preset (https://opencode.ai/zen/go/v1) covers every Go model: each
model is routed to its endpoint family (chat completions, responses,
messages, Gemini), always with the x-opencode-session header Go
requires. Unknown models fall through protocols when Go names the
mismatch; the conversation and tools translate per protocol.
* agent chat: keep Go reasoning metadata, route all listed models (#5656)
* fix agent chat Go endpoints to documented inference routes
* style: collapse long assert in Go routing test (cargo fmt)
* refactor: share Go tool/args helpers, table-driven protocol routing (no behavior change)
* refactor: share GoCallAccum across Go reply parsers (no behavior change)
* refactor: share part/call extractors across Go history converters (no behavior change)
* refactor: share post_to envelope between post and go_post (no behavior change)
* refactor: pass system note into go_responses_input, drop history clone (no behavior change)
* refactor: lazy Gemini name map, shared go_tools, named tool-call cap (no behavior change)
* refactor: share shaped-tool wrapper across Go tool converters (no behavior change)
* fix: set request_timeout_s in Go test setups for rebased main (no behavior change)
* fix: expect ToolsAvailable first in tool-loop tests (matches new event order)
* simplify: reuse tool_arguments, drop restater comments (no behavior change)
---------
Co-authored-by: Julius <julius@localhost>
Co-authored-by: Jom_g <jom_g@users.noreply.github.com>
Co-authored-by: HoloPort <holoport@localhost>
* claim: #8065
* Context menu: only the row under the pointer stays highlighted while a flyout closes (#8065)
A row that owns a flyout kept its highlight for the whole of the flyout's
closing delay, so the row the pointer had settled on lit up alongside it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* claim: #7855
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019FAWPuFoh9qxn2uS2j53QZ
* Motion interference: a check cut short by its work budget is not clear (#7855)
motion.interference stops when its work budget runs out (truncated) but
answered clear: collisions.is_empty(), so a check that reached only the first
steps read as a pass: a gear pair checked for 14 of 61 samples was "clear".
clear is now true only when every sample was checked and none collide.
The budget is an argument of interference_within, so a test can cut a check
short: B slides up to touching A, a full check is clear, and the least work
that checks a first step stops after 1 of 16 samples with clear: false (it
was clear: true before). The guide and the mechanisms skill say the same.
The issue's second request, a budget_s on motion.interference, is not here.
Refs #7855.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019FAWPuFoh9qxn2uS2j53QZ
* Motion interference: budget_s gives a check the time it needs (#7855)
The work budget is fixed, so a gear pair drawn in detail was cut short
after a few samples, or refused before its first one. motion.interference
now takes budget_s (seconds, above 0, at most 86400, as inspect.interference
allows): the check then runs until that time instead of the work budget,
looked at before each sample, and echoes budget_s in its answer. Without
budget_s nothing changes. The refusal before a first step now names the
budget that ran out and suggests budget_s.
Tests: given a time, the smallest work budget (which checks no step) no
longer stops the check, through motion.interference too; budget_s 0, -1,
1e9 and "soon" are refused.
Fixes#7855.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019FAWPuFoh9qxn2uS2j53QZ
---------
Co-authored-by: Claude <noreply@anthropic.com>
Collapse All from a component or placement row folds everything under that
row and leaves the row itself open, matching the whole-tree Collapse All,
which already keeps the design row open.
* Standard Parts and Contact Set: label the rows in words, not with raw keys
The Insert Part dialog listed a size's dimensions under the library's
keys (d, head height, socket depth), and the Contact Set dialog's
checkbox read "enabled". The dimensions now take messages (Nominal
diameter, Head height, Socket depth, ...) and the checkbox reads
Enabled, translated like the rest of the dialogs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Contact Set: translate the Enabled checkbox's label
ui_kit::checkbox paints the text it is given; the callers translate it,
as Configuration Rules' Enabled does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The Interface page's strip of tab accent colours looked up its own
word-cased names ("Sheet Metal", "Surface"), which the catalogs
mostly don't carry or carry with another meaning: in German it read
"Sheet Metal" and "Flächenkörper" where the ribbon says Blech and
Fläche. It now names each tab the way the ribbon does, through the
tab's heading message, and the Form tab gets one.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Preferences: the snap-suppress choice and Direct interaction in the user's language
The Hold to stop snapping choice joined its keys with an English "or"
("Cmd or Option", the default) in every language, and Navigation's
Direct interaction setting and its note had no message. The choice now
reads through a message with the keys as placeholders, and the setting
gets its two messages.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Move the new test away from where another open PR adds one
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Dialogs: 54 labels that read English in every language get messages
The construction dialogs' selection fields (First edge, Second point,
Through point, ...), the joint types (Rigid, Revolute, Slider, ...), the
Sheet Metal and Plastic rules' rows (K Factor, Flange Gap, Min Draft,
...) and a few options and prompts are passed to the label translator as
English source text that no catalog carried, so they showed English in
every language. They are added in one pending file, with the eleven
usual translations, and a test makes sure every dialog's selection
fields keep a message.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Dialog labels: German Flange Gap is Flanschspalt, as the catalog's Flansch
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The Italian catalog lacked 1350 of the English messages. This batch
translates the 719 outside the Render workspace: Preferences, the
Assistant, the help texts, command labels, materials, navigation and
the rest. It uses the catalog's own terms (Schizzo, Lavorazione, Corpo,
Giunto, Cronologia, Struttura, Risalto, Appoggio, Aggancio a scatto).
The Render workspace's 600 messages are left for a second batch while
that workspace is still being built.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Insert Part: name the part families in the user's language
The Type list of Insert Part and the Change Part Size dialog showed the
library's English family names (Socket head cap screw, Hex nut, ...) in
every language. Each family now has a message under its library id,
and a family the catalogs don't know keeps the library's name.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Insert Part: the Name field's automatic hint in the user's language
The hint was the English word "automatic"; it now takes the catalogs'
parts.automatic_name, which every language already translates.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Move the family-name helper and its test away from where #8005 adds its own
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Turkish: translate the 113 messages the catalog was missing
The Turkish catalog lacked 113 of the English messages: the newer
Preferences settings, the hole presets, the slicer messages, the
Assistant's start-up options and tool summaries, and the Render
workspace's stage, shots and inspector labels. They are translated in
one pending file, with the catalog's own terms.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Translate the 87 messages main added since (the navigation bar's)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Dialogs: translate the texts a few dialogs draw themselves
The joints' Play/Stop buttons, the Motion Study's Loop and the BOM's
sub-parts checkboxes, the Stitch tolerance hint, the Flange type and the
Emboss wrap note were drawn as English literals; the joint lists named
each joint's type by its Rust name (PinSlot, PointOnPlane). They now take
messages, and the joint lists use the Type box's label, translated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Move the new test away from where another open PR adds one
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The Surface, Sheet Metal, Plastic, Frame, Move, Align, Assembly, Cam,
Coupling, Sweep, Part Size and sketch editing dialogs refuse their
inputs with English sentences ("select bodies first", "pick faces of
one body"). The dialog translates a refusal through the catalogs as it
shows it, but no catalog carried these 96, so they showed English in
every language. They are added in one pending file with the eleven
usual translations.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Everything outside the Render workspace, which is still being built:
Preferences and the new navigation bar, the Assistant, the help texts,
command labels, materials, hole presets, the slicer messages and the
remaining dialog labels, with the catalog's own terms.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Everything outside the Render workspace, which is still being built:
Preferences and the new navigation bar, the Assistant, the help texts,
command labels, materials, hole presets, the slicer messages and the
remaining dialog labels, with the catalog's own terms.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Everything outside the Render workspace, which is still being built:
Preferences and the new navigation bar, the Assistant, the help texts,
command labels, materials, hole presets, the slicer messages and the
remaining dialog labels, with the catalog's own terms.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch: the status in the view and the browser's editing mark in the user's language
While a sketch is edited, the view's corner said "Sketch1 — 2 degrees of
freedom" (or "fully constrained", "constraints conflict") and the
browser marked the sketch "(editing)" or "(attachment lost)", in
English whatever the language; one degree of freedom read "1 degrees of
freedom". They now take messages: the catalogs' existing degrees-of-
freedom ones and four new ones.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch status and editing mark: the sketch's default name as shown elsewhere
The browser shows a default name in the user's language ("Skizze 1")
except on the edited sketch's row, whose mark made it a different text;
the view's status kept the stored name too. Both now use the name as the
browser shows it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Motion Study, Motion Trace, the motion plot, Explode and the contact
check drew their notes and refusals as they got them ("add a key for a
joint first", "select a point on a component", "none"), so they read
English in every language. They now go through the label translator, and
the four that no catalog carried get messages.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Double-clicking a sketch row inside a component (an inserted fastener's sketch)
activates that component so the sketch is edited in its placement. Finish and
Cancel only closed the sketch, so the component stayed active and every other
body stayed faded.
Edit Sketch now remembers the component and placement active before it
activated the sketch's own (also when sketch.edit activates the component of a
face-attached sketch), and leaving the sketch makes that one active again, as
Finish already puts the history marker back. A component activated while the
sketch is open stays active.
Co-authored-by: Phanoob <phanoob@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Maya moves the camera with Alt and any button (left orbits, middle pans,
right zooms), middle drag also pans. HFSS pans with Alt+left drag and zooms
with Shift+Alt+left; its Ctrl+left orbit is the drag from empty space every
preset already has, so a Ctrl+click on geometry still adds to the selection.
The help texts are in the catalog for every locale that has the other
presets' help.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #5592
* Canvas and Decal: send the picked face, so a large design takes the image (#5592)
A click with Canvas or Decal sent only the point, so the engine searched every face of every
body for it and gave up on a large design ("face capture work limit exceeded"). Send the
picked body, face index and path as Create Sketch does; canvas.decal takes that face pick too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Canvas and Decal test: drop the check that a point alone gives up
On upstream main a sketch on a face by its point alone no longer gives up on this design, so
the test keeps only what the picked face has to do: both images land on it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #6039
* Sketch: offer an arc length dimension for a selected arc (#6039)
The arc length dimension already existed in the engine (sketch.dimension with
type arc_length), but nothing in the UI led to it: the Dimension tool and the
context menu always gave an arc its radius.
With exactly one arc selected, the sketch context menu now offers Arc Length
Dimension next to Sketch Dimension, which adds the dimension through the
existing command.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FN5eEMrqWWXqi65FcK8GXq
* test: move the arc length menu test up to avoid a merge conflict
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: sascha2101 <221609842+sascha2101@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #5517
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Right-click opens the menu despite tiny pointer movement (#5517)
A Magic Mouse moves the pointer a fraction of a point as it is clicked.
The right button orbited on any movement, and the menu needed exactly
zero movement in the release frame, so most right-clicks only nudged
the view. The right button now navigates once the press can no longer
be a click (egui's click distance and time), catching up with the
travel since the press, and a right-click is egui's click.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #7947
* Browser: reveal a placed body's row when it is picked or found (#7947)
The off-screen skip in `entry_row` keeps the reveal target's row alive so it
still has a rect to scroll to, but it matched `app.tree.reveal` (a world body
name) against `Entry::key` (the placement-local name). For a placed body those
never match, so the row returned early and the `scroll_to_rect` below it, which
does use `row_key`, was never reached. `app.tree.reveal` is then cleared at the
end of the frame, so there was no second chance.
Match the skip on `row_key` minus its `b:` prefix, as the `scroll_to_rect`
already does.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
The Box, Cylinder, Cone, Sphere, Torus and Coil dialogs labelled their
size rows with the command's parameter names (length, top_diameter,
section_size), in every language. Each row now takes a message: Length,
Top diameter, Major radius, Section size, translated as the rest of the
dialog is.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Fix linked component materials and appearances (#7609)
* Linked components: the cached link read carries the source properties
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Update Links: a link without recorded inherited properties is read again
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Preserve authored linked-face overrides and apply cached source properties
---------
Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Navigation bar: slot model and one-row renderer (#6585)
* Navigation bar: customisation section in Preferences (#6585)
* Navigation bar: icons for visual styles, selection modes and display (#6585)
* Navigation bar: redraw the Bodies, Parts and Components selection icons (#6585)
* Navigation bar: refine the Ground shadow, Ambient occlusion, Parts and Components icons (#6585)
* Navigation bar: solid cube and a longer arc for the Ground shadow icon (#6585)
* Navigation bar: the owner accepts the new icons as final (#6585)
- Leave the running preview behind when the selection moves to another setup, asked to stop
- Keep at most two obsolete previews running; streaming, tools and other inputs wait as before
- Keep no result of a cancelled watched evaluation in the shared evaluation cache
- Test the detaching, the cancel reaching the spawned job, and the cache guard
Co-authored-by: P'tite Tête <asso@ptitetete.com>
#6471 joined the body a To Object extent runs to into the tool, whatever its component.
A connector's boss extruded up to an enclosure's face (each in its own component) merged the
enclosure into the connector, and features referencing the swallowed body failed after it.
The body it runs to now joins only when it is in the feature's own component, or when the
feature names it as a target.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #4217
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Browser: Bodies folders only where they help: Always, Several bodies only (the default) or Never (#4217)
A new preference on the Interface page, in the Layout section under Browser, sets when a
component's bodies sit in a Bodies folder of their own. Several bodies only lists a component's
lone body, when it has no groups, straight under its placement. Never lists every component's
bodies and groups there. Always keeps the folder as before. The design's own Bodies folder and a
folder hidden with its eye always stay.
One rule, browser::bodies_folded, decides it for the rows and for the menu. A folded folder
draws no row and stays open; its items move up one level, and their own eyes, hover and drops do
its jobs. While a folder is folded, its placement's menu takes the folder's Create Components from
Bodies and Group Selected; New Group and Show All were already there.
ui.prefs takes browser_bodies_folders (always, several, never) and refuses other values; saved
preferences read an unknown value as the default. The s37 scenario sets Always so that it still
checks the Bodies folder's chevron.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Browser: Bodies folders in Arabic, Hebrew and Vietnamese too (#4217)
Arabic, Hebrew, Japanese, Vietnamese and Chinese are complete catalogues, so
every_complete_catalog_carries_every_english_message fails a new message any of them lacks. The
preference's four messages get their Arabic, Hebrew and Vietnamese text, in the words those
catalogues already use for a body and a folder.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #2218
* Preferences: search settings across every page (#2218)
A field at the top of Preferences filters the live controls with the same scorer as command search, including a dropdown's current value and the English source of a translated label.
* Preferences search: keep the plain page list for its tests (#2218)
The production dialog always draws the search strip, so the no-header entry is test-only and no longer warns as unused.
* Preferences search: show only the settings that match (#2218)
A page name or a loose string of letters was keeping the rest of that page on screen, including notes and the mouse legend.
* Preferences search: drop stray chrome and clear from the field (#2218)
The colour swatches and folded Advanced heading were painted on every query, and the field had no way to empty itself.
* Preferences search: fold shortcut matches and drop the blank gaps (#2218)
* Preferences search: stop unmatched pages painting over the hit (#2218)
* Preferences search: a page tab clears the search and shows that page (#2218)
* Preferences search: keep OK and the graphics checkboxes in view (#2218)
A dropdown that skipped its real height on a dialog's first frame left OK outside the window, and the search strip used up the rows the graphics page needs. The shortcut fold is in the UI kit gallery.
* Preferences search: type a query and open the match from the keyboard (#2218)
A printable key or Cmd/Ctrl+F focuses Search settings, arrows move through the matches, and Enter opens that row. Escape clears the query before it can close the dialog, and clearing returns to the page that was up when the search started.
* Preferences search: translate the search strings for the complete catalogs (#2218)
Arabic, Hebrew and Vietnamese are delivered complete, so Search settings and the empty result need a translation there. German and Korean get the same two strings.
* claim: #7107
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch: Delete on a picked profile deletes the curves around it (#7107)
While a sketch is being edited, a profile picked by clicking inside a
shape stands for the curves of its outer loop, so Delete removes the
shape instead of failing with "a profile goes with its sketch". Holes
inside it are shapes of their own and stay. Outside the sketch a
profile still goes only with its sketch.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Delete a picked letter's whole text, not just its outline (#7107)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: new designs save as .sc
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* New designs save as .sc; .solidcraft files still open and keep their name on Save
DESIGN_EXT is now "sc"; DESIGN_EXTS lists sc, solidcraft and solvecraft and
every open path (dialogs, drag-drop, recent files, CLI, MCP, links, the web
picker) accepts all three. Save of an opened .solidcraft file writes that
file; Save As, autosave, recovery and crash reports use .sc. Windows, macOS
and Linux register .sc alongside the older extensions.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Update UI tests for the .sc default extension
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Ribbon tooltips: help for the Solid > Modify tools; new toolbar commands need it (#2758)
Writes the two-sentence help for the 16 Modify tools that had none (en, fr,
ja, zh), and adds a test that fails when a toolbar command has no help; the
190 older ones are on a backlog that only shrinks. AGENTS.md, CONTRIBUTING.md
and the Adding a command guide say so.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Ribbon tooltips: the help test reads the ribbon as the toolbar lays it out (#2758)
Review: commands placed by a workspace section without .at(...) were not
checked; 79 of them join the backlog. Physical Material's help no longer
promises the centre of mass.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Ribbon tooltips: French help uses the glossary terms (#2758)
Matériau, Apparence and Scinder, as rlartot asked in review; Delete Face
says the hole stays open.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Ribbon tooltips: the help rule fits the commands skill's size limit (#2758)
AGENTS.md became a router to skills on main, so the rule lives in
skills/solidcraft-commands/SKILL.md, kept under its 3600-character limit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Ribbon tooltips: Arabic, Hebrew and Vietnamese help; the backlog follows main (#2758)
Arabic, Hebrew and Vietnamese are complete catalogs now, so the 16 texts
come in those too. Twelve toolbar commands that landed before the rule
join the backlog; the Render commands, which left the ribbon, leave it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #7371
* Browser with History under it: drag the split between the two cards (#7371)
Under the browser, History took exactly half the column. The seam
between the two expanded cards is now a grip with the vertical resize
cursor; dragging it moves the split, as the column's right edge moves
its width. The split is kept as the browser's share of the column in
a new preference, browser_share, whose default is the equal split, so
nothing changes until someone drags.
Either card keeps at least its header and two rows. Folding either card
works as before and gives the other the rest; while one is folded or
folding there is no grip. A non-finite share falls back to one half.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FN5eEMrqWWXqi65FcK8GXq
* Prefs: keep the browser share with the History-under-browser settings
Moves the entry so it no longer sits next to the render and dialog
preferences that main added after browser_width.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: sascha2101 <221609842+sascha2101@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #7253
* Sketch: deleting a dimension in a rolled-back history no longer rebuilds the features after the marker (#7253)
The check before a delete evaluated the whole history twice when the
timeline was rolled back. Removing a sketch constraint changes the later
features' cache keys, so on a long history Delete on a dimension took
minutes. When the design differs only in sketch constraints (and model
parameters nothing uses any more) and each changed sketch solves to the
same geometry, profiles and ends, the features after the marker see the
same inputs: compare the live models only. Anything else still gets the
full check, so the report is unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The app id moves from ai.storyteller.solidcraft to io.tangencylabs.solidcraft: macOS bundle id
(the CLI signs as io.tangencylabs.solidcraft.cli), Linux desktop/metainfo/MIME file names, icon
name, Wayland app_id / X11 class, Flatpak, Snap common-id, nfpm/FreeBSD/Nix/AppImage staging,
the web app's icon paths and the docs.
Existing installs keep working: the Flatpak repository marks both old ids
(ai.storyteller.solidcraft and ai.storyteller.solvecraft) end-of-life with a rebase straight to
the new id, re-marking the solvecraft ref that pointed at the 0.8 id; the manifest grants
read-only access to both old ~/.var/app folders and the first launch copies settings and
autosaves from the newest one; AppStream <replaces>/<provides> and X-Flatpak-RenamedFrom list
both; the AppImage removes menu entries it wrote under either old id. The data and settings
folders (solidcraft / SolidCraft) are unchanged. The MSI UpgradeCode and ProgIDs are unchanged.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #7203
* Report a bug: Ctrl/Cmd+Enter continues on GitHub (#7203)
The report's fields are multi-line, so Enter adds a line break and the
only way on was the mouse. Ctrl+Enter (Cmd+Enter on a Mac) in any field
now does what Continue on GitHub does: it is read before the fields see
it, so it adds no line break, and an empty description is still asked
for. A note under the description says so, with the keys of this
computer.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Report a bug: keep the shortcut hint apart from the other report changes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Report a bug: holding Ctrl/Cmd+Enter continues on GitHub once
egui's consume_key also matches the key's repeats, so every repeat frame
opened another GitHub tab. Only the press counts now; repeats are dropped.
The test sends the modifiers each platform really sends (mac_cmd or ctrl
together with command) and holds the key.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Locales: Arabic and Hebrew for the new strings
ar and he are complete catalogs on main now (#3458): every new message
needs them. The pending-messages test compares right-to-left messages in
display order once #7532 lands (#7527).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Locales: Vietnamese for the new strings
Vietnamese became a complete catalog on main (#5823), so every new English
message needs it too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Report a bug: the Save screenshot test scrolls its button into view
The shortcut hint adds a line above the screenshot, which moves its
buttons below the fold of the review's scroll area at the test's size.
The test scrolls them into view first, as the other report tests do.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Save as Mesh: Add all bodies and Add visible bodies buttons
Two buttons under the Bodies input fill it with every body of the design,
or with only the bodies the viewport shows.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Save as Mesh: translate the two body buttons for every complete catalog
ar, he, ja, vi and zh are complete catalogs, so every English message needs
an entry there; ko as the ui-text skill asks.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* claim: #6828
* Viewport: clear new resolve targets so AMD Direct3D 12 shows the scene (#6828)
With anti-aliasing above 1x the scene is drawn multisampled and resolved
into textures that nothing but the resolve writes. wgpu counts the resolve
as their first write and never clears them. Direct3D 12 requires a render
target placed in reused memory to be cleared, discarded or copied over
first, and some AMD Radeon drivers then show the uncleared texture as
transparent, a flat colour or speckles: no grid, origin planes or bodies
(#6828, #7157, #6613, #7052). Each new resolve texture (the frame's colour
and the see-through sums) is now cleared once when it is made.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #7502
* Assembly: name placed bodies from a per-design index, reveal the Browser from the body's path, evict cache entries singly (#7502)
* Release the placement index before in-place document edits so pinning body names stays in place
---------
Co-authored-by: Dahak Builds <dahakbuilds@users.noreply.github.com>
A sketch on a face of a placed component is evaluated in that component's frame, so the
evaluator moves (and meshes) the bodies placed differently into it. FrameBodies keeps those
moved bodies, but every evaluate started a new generation and dropped what neither it nor the
evaluation before it used. Evaluations that reuse every result never touch the cache, so two
of them in a row emptied it: Delete refreshes the model three times (selection.delete's nested
refresh, its own, then execute's), and the confirmation's dry run more, and an undo answered
from the cache is another. The next edit of the sketch then moved and meshed every placed body
again: in a design of ~3,500 imported bodies each sketch edit after a Delete took 46 s, 34-43 s
of it tessellation.
The generation now starts on the evaluation's first use of the cache, so an evaluation that
moves nothing leaves it alone. It still holds at most two generations of moved bodies.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #6380
* Cylinder: place by distances from reference edges, like Hole (#6380)
A Cylinder standing on a flat face can take up to two straight edges of
that face as references, each with the distance of its base centre from
the edge's line, measured in the face's plane, as a Single hole does
(#37). `solid.cylinder` takes them as `references: [{edge, distance}]`
with a numeric `base`; the edges are found again by their persistent
names, else their points, so the cylinder follows them when the part
changes, and each distance is a parameter (an input of Inspect Feature).
A cylinder without references stays at its base, as before.
In the Cylinder window, started on a picked face, Reference 1 and 2 take
the edges, each with its Distance measured from the pick, so it works
with the mouse alone; the canvas shows each distance with a box to type
it in, and Edit Feature picks the edges again.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Cylinder: Edit Feature shows each reference edge input once (#6380)
A face selected when Edit Feature opened the window had already added
the reference inputs; the edit added them a second time.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Cylinder references: the measured-once cost test's cylinder has none (#6380)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Cylinder: pick the face it stands on in its window, then the edges (#6380)
The Cylinder window took its place only from a face selected before it
opened, so started first it stood at the origin and offered no reference
edges. It now has a Face input before Reference 1 and 2, as the Hole
window does: start Cylinder, click the face, click the edges, OK.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Glossary: reference edge, and "Reference" among the words with more than one meaning (#6380)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Cylinder edge references: the Cone dialog (#6250) gets the references field too
The Cone primitive landed on main after this branch began; its dialog initialisers lacked
the `refs` field the Primitive kind gained here. The dense form check for Cylinder fills only
its fields: its face and reference edges place it, which the reference tests cover.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Cylinder: declare references in its contract; reference-placed cylinders are not placement-independent
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
* Navigation: Free orbit can turn around the vertical axis (#6964)
A new option under Free orbit, Turn around the vertical axis, makes mouse
drags a turntable without the pole limit: sideways turns about the world's
up axis (reversed upside down so the model follows the cursor), up/down
tilts over the top. Off by default; free orbit without it is unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* i18n: Japanese and Chinese for the vertical-axis orbit option (#6964)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* i18n: Arabic, Hebrew, Vietnamese, German and Korean for the vertical-axis orbit option (#6964)
Main now treats ar, he and vi as complete catalogs, so the merge queue's
every_complete_catalog_carries_every_english_message failed on the two new ids.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhpMr6L9PR8SJXjM6MbQrD
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: danieldimitrov86 <danieldimitrovdev@gmail.com>
* claim: #7756
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* File dialogs open in the folder used last (#7756)
The desktop dialogs never said where to open, and the Linux ones then
start in the home folder every time. Open, Insert, attach, Save As and
Export now open in the folder of the file last chosen in a dialog, or of
the open design when another design becomes the open one, whichever came
last. Also fixes#7735.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #5769
* Let body appearances replace imported STEP face colours (#5769)
A body appearance assigned in the design now replaces the face colours the
body was imported with; face appearances set in the design still win over it.
* claim: #7567
* Sketch: a drawing tool's value box takes `name = value` (#7567)
While drawing, a value box (circle diameter, line length, rectangle width and
height, slot, arc, polygon) now accepts `name = expression` and
`#name = expression`, as the Dimension tool already does: the shape is drawn at
the expression's size and its dimension defines and uses the user parameter
`name`, in one undo step.
- sketch.draw.dimensioned checks a definition's right-hand side, read as the
new parameter will hold it (a bare number in the design's unit), and passes
the text on to sketch.dimension; the reply lists `defined`.
- The value boxes size the preview and validate by the right-hand side, refuse
a definition in a box that is not a dimension (polygon sides, rounded
rectangle corner radius, conic rho), and the status line names the
parameters defined.
- Guides: parameters and sketching.
Fixes#7567
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #7296
* Viewport: a frame the GPU has no memory for is drawn smaller, not a crash (#7296)
The viewport's offscreen frame, its see-through targets and the cached
still picture were created with no error scope, so a texture the card had
no video memory for went to wgpu's default handler and closed the app
("wgpu error: Out of Memory" on a GT 740). They are now created under error
scopes; a refused frame lowers the renderer's frame budget and is asked for
again smaller (less supersampling, or the view's own frame drawn smaller and
filtered up), refused OIT targets fall back to sorted see-through surfaces,
and the status line says once that the view is drawn at a lower quality.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Locales: Arabic and Hebrew for the new strings
ar and he are complete catalogs on main now (#3458): every new message
needs them. The pending-messages test compares right-to-left messages in
display order once #7532 lands (#7527).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* GPU memory test: targets take the OIT flag main added (#7296)
The test's two GpuTarget initializers predate the oit field and no longer
compiled once the branch met main.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Locales: Vietnamese for the new strings
Vietnamese became a complete catalog on main (#5823), so every new English
message needs it too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #6003
* Save: a design changed while it is written stays unsaved, and scripts wait for it (#6003)
Two of the gaps @raykholo found in #6003 were still open on main:
- `engine.script` ran while the window's Save was being written: it calls
the session directly, past `SolveApp::run`'s file-operation guard, and
`drain_control` did not hold design-changing requests for a save. They
now wait for the save like they wait for a file being loaded or picked.
- `adopt_save` marked whatever the session held when the save finished as
saved. It now takes up the state the save wrote (`done.saved`, and its
visibility), so a change made meanwhile stays unsaved instead of being
lost on close without a prompt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FN5eEMrqWWXqi65FcK8GXq
---------
Co-authored-by: sascha2101 <221609842+sascha2101@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #6570
* test(text): cover nonpositive pitch and format-12 glyph zero (#6570)
Original synthetic font regressions. Focused native font tests, strict scoped Clippy and wasm pass. Full repository gate status is recorded in PR #6574; no CI infrastructure changes.
* Sketch: switch a circle or arc dimension between radius and diameter (#7499)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch: Toggle Radius / Diameter for a circle's or an arc's dimension (#7499)
New command sketch.toggle_diameter, with input and output schemas, and a
context-menu item on a single selected radius or diameter dimension. The
circle keeps its size: the dimension's parameter is halved or doubled (a
plain value is rewritten, an expression is wrapped). Refused, with nothing
changed, when anything else uses that parameter.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Phanoob <phanoob@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #7741
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Move: a copying Move can be edited from the History (#7741)
Editing a feature runs its command on a scratch session with the marker
right after it. For the last feature that marker sits at the end of the
timeline, and place_feature pushed the new feature after it, rolled back,
so a copying Move found no copies and refused the edit. A marker right
after the last feature now moves on past a feature added there.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #7749
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Delete: deleting one instance of a component takes its joints with it (#7749)
Deleting an occurrence while its component had other instances dropped
only the occurrence: the joints on it, their motion links, joint origins,
contact set places and exploded moves stayed, so a joint stayed in the
Browser naming an occurrence that was gone. The clean-up that
remove_component_tree did is now Document::remove_occurrences, used by
both.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #5638
* Box dialog: place the box by its corner or centre (#5638)
The Box dialog gets a Placement choice (Corner or Center) with X, Y and Z
fields that take expressions and units, and a Point input: a vertex, a
point on an edge or face, or the origin picked in the view fills them.
The Cylinder dialog gets X, Y and Z for the centre of its base. Left
alone, a new dialog sends no placement, so the box stays at the origin;
Edit Feature shows the stored point and OK without a change keeps it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Box dialog test: pass the active feature to Input::accepts
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Box dialog: Base Center in the complete Arabic, Hebrew and Vietnamese catalogs (#5638)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
The native codec gave one body back with other topology: a pattern copy
cut by an automatic extrude, 10 faces, 18 edges and 10 vertices, came
back with 12 vertices, so the design opened with another body than it
was saved with. Encoded::of now decodes each body it encodes and keeps
the model only when every body comes back with the same face, edge,
vertex and shell counts; otherwise the design is saved without its
model, as when the encoder refuses a body, and opening it evaluates it.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #6707
* feat(dialogs): open in the left column in place of the browser and History (#6707)
* fix(i18n): Vietnamese messages for the dialog placement option (#6707)
* claim: #6525
* perf(eval): reuse local geometry across placement changes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(engine): preserve recovery journal through shared refresh
The shared evaluator retains the document Arc, so refresh and body-name
pinning can copy it after command_done recorded its address. The next read
then mistakes that copy for an outside edit and discards the replay.
Record completion after refresh, name pinning or failed-command restoration.
Add a minimal journaled box/inspect regression in the existing crash suite.
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #7009
* Assistant: connect the last-used agent and open the panel at start (#7009)
Two Settings > AI options, off by default: when SolidCraft starts, connect
the agent started last (once the installed agents are known; a missing one
is not swapped for another) and open the Assistant panel. Not for runs
started with --control.
A connection that never gets ready (no agent, start error, the agent ends
while starting) is a new Failed state: the header reads Connection failed
and Connect reads Retry. The control channel reports it as "failed" and
takes connect_at_start / open_at_start.
* Assistant: keep the start-up agent when a design replaces the blank one (#7009)
Opening a design over the blank design SolidCraft starts with reset its
chat and stopped the agent connected at start. A chat not asked anything
yet now starts over in place and keeps its agent. Turning AI features
off also drops a start-up connection still waiting for discovery.
* i18n: Arabic, Hebrew and Vietnamese for the start-up options (#7009)
ar, he and vi are complete catalogs on main now, so every new English
message needs them.
---------
Co-authored-by: MateuszSychXpl <151755914+MateuszSychXpl@users.noreply.github.com>
Hovering or picking a face in the view lights the feature that made that face (its
creator, as Edit Feature on a face finds it: the hole, not the plate it was cut into)
in the bottom timeline, the History on the left and the Browser's Features folder;
edges and vertices light the feature that made their body, as before. A new face or
edge pick scrolls the history shown to that feature once, through the Find in
Timeline reveal.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Update Links reads again only the linked designs that changed (#5606)
Update Links read, evaluated and STEP round-tripped every linked design on every run. Remember a digest of each linked file, of the STEP it gave and of its face names, and skip a link whose file, bodies and face names are all unchanged. Read a linked design through its saved model when the file has one, as opening it does.
* Drop an import the rebase left unused
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #7350
* Report a bug: Retake rebuilds the details instead of refusing, and keeps the screenshot (#7350)
Once anything changed with a report open, every Retake refused with "Settings or model
changed. Close this draft…", and each refused, waiting, timed-out or stale capture deleted the
screenshot the draft had. Retake now rebuilds the technical details for the current view
(keeping what was typed, edited details and a crash draft's body) and re-baselines before it
captures; the screenshot is replaced only when a new frame is accepted.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Locales: Arabic and Hebrew for the new strings
ar and he are complete catalogs on main now (#3458): every new message
needs them. The pending-messages test compares right-to-left messages in
display order once #7532 lands (#7527).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Locales: Vietnamese for the new strings
Vietnamese became a complete catalog on main (#5823), so every new English
message needs it too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The default view sends the single pixel's ray through a box edge, which the
raytracer rejects as an ambiguous interface normal. The job then ended Failed
whenever the render thread got ahead of render.cancel (about 7% of runs under
load), and the test only accepted Cancelled or Completed. Render off-axis so
the outcome no longer depends on that race, and print the phase and error if
it ever fails again.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #7148
* Lost GPU: a dropped frame no longer crashes the window's close (#7148)
On a lost device wgpu's present and discard return early and leave the
acquired frame's image in the surface, holding a swapchain acquire
semaphore. Dropping the surface later (the window closing for the
restart after the loss) then panicked in wgpu-hal. The painter now holds
the frame as AcquiredFrame, which destroys a second handle to the
frame's texture after the frame is presented or dropped; wgpu-core's
destroy of a surface texture takes the image back without checking the
device, and does nothing once a present or discard took it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #7077
* Repeat Last Command in command search, with a key you can change (#7077)
Shift+R already started the last tool or dialog again (#3045), but it was a
key the app kept for itself: command search could not find it and Keyboard
Shortcuts could not change it. It is now the command edit.repeat, with
Shift+R as its default key. A call through the engine is refused, because
only the app has a tool to start.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Locales: Arabic and Hebrew for the new strings
ar and he are complete catalogs on main now (#3458): every new message
needs them. The pending-messages test compares right-to-left messages in
display order once #7532 lands (#7527).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Locales: Vietnamese for the new strings
Vietnamese became a complete catalog on main (#5823), so every new English
message needs it too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #6533
* Sketch: add points to an existing spline (#6533)
A spline could not take more points after it was drawn. The new command
sketch.spline.insert_point inserts a fit point on the curve where it
passes nearest a spot, between its neighbours, or adds a new first or
last point. Existing points keep their indices, so constraints stay. A
right-click on a fit point spline offers Add Spline Point at the clicked
spot. Control point splines are refused for now: with uniform knots one
more control point would reshape the whole curve.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Locales: Arabic and Hebrew for Add Spline Point
ar and he are complete catalogs on main now (#3458). The pending-messages
test compares right-to-left messages in display order once #7532 lands
(#7527).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Locales: Vietnamese for the new strings
Vietnamese became a complete catalog on main (#5823), so every new English
message needs it too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #7219
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* History: delete a folder with its features, and suppress a whole folder (#7219)
`timeline.delete` and `timeline.suppress` take `groups: [timeline folder ids]`:
every feature in those folders and their nested folders, in one undo step.
A delete takes the folders themselves too; `timeline.ungroup` still keeps the
features. The History on the left offers Suppress/Unsuppress Selected Folders
and Delete Folders and Features… (asking first, like Delete Selected
Features…, when something outside needs them); a folder whose features are
all suppressed is drawn faint.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* History folder actions: Arabic, Hebrew and Vietnamese strings (complete catalogs)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #6981
* Drive Joints: say why a rigid joint can't be driven and offer Edit Joint (#6981)
Drive Joints opened on joint 0 when no joint moved, and its preview ran
joint.drive on that rigid joint, so the dialog showed a red "a rigid joint
does not move" with a Create Issue button.
The dialog now says a rigid joint holds its parts still and how to let them
move (Revolute to rotate, Slider to slide, Cylindrical for both), with an
Edit Joint button that turns it into Edit Joint on that joint. Its commands
refuse a rigid joint, so nothing is previewed and OK stays off. A rigid
joint picked in the browser opens Drive Joints on it to say the same; the
joint box lists only joints that move. joint.drive's own refusal names the
types that move and joint.edit.
* Drive Joints: the rigid joint message in Arabic, Hebrew and Vietnamese (#6981)
Main now delivers ar, he and vi complete, so every new message needs them.
---------
Co-authored-by: MateuszSychXpl <151755914+MateuszSychXpl@users.noreply.github.com>
* claim: #7395
* Sketch: a circumscribed polygon stays regular when dragged (#7395)
Circumscribed Polygon held its sides equal and tangent to the support
circle only. Equal sides round one circle leave every other corner free
(a rhombus round a circle), so a hexagon had 9 degrees of freedom instead
of 4, and dragging its centre or a corner pulled it out of shape (radius
10 became 8.02, angles 174/66 degrees).
Its corners now also lie on a construction circle concentric with the
support circle, which makes it regular as an inscribed polygon is, and
one side is tangent to the support circle, which sets its size: dof 4
(centre, size, turn). One tangent rather than all of them, so no
constraint is redundant.
Test in sketch_ux_tests: 4, 5 and 6 sides, dragged by the centre and by
a corner, stay regular with their sides on the support circle.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
A point a distance from another point on the same line has two places on it, the same distance
either way. A solve after a value edit or a link move could land on the other one and report the
sketch solved, with no warning: the point jumped to the other side of its anchor, twice the
distance away. Such a distance is now an orientation the solves keep, like an unsigned offset's
side: which way along the line the one point lies from the other. One point must be on the line
by a constraint (an end, a point held on it, or coincident with either); the other may lie on it
by what else holds it (the mirror image of a point on it across a line square to it), read where
it is in the reference state.
Test: a point 3 mm along a linked rail from a point held on it, or mirrored onto it: the image
back past that point counts as a flip, and the rail walked far keeps the side.
Fixes#7506.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #6804
* Sheet metal: a base or contour flange hides its sketch (#6804)
Only an Extrude or a Revolve counted as using its sketch, so the sketch of a
base flange stayed shown. Its line on the plate's bottom edge lies exactly on
that edge and took the Flange dialog's click: a sketch line makes a contour
flange, a separate flat wall with no bend whose Flip moves it into the plate.
A base or contour flange now uses its sketch as an Extrude does: hidden unless
shown on its own, and shown while its feature is edited.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FN5eEMrqWWXqi65FcK8GXq
* Sheet metal: test that a click on the bottom edge makes an edge flange (#6804)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FN5eEMrqWWXqi65FcK8GXq
---------
Co-authored-by: sascha2101 <221609842+sascha2101@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The merge queue ran the four-shard GPU path tracer tests (about 56 runner-minutes)
on every group. Now they run when the diff touches render code (crates/render,
crates/gpu/src/path, the render workspace UI, assets/render), or a pull request
carries the new render-tests label (a pull request or a group holding one). The
nightly and manual runs still always run them; a push to main does not.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Typed contracts for the cylinder, sphere, torus, pipe, bounding solid and scale
Contracts for solid.cylinder, solid.sphere, solid.torus, solid.pipe, solid.bounding_solid and
solid.scale, with `FeatureResult` output, and the shared types `AxisDir` (calling
`cmd::axis_dir_form`, split from `axis_dir_param`), `NumberExpr` and `ScaleFactors`. The
descriptions say what the evaluation does: the limits it enforces, the real default scope of
`targets`, and where `participants` overrides a key or has no effect. Scale makes no body, so it
declares `name` and `participants` itself. Box's descriptions get the same corrections. The
reply test runs every form in Warn and checks for no `param_warnings`; the guide lists the new
types and adds lessons from the conversion.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Typed contract for the cone
`solid.cone`, added on main beside the primitives this PR converts, gets its contract and
`FeatureResult` output: the cylinder's keys plus `top_radius` / `top_diameter`, with the
evaluation's limits in the descriptions (a radius above 0, a top radius of 0 or more, a height
that is not 0 and may be negative, sizes within 1e7 mm). A `null` top radius stays the handler's
to refuse, since the handler takes it beside a `top_diameter`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #4860
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Graphics: the window no longer waits for Direct3D 12 shader compiles (#4860)
On Direct3D 12, wgpu compiles every pipeline's shaders with FXC when the
pipeline is made, and nothing is kept between launches, so the window
waited seconds for the viewport.
- The GPU tessellation shader, most of that time, compiles on a thread of
its own. Bodies are built on the CPU until it is ready, then drawn from
their plans; where it never compiles they stay on the CPU, as before.
- The viewport's pipelines build in parallel, one job per fragment shader
(solidcraft_gpu::concurrent). A job that errs, panics or cannot start
runs again on the calling thread, so errors reach the caller's scopes.
Threads only on Vulkan, Metal and D3D12 with FXC.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #7327
* Trackpad: inertia stops when Shift changes mid-orbit (#7327)
macOS sends the inertia after the fingers lift as its own scroll bracket,
with the modifiers held at the time. Each move was routed by its own
modifiers, so releasing Shift while an orbit coasted turned the rest of
the inertia into a pan (and pressing Shift turned a coasting pan into an
orbit). A bracket that starts as the fingers' one ends now keeps the
fingers' mode and ends when the modifiers change. Switching between pan
and orbit while the fingers are down works as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #7225
* fix: headless pictures no longer keep the previous picture's highlight
Capturer::rgba_highlighted left the highlight slot empty when a picture had
no highlight. The renderer keeps its last highlight scene while the slot is
empty, so a selected edge's band drawn for one picture showed in the next
one on the same capturer. The depth tests share their capturers across test
threads, so they failed at random: an edge "192 of 612 px drawn", a washer
showing through its plate, a cut showing what lies behind it. A picture now
always gets a highlight scene of its own, empty without one.
Clearing the leak showed that a_selected_edge_on_a_grazing_face_draws_whole
passed at 88 degrees in a design of radius 30 only by counting the leaked
band: its reference line lies 143 mm behind the sheet's middle, past that
design's far plane, and drew nothing. The small design now holds the edge.
New test: a_picture_without_a_highlight_shows_none_of_the_one_before.
Full `--test depth` runs on Apple silicon: 10 of 10 failed before, 0 of 20
after.
* claim: #7274
* Renaming an imported body with numbered siblings evaluates nothing again
Since #2467, Model::rename_body evaluates the design whenever another body's
name is worked out from the renamed one's (`X (2)`). Every STEP import whose
solids share a label has such names (`Part`, `Part (2)`, ...), so renaming
one of its bodies re-read the import and meshed every body again.
body.rename keeps the names of a body's siblings and of later bodies named
after it, so for a body no later feature changes (the same mesh from the
feature that made it to the end), renaming it in the model gives the names
evaluating the renamed design gives. That case is followed again, as before
#2467; bodies later features change still take #2467's checks.
* claim: #7192
* Web app: Report a bug saves its screenshot as a download (#7192)
Save screenshot… wrote the PNG with image's save, that is std::fs, which
the browser doesn't have. The screenshot is now encoded in memory and
written through vfs, as every other save is, so the web app hands it out
as a download. Natively the same file is written and ui.screenshot
replies as before. A failed save says why in plain text instead of a
Rust debug value.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Web app: a screenshot without a path no longer panics (#7192)
`ui.screenshot` without a `path` saved to `std::env::temp_dir()`, which panics on
wasm32-unknown-unknown (std has no file system there), and the web app's control
requests come through `window.solidcraft.request`. `vfs::temp_dir()` now returns
`vfs::BROWSER_TEMP`, a folder of the in-memory files, in the browser, and
`save_screenshot` and `vfs::design_folder_refusal` (reached when a request names
`allow_paths` while a design is open) use it. Natively nothing changes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Web app: keymap and theme exports download (#7192)
Both exports wrote with `std::fs::write`, which always fails in the browser. They
now write through `vfs::write` like the other saves, so the web app hands the file
out as a download. Natively the file is the same; an agent's folder scope now
applies to them as to every other write.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Tests: say what the folder-scope checks of the saves show (#7192)
The screenshot test wraps the write in a folder scope by hand: that proves the write goes through vfs, not that ui.screenshot's write runs inside the request's scope. ui.screenshot checks the agent's folders when the request arrives; the PNG is written in a later frame, outside that scope, as on main.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The seat of a subscribed connection on the DesignWatch lived in the
connection's serve thread. It left only when that thread saw the end of
the stream, so one unbounded blocking read stood between a closed client
and the count. Under load (GPU backend probing and crash-dump children
on Windows) that read can sit for seconds; the test's 10 s deadline then
expired and `!WATCH.subscribed()` failed (#7226).
Move the seat onto the events thread. The thread ends on `stop`, or on
the first write a closed client refuses, so the next publish retires the
seat. The test now publishes while it waits, and its wait no longer
depends on the serve thread.
Neither theme set the File view's rail colours, so SolidCraft derived the rail from the accent and
the start page's whole sidebar was the primary orange, against the design system's one-or-two
ember budget (#4868 derives an unset rail from the accent). The rail is now n0 (void, beside the
app bar), its hovered and open row n3 (raised), and its text n11.
Session::refresh evaluated the model and then solved the joints, which
writes the solved placements into the design. A feature that reads where
a part is placed kept the result made for the placement before the
solve: a mirror of a jointed part's body, made from the root, ended up
4 mm below the part after a move the slider took back.
When the solve moves parts, the model is now evaluated again for the
placements the design holds, and the joints solved for that model, at
most three rounds. Features that don't read placements come from the
cache.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
A tangency at a point the two curves share (or a line's end held on the round) is solved as a
block of its own, and the orientations a solve keeps left it out. When a point placed by
parameters moved far, the line could land on the other tangent from its far end (its mirror
image) with every constraint met and no warning. It now keeps the round's side of the line, as a
tangency away from the ends already does: kept where the move can be walked there, reported as
a flip where it can't.
Fixes#7428.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #7206
* Browser: a body's name hides the body, not a sketch of the same name (#7206)
`browser.visibility` looked a key up as a feature first, so when a sketch
had exactly the name of a body (a sketch named after the body it makes),
the body's eye in the browser hid and showed the sketch instead, and the
body could not be hidden at all. The browser names a sketch by its id and
a body only by its name, so a name that is exactly both now names the
body. The sketch still hides and shows by its id; matches that differ
only in case are left as they are (#6967, #7004).
Fixes#7569 too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FN5eEMrqWWXqi65FcK8GXq
---------
Co-authored-by: sascha2101 <221609842+sascha2101@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #7131
* feat: Complete the French UI catalog (#7131)
- Add the 1,153 messages missing from fr.json as a pending fr block
- Keep the terminology already used in fr.json and French typography
---------
Co-authored-by: P'tite Tête <asso@ptitetete.com>
* claim: #7171
* Report a bug: take the screenshot once the preview is done instead of giving up (#7171)
A report opened while a preview was being computed took no screenshot
and asked to be reopened, which loses what was typed. It now waits for
the preview with the report open and takes the screenshot as soon as it
is done; after 10 s it shows the preview unfinished and says so in the
report (a hanging preview is often what is being reported).
The preview the report waited for belongs to its context unless the
design, the dialog or the selection changed meanwhile, so the retake
check compares those alone. What is typed while the capture hides the
report waits for it, and its focused field gets the focus back.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Report a bug: keep the new helpers apart from the other report changes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Report a bug: keep what is typed as the waited-for screenshot is taken
The screenshot a report waits for starts in the frame the preview
finishes, before the form is drawn, so what was typed in that frame went
nowhere. It is now held with what comes while the form is hidden. The
report's context also leaves the dialog's own state out: a preview error
the dialog shows meanwhile (a sketch fillet's) no longer counts as a
change that refuses the screenshot, since it is often what is reported.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #7188
* macOS menu: disable design commands behind an open Report a bug draft (#7188)
The native menu bar kept running its actions while a Report a bug draft was
open, although the app's own toolbar and shortcuts wait behind it: Edit > Undo
or Cmd+Z removed the last feature, so the report described a design that no
longer existed. FileAction::enabled now refuses every action but Quit while a
draft is open. Settings, Keyboard Shortcuts and Agent Chat are refused too:
their windows open over the report but take no input until it closes. The
menu's Undo and Redo still go to a focused report field
(FileAction::menu_enabled), which no longer leans on Settings being enabled.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Menus: Quit and Help's windows wait behind an open Report a bug draft
Quit switched to the first design with unsaved changes under the draft and
asked about it in a prompt that can't be answered while the report blocks
the app; with nothing unsaved it quit and lost the typed report. Like any
modal window, the draft now turns Quit off. Help's items that open a
window (About, Keyboard Shortcuts, Messages, Crash Reports, Connect AI
assistants) wait too, in the native menu and in the app's Help menu;
Documentation and Report an Issue… stay.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #7190
* Report a bug: focus the empty description once, not every frame (#7190)
After Continue on GitHub found What happened? empty, the form moved the
focus back to it on every frame, so typing in Summary went into the
description. Continue now re-arms the one-time focus (and scroll into
view) instead; the other fields stay free to type in.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #1156
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Startup tests: start the backends the app would, no OpenGL driver on Windows (#1156)
The software-adapter test enumerated every wgpu backend and the probe test
asked OpenGL on its own even where the app stops at D3D12, so both loaded the
GPU vendor's OpenGL driver (atio6axx.dll on AMD) into the test process, where
it once aborted the process at exit with c0000409. The adapter test now starts
what the software renderer starts (D3D12 alone on Windows) and the probe test
probes in the app's order, stopping at the first backend with a GPU.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Startup tests: read the software renderer's backends from wgpu_options
Review: the adapter test now takes the backends from the app's own
wgpu_options(true, None) instead of a copy of its choice, and the probe test
no longer re-checks the order choose_backends' own test covers.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #7281
* cargo xtask skills: check the files as Git stores them, on a CRLF checkout too (#7281)
A Windows checkout with core.autocrlf gives AGENTS.md and the SKILL.md files CRLF line endings,
so `front_matter` found no `---\n` and every skill was reported, and each line's `\r` counted
towards the size caps. The check now reads the files with CRLF taken as LF, as #4960 did for
icon_audit. The new test checks a CRLF skill filled to the cap.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #7299
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch: a circle centred on an existing point shows its Coincident glyph (#7299)
The centre circle tool sent a centre snapped to an existing point as that
point's id, so the circle shared it: attached, but with no constraint and so
no glyph to see, pick or delete. A plain point snap now goes as its place,
where `connect` ties the new centre to the point with a Coincident the sketch
draws. Midpoint, on-curve, crossing and body-vertex snaps still go as refs,
so the centre stays held there (#3099 keeps passing).
Fixes#7299
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Phanoob <phanoob@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Claim: centre arc on an existing point shows its Coincident glyph (Refs #7299)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch: an arc centred on an existing point shows its Coincident glyph (Refs #7299)
The Centre arc tool sent a centre snapped to an existing point as that point's id, so
the arc shared it with no constraint and the sketch drew no glyph. A plain point snap
now goes as its place, where draw's connect ties the new centre with a Coincident;
midpoint, on-curve, crossing and body-vertex snaps still go as refs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Phanoob <phanoob@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #7156
* Highlight the selected sketch in the viewport (#7156)
Picking a sketch in the Browser or the timeline selected it, but the
viewport lit nothing: the highlight only drew the bodies and faces a
feature made. It now draws all of a picked sketch's curves in the
selection colour, over the model and also while the sketch is hidden,
so a sketch is easy to find without hiding and showing it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #7348
* Report a bug: include the Messages log and other failing features (#7348)
About half of the reports say "No command error was recorded" and often
can't be reproduced. The error was often in the Messages log, or on a
feature that was neither selected nor the latest. The technical details
now list every failing feature (id, name, kind, error; at most 10) and the
last 8 messages with level and age. Each entry is one quoted line of at
most 300 characters, with the home folder shown as ~.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #6977
* Open from computer: let iOS and iPadOS pick .solidcraft files (#6977)
The iOS file picker matches an input's accept list by system file type,
not by extension. No system type has the .solidcraft extension, so the
designs were greyed out. On an iPhone or iPad the file inputs now offer
every file; other devices keep the extension filter.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #7007
* A surface left without a configuration is configured again, not a crash (#7007)
wgpu reports device-loss errors to no error scope. On D3D12 a driver reset
found by Surface::configure drops the old configuration and reports nothing,
so the surface was taken as configured and the next acquire panicked with
"Surface is not configured for presentation". A configure that meets a lost
device or an unsupported surface is now a refused configure, the acquire runs
under an error scope, and its Validation status reconfigures the surface.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
model_render_follows_the_grid_and_origin_switches_unless_asked compares nine
renders byte for byte and has failed once in a full run without coming back
alone. A mismatch now names the step, counts the differing pixels, lists the
renderer that drew each picture and keeps both PNGs.
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The value box beside a command's geometry and the placement fields kept a number's width, so a
formula or parameter names scrolled out of sight while typing. They keep that width for a number
and grow with the text, up to three times it.
* claim: #7166
* Toolbar: a panel with no buttons keeps one that opens its menu (#7166)
Removing New Sketch from the Model tab (Remove from Toolbar, or a drag off
the toolbar) left the Sketch panel empty above its label, so Sketch seemed
to be missing from the toolbar. A panel whose buttons are all gone is now
drawn like a collapsed panel: its first command's icon over a caret, which
opens the panel's drop-down, in the same room.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #7643
* Web app: Import… reads shortcuts and theme files through vfs (#7643)
Import… on the Keyboard Shortcuts page and the theme strip's Import… tile
read the picked file with std::fs::read_to_string. In the browser a picked
file is kept in vfs's in-memory files under /upload/, and std::fs on
wasm32 is unsupported, so every import failed. Both now read through
vfs::read_to_string, capped (a theme file at theme_files::MAX_BYTES, a
shortcuts file at the new toolbar_custom::MAX_IMPORT_BYTES), like the
other imports. Natively it is the same file on the disk.
The new tests import a file outside a vfs folder scope (refused, nothing
imported) and inside it (imported). With std::fs both fail: the file
outside the scope is imported.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
timeline.redefine evaluated every later feature in its trial session, then again in the session:
the evaluation cache keeps only the last few states of a design with large bodies, so the trial's
results were gone by then. The trial keeps them in the session's kept results (or its own), and
the session's evaluation takes them from there. EvalCache::with_budget gives tests a small cache.
* claim: #7152
* Preferences: the Colour vision preview shows the chosen mode again (#7152)
Since the preferences redesign the Colour vision section previewed a mode
with four status chips (Selected, Warning, Error, Done). No colour vision
mode changes those colours, so the chips looked the same in every mode.
The section shows the sample of what the modes recolour again (sketch
lines, axes, bends, Draft Analysis, a failed feature), drawn in the mode
being chosen (Tokens::get_in). The status chips stay as they are.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FN5eEMrqWWXqi65FcK8GXq
---------
Co-authored-by: sascha2101 <221609842+sascha2101@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #7091
* Report a bug: scale a large window's screenshot down instead of refusing it (#7091)
A frame over 16 million pixels (a 6K display, a window across two 4K
screens) was refused with "Screenshot dimensions are unsupported", so the
report went out without its picture. Such a frame is now averaged down by
the smallest whole factor that fits.
The frame is also fitted to the longest side a texture can have: a frame
under the pixel limit but wider than the GPU's limit (three ultrawide
screens, 10320 x 1440, on a device limited to 8192) made the preview's
upload fail wgpu validation, and the window's device panics on that.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Inspect: center of mass measures the world state, as Measure does (#5246)
inspect.center_of_mass read the model state, so in an assembly a moved
occurrence was measured where its bodies were made, a second placement
was left out, and a placed copy's world name was "no such bodies". It
now reads the world state, as inspect.measure does, and agrees with
component.properties' mass centre at uniform density.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Center of mass test: unground the placed part before moving it
Upstream now grounds a design's first part when it is made.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #6281
* assets: add Render appearance, library and paint icon drafts for owner review
* assets: add Render lighting, environment and light-type icon drafts for owner review
* assets: add Render camera, shot, frame and strip icon drafts for owner review
* assets: add Render output, result and compare icon drafts for owner review
* assets: list the Render icon drafts in the inventory and ATTRIBUTION
* assets: remake the Render light icons as one family with a shared dotted stem
* assets: remake the Render look icons around the lit look sphere
* assets: redraw the Render look icons so each action reads at a glance
* assets: record the owner's acceptance of the Render light icons
* assets: simplify the Render look icons and make Remove Override an alias of From Material
* assets: record the owner's acceptance of Render look icons
* assets: mark the removed look on From Material with a cross instead of an arrow
* assets: record the owner's acceptance of further Render icons
* assets: redraw the Render library, presets, environment, ground and backdrop icons
* assets: restore the first Render library and backdrop drawings and equalise the cube in the studio icons
* assets: record the owner's acceptance of the Render studio icons
* assets: redraw the Render camera and shot icons as one family
* assets: redraw the Render All Shots and Lens icons so they read at a glance
* assets: put the play badge back on the Render All Shots icon
* assets: redraw the Render output and result icons around one picture base
* assets: give Render Image its own play badge and enlarge the Compare handle
* assets: record the owner's acceptance of the Render output icons
* assets: describe the final Render icon drafts in RENDER_DRAFTS.md and ATTRIBUTION.md
* claim: #6281
* ui_kit: lit sphere thumbnail, cached and bounded
* ui_kit: look-chain widget (four steps, one active, empty steps dashed)
* ui_kit: add both to the gallery and the UI kit notes
* claim: #6281
* docs: record the Render workspace interaction design and implementation roadmap
* docs: plan the Render panels so each has one purpose
* docs: replace the tabs-and-ribbon plan with the stage-bar design
* docs: drop the note about the superseded draft
* docs: plan every Render panel and where each old control goes
* docs: defer the shared lighting rig, UI steps go first
* Patterns: a body of a moved component is copied where it is shown (#4763)
A pattern of bodies made outside the bodies' component (a screw's body patterned from the root)
moved each body's stored geometry, which is in its component's frame, by transforms in the
pattern's frame. The copies landed where the body sits before its component is placed: near the
origin, for every pattern kind. As Mirror does, each copy is now moved as the pattern moves the
body in the world and kept beside its source, in its component.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Pattern test: unground the placed component before moving it
Upstream now grounds a design's first part when it is made.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Hovering many bodies re-emits every edge in the highlight's colours. Each edge
interned its three colours into the palette again, each segment was written in
eight small appends, and segments without their faces' normals encoded the
same zero normals again. Lines are now one record, the edge colours are
interned once per highlight, the buffers are reserved per edge, and the shared
width word is worked out once.
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The no-OIT glass cache compared the blocks' vertex and run buffers by address
without holding them, so a freed buffer's address could come back for other
contents. It now holds them and compares with Arc::ptr_eq.
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Instanced placements were culled per view, but a block drew every body in it
whatever the camera saw. Blocks now keep each body's vertex span and box; when
the view changes, each class's runs are drawn without the bodies whose boxes
are out of the view. A block whose bodies are all in view draws its runs as
they are.
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-27 06:35:11 +00:00
4264 changed files with 256271 additions and 285567 deletions
What you clicked or typed, in order. For a problem on a particular design, `solidcraft-cli script design.solidcraft --out repro.json` turns its history into a script that `solidcraft-cli eval repro.json` replays; attach it, trimmed to the failing step if you can.
What you clicked or typed, in order. For a problem on a particular design, `solidcraft-cli script design.sc --out repro.json` turns its history into a script that `solidcraft-cli eval repro.json` replays; attach it, trimmed to the failing step if you can.
Using an AI agent with SolidCraft's MCP server? The `solidcraft-reporting` skill in [`skills/`](https://github.com/tangency-labs/solidcraft/tree/main/skills) has it reproduce the problem in a fresh design and write these steps for you.
# `ci` job still runs and reports the commit's real result (see the `ci` job): the merge queue
# reads `ci` from the newest run of a commit, so a run without it would block a PR that passed.
# Cross-compilation is opt-in through Run workflow; release builds remain in release.yml.
# The release gate (release-gate.yml) calls this workflow (workflow_call) as a Run workflow with
# cross-compilation on.
# Every Linux job runs on Blacksmith's Ubuntu 24.04 runners (blacksmith-<N>vcpu-ubuntu-2404), sized
# per job: 32 vCPU for the tests, 16 for Clippy, the GPU path tests, the archive build (nightly) and the merge queue's, 8 for compiles, doc and reference tests, 4 for the light checks, 2 for Select checks and ci.
# The Windows shader job runs on Blacksmith's Windows Server 2025 runners (blacksmith-8vcpu-windows-2025);
@@ -50,14 +48,10 @@ on:
# only (other labels are skipped, see the top of this file).
# Pull requests test the affected packages (scripts/ci/affected.py).
scope=(--workspace)
if [ "$FULL" != true ]; then scope=(); for p in $PACKAGES; do scope+=(-p "$p"); done; fi
status=0
# The GPU path tracer's tests run in their own job (GPU path tests): one at a time, they
# took about 36 of this job's 45 minutes.
# A failed test does not fail the step by itself: the tests known to fail today
# (tests/KNOWN_FAILING_TESTS) may fail, any other fails it, and so does a listed test
# that passes. nextest's exit code 100 is "tests failed"; any other is its own error
# (a build error, a bad filter) and fails the step at once.
status=0
cargo nextest run "${scope[@]}" --cargo-profile ci --locked --profile ci --no-tests=pass \
--ignore-default-filter -E "($base) & not (package(=solidcraft-gpu) & test(/^path::/))" || status=$?
if [ "$status" -ne 0 ] && [ "$status" -ne 100 ]; then exit "$status"; fi
cargo xtask known-failing --junit "$junit"
--ignore-default-filter -E "($base) & not (package(=solidcraft-gpu) & test(/^path::/))" || status=1
if [ "$status" != 0 ]; then
# A failed golden gets the hint to re-record it (junit.xml lists the failures).
if [ -f "$junit" ] && tr '\n' ' ' < "$junit" | grep -q '<testcase name="tests::robustness_goldens::[^"]*"[^>]*>[[:space:]]*<failure'; then
echo "::error::A robustness golden changed. If the new result is intended, re-record it in this pull request and say why:"
echo "::error::ROBUSTNESS_GOLDENS_BLESS=\"why it changed\" cargo test --profile ci -p solidcraft-engine --lib robustness_goldens:: (see docs/robustness.md)"
@@ -10,7 +10,6 @@ Work: ROADMAP.md, "Where help is wanted most" in CONTRIBUTING.md, issues labelle
- No GPL/LGPL/AGPL code, not even reading it (OpenCASCADE, FreeCAD, SolveSpace, CadQuery, LibreCAD, QCAD): `cargo xtask licences --check`.
- Every asset is original or openly licensed with a row in ATTRIBUTION.md; read `assets/AGENTS.md` before icon work: `cargo xtask assets`, `cargo xtask icons`.
- Never crash: no unwrap/expect/panic/unsafe in non-test code (unsafe allowlist in crash-safety), errors are `Result`, input is hostile: clippy `-D warnings`, `hostile_params_never_panic`.
- Model decisions (what an operation does, which bodies it touches, which face or edge a point or name means, depths, nearness) use exact geometry; a mesh is for display, export, previews and mesh bodies (`xtask/mesh-decisions.txt` only shrinks): `cargo xtask mesh-decisions`.
- Everything is a command and programmatic calls never open dialogs: `skills/solidcraft-commands/SKILL.md`.
@@ -101,22 +101,6 @@ Editable drawing code, native projects and shipped exports are collected under `
| tests/step-conformance/tiny_pin.step | step-conformance contributors | generated from formulas by https://github.com/dan335/step-conformance (corpus/generated, 96f30bb); import regression fixture | CC0-1.0 |
| tests/step-conformance/torus_ring_nurbs.step | step-conformance contributors | generated from formulas by https://github.com/dan335/step-conformance (corpus/generated, 96f30bb); import regression fixture | CC0-1.0 |
| tests/step-conformance/washer.step | step-conformance contributors | generated from formulas by https://github.com/dan335/step-conformance (corpus/generated, 96f30bb); import regression fixture | CC0-1.0 |
| tests/kernel-cases/import/1721-closed-bspline-tube-face/input.step | step-conformance contributors | copy of tests/step-conformance/cylinder_nurbs.step (generated from formulas by https://github.com/dan335/step-conformance, corpus/generated, 96f30bb); kernel regression case for tangency-labs/solidcraft#1721 | CC0-1.0 |
| tests/kernel-cases/import/2043-outer-bound-opposing-surface-sense/input.step | SolidCraft contributors | synthetic STEP file written by hand from ISO 10303-21 for the kernel regression case of tangency-labs/solidcraft#2043 | MIT OR Apache-2.0 |
| tests/kernel-cases/import/258-cone-point-vertex/input.step | step-conformance contributors | copy of tests/step-conformance/cone_point_vertex.step (generated from formulas by https://github.com/dan335/step-conformance, corpus/generated, 96f30bb); kernel regression case for tangency-labs/solidcraft#258 | CC0-1.0 |
| tests/kernel-cases/import/258-cone-to-point/input.step | step-conformance contributors | copy of tests/step-conformance/cone_point.step (generated from formulas by https://github.com/dan335/step-conformance, corpus/generated, 96f30bb); kernel regression case for tangency-labs/solidcraft#258 | CC0-1.0 |
| tests/kernel-cases/import/258-tiny-pin/input.step | step-conformance contributors | copy of tests/step-conformance/tiny_pin.step (generated from formulas by https://github.com/dan335/step-conformance, corpus/generated, 96f30bb); kernel regression case for tangency-labs/solidcraft#258 | CC0-1.0 |
| tests/kernel-cases/import/3098-bspline-edges-over-tiny-knot-span/input.step | SolidCraft contributors | synthetic STEP file written by hand from ISO 10303-21 for the kernel regression case of tangency-labs/solidcraft#3098 | MIT OR Apache-2.0 |
| tests/kernel-cases/import/3208-extruded-ellipse-face-over-parameter-seam/input.step | SolidCraft contributors | synthetic STEP file written by hand from ISO 10303-21 for the kernel regression case of tangency-labs/solidcraft#3208 | MIT OR Apache-2.0 |
| tests/kernel-cases/import/3352-vertex-off-its-edge-curve/input.step | SolidCraft contributors | synthetic STEP file written by hand from ISO 10303-21 for the kernel regression case of tangency-labs/solidcraft#3352 | MIT OR Apache-2.0 |
| tests/kernel-cases/import/3730-void-oriented-shell-false/input.step | SolidCraft contributors | synthetic STEP file written by hand from ISO 10303-21 for the kernel regression case of tangency-labs/solidcraft#3730 | MIT OR Apache-2.0 |
| tests/kernel-cases/import/3880-cylinder-seam-edge/input.step | step-conformance contributors | copy of tests/step-conformance/cylinder_seam.step (generated from formulas by https://github.com/dan335/step-conformance, corpus/generated, 96f30bb); kernel regression case for tangency-labs/solidcraft#3880 | CC0-1.0 |
| tests/kernel-cases/import/3880-nurbs-hemisphere-dome/input.step | step-conformance contributors | copy of tests/step-conformance/dome_nurbs.step (generated from formulas by https://github.com/dan335/step-conformance, corpus/generated, 96f30bb); kernel regression case for tangency-labs/solidcraft#3880 | CC0-1.0 |
| tests/kernel-cases/import/3963-closed-spline-edge-vertex-past-start/input.step | SolidCraft contributors | synthetic STEP file written by hand from ISO 10303-21 for the kernel regression case of tangency-labs/solidcraft#3963 | MIT OR Apache-2.0 |
| tests/kernel-cases/import/4005-cylinder-written-as-near-zero-cone/input.step | SolidCraft contributors | synthetic STEP file written by hand from ISO 10303-21 for the kernel regression case of tangency-labs/solidcraft#4005 | MIT OR Apache-2.0 |
| tests/kernel-cases/import/4006-plane-written-as-cone-of-nearly-right-angle/input.step | SolidCraft contributors | synthetic STEP file written by hand from ISO 10303-21 for the kernel regression case of tangency-labs/solidcraft#4006 | MIT OR Apache-2.0 |
| tests/ssi-corpus/cases/*.json.gz, tests/ssi-corpus/LICENSE-MIT, tests/ssi-corpus/LICENSE-APACHE | ssi-corpus contributors (jyviko) | 119 surface/surface intersection cases with exact ground truth from https://github.com/jyviko/ssi-corpus (cases/data, b04c8a6), gzip-compressed, unchanged; SSI scorer fixtures (tests/ssi-corpus/README.md) | MIT OR Apache-2.0 |
| tests/ssi-corpus/cases/y23_*.json.gz | Yang, Jia and Yan (paper authors: published surfaces, figures and topology); ssi-corpus contributors (reconstructions and truth) | 25 cases built from Yang, Jia & Yan 2023, "Topology Guaranteed B-Spline Surface/Surface Intersection", ACM Transactions on Graphics 42(6), Article 211; each cites the paper in its `source` field | CC-BY 4.0 (paper); MIT OR Apache-2.0 (case files) |
| tests/step-regressions/rod_doubled_edge.step | SolidCraft contributors | written by SolidCraft from the commands of #4598 (a bored rod moved 100 mm and exported); import regression fixture | MIT OR Apache-2.0 |
| tests/oracle/17-loft/reference.stl | SolidCraft contributors | triangulation of our own recipe (tests/oracle/17-loft/recipe.json) built with FreeCAD as an external tool by tools/freecad-oracle | MIT OR Apache-2.0 |
| tests/oracle/23-bracket-multifeature/reference.stl | SolidCraft contributors | triangulation of our own recipe (tests/oracle/23-bracket-multifeature/recipe.json) built with FreeCAD as an external tool by tools/freecad-oracle | MIT OR Apache-2.0 |
@@ -144,14 +128,14 @@ Editable drawing code, native projects and shipped exports are collected under `
| assets/fonts/ibm-plex-mono/OFL.txt | IBM Corp. | Unmodified accompanying font licence | SIL OFL 1.1 |
| assets/fonts/ibm-plex-sans/OFL.txt | IBM Corp. | Unmodified accompanying font licence | SIL OFL 1.1 |
| assets/fonts/liberation-sans/OFL.txt | Red Hat, Inc.; Liberation Fonts contributors | Unmodified accompanying font licence | SIL OFL 1.1 |
| assets/icons/app/linux/exports/hicolor/128x128/apps/ai.storyteller.solidcraft.png | SolidCraft contributors | Generated from assets/icons/app/shared/source/app_icon.rs with the original CLI/ICO encoders | MIT OR Apache-2.0 |
| assets/icons/app/linux/exports/hicolor/16x16/apps/ai.storyteller.solidcraft.png | SolidCraft contributors | Generated from assets/icons/app/shared/source/app_icon.rs with the original CLI/ICO encoders | MIT OR Apache-2.0 |
| assets/icons/app/linux/exports/hicolor/24x24/apps/ai.storyteller.solidcraft.png | SolidCraft contributors | Generated from assets/icons/app/shared/source/app_icon.rs with the original CLI/ICO encoders | MIT OR Apache-2.0 |
| assets/icons/app/linux/exports/hicolor/256x256/apps/ai.storyteller.solidcraft.png | SolidCraft contributors | Generated from assets/icons/app/shared/source/app_icon.rs with the original CLI/ICO encoders | MIT OR Apache-2.0 |
| assets/icons/app/linux/exports/hicolor/32x32/apps/ai.storyteller.solidcraft.png | SolidCraft contributors | Generated from assets/icons/app/shared/source/app_icon.rs with the original CLI/ICO encoders | MIT OR Apache-2.0 |
| assets/icons/app/linux/exports/hicolor/48x48/apps/ai.storyteller.solidcraft.png | SolidCraft contributors | Generated from assets/icons/app/shared/source/app_icon.rs with the original CLI/ICO encoders | MIT OR Apache-2.0 |
| assets/icons/app/linux/exports/hicolor/512x512/apps/ai.storyteller.solidcraft.png | SolidCraft contributors | Generated from assets/icons/app/shared/source/app_icon.rs with the original CLI/ICO encoders | MIT OR Apache-2.0 |
| assets/icons/app/linux/exports/hicolor/64x64/apps/ai.storyteller.solidcraft.png | SolidCraft contributors | Generated from assets/icons/app/shared/source/app_icon.rs with the original CLI/ICO encoders | MIT OR Apache-2.0 |
| assets/icons/app/linux/exports/hicolor/128x128/apps/io.tangencylabs.solidcraft.png | SolidCraft contributors | Generated from assets/icons/app/shared/source/app_icon.rs with the original CLI/ICO encoders | MIT OR Apache-2.0 |
| assets/icons/app/linux/exports/hicolor/16x16/apps/io.tangencylabs.solidcraft.png | SolidCraft contributors | Generated from assets/icons/app/shared/source/app_icon.rs with the original CLI/ICO encoders | MIT OR Apache-2.0 |
| assets/icons/app/linux/exports/hicolor/24x24/apps/io.tangencylabs.solidcraft.png | SolidCraft contributors | Generated from assets/icons/app/shared/source/app_icon.rs with the original CLI/ICO encoders | MIT OR Apache-2.0 |
| assets/icons/app/linux/exports/hicolor/256x256/apps/io.tangencylabs.solidcraft.png | SolidCraft contributors | Generated from assets/icons/app/shared/source/app_icon.rs with the original CLI/ICO encoders | MIT OR Apache-2.0 |
| assets/icons/app/linux/exports/hicolor/32x32/apps/io.tangencylabs.solidcraft.png | SolidCraft contributors | Generated from assets/icons/app/shared/source/app_icon.rs with the original CLI/ICO encoders | MIT OR Apache-2.0 |
| assets/icons/app/linux/exports/hicolor/48x48/apps/io.tangencylabs.solidcraft.png | SolidCraft contributors | Generated from assets/icons/app/shared/source/app_icon.rs with the original CLI/ICO encoders | MIT OR Apache-2.0 |
| assets/icons/app/linux/exports/hicolor/512x512/apps/io.tangencylabs.solidcraft.png | SolidCraft contributors | Generated from assets/icons/app/shared/source/app_icon.rs with the original CLI/ICO encoders | MIT OR Apache-2.0 |
| assets/icons/app/linux/exports/hicolor/64x64/apps/io.tangencylabs.solidcraft.png | SolidCraft contributors | Generated from assets/icons/app/shared/source/app_icon.rs with the original CLI/ICO encoders | MIT OR Apache-2.0 |
| assets/icons/app/shared/source/app_icon.rs | SolidCraft contributors | Original SolidCraft drawing code: the borderless mark, drawn from the faces and gradients of assets/icons/app/macos/source/SolidCraft.icon/Assets/03-solid-blue-cutout.svg | MIT OR Apache-2.0 |
| assets/icons/app/shared/source/windows_ico.rs | SolidCraft contributors | Original SolidCraft resource data or drawing code, collected without redesign | MIT OR Apache-2.0 |
| assets/icons/app/windows/exports/solidcraft-executable.ico | SolidCraft contributors | Generated from assets/icons/app/shared/source/app_icon.rs with the original CLI/ICO encoders | MIT OR Apache-2.0 |
@@ -187,9 +171,9 @@ Editable drawing code, native projects and shipped exports are collected under `
| assets/locales/zh.json | SolidCraft contributors | Original SolidCraft resource data or drawing code, collected without redesign | MIT OR Apache-2.0 |
| assets/locales/zh-TW.json | SolidCraft contributors | Original Traditional Chinese (Taiwan) translation of SolidCraft's English catalog; no commercial CAD text used | MIT OR Apache-2.0 |
| assets/materials/library.rs | SolidCraft contributors | Original SolidCraft resource data or drawing code, collected without redesign | MIT OR Apache-2.0 |
| assets/platforms/macos/dmg/background.tiff | SolidCraft contributors | original disk image window background (blueprint sheet: grid, double border, circles, centre lines, leader arrow, title block) drawn by assets/platforms/macos/dmg/make_background.py in IBM Plex Sans/Mono; 1x and 2x representations | MIT OR Apache-2.0 |
| assets/platforms/macos/dmg/make_background.py | SolidCraft contributors | authoring script that draws the disk image background (Pillow); not part of any build | MIT OR Apache-2.0 |
| assets/platforms/macos/dmg/layout.json | SolidCraft contributors | disk image window size, icon size and icon positions shared by the background script and settings.py | MIT OR Apache-2.0 |
@@ -308,6 +292,53 @@ Editable drawing code, native projects and shipped exports are collected under `
| assets/icons/ui/design/svg/model/colored_components_off.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/combine.svg | SolidCraft contributors | two-tone modelling icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/component.svg | SolidCraft contributors | two-tone modelling icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
### Render workspace icon drafts
Original SolidCraft drawings, Open for owner review; no runtime assignments.
| File | Author | Source | Licence |
|---|---|---|---|
| assets/icons/ui/design/svg/model/render_all_shots.svg | SolidCraft contributors | original Camera icon master (64 grid): stacked camera bodies with a lower-right play badge | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/render_appearance.svg | SolidCraft contributors | original Looks icon master (64 grid): shaded look sphere on the top corner of an accent-shaded cube | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/render_aspect_frame.svg | SolidCraft contributors | original Camera icon master (64 grid): wide rounded frame with two opposite corner brackets | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/render_backdrop.svg | SolidCraft contributors | original Studio icon master (64 grid): grey cube in front of a tall upright accent panel | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/render_cancel.svg | SolidCraft contributors | original Output icon master (64 grid): picture with a lower-right round stop badge | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/render_check.svg | SolidCraft contributors | original Output icon master (64 grid): picture with a lower-right round check badge | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/render_compare.svg | SolidCraft contributors | original Output icon master (64 grid): two grey pictures split by a vertical slider with opposed arrows | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/render_copy_look.svg | SolidCraft contributors | original Looks icon master (64 grid): look sphere in a square frame offset from a second frame outline | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/render_execute.svg | SolidCraft contributors | original Output icon master (64 grid): picture with a lower-right round play badge | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/render_export_image.svg | SolidCraft contributors | original Output icon master (64 grid): picture with a large outward diagonal arrow at the upper right | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/render_exposure.svg | SolidCraft contributors | original Camera icon master (64 grid): split light and accent disc with plus and minus marks | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/render_fit_product.svg | SolidCraft contributors | original Camera icon master (64 grid): grey cube inside four corner brackets | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/render_frame_guides.svg | SolidCraft contributors | original Camera icon master (64 grid): rounded frame with a rule-of-thirds grid and four accent nodes | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/render_from_material.svg | SolidCraft contributors | original Looks icon master (64 grid): look sphere struck through above a plain grey slab | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/render_ground.svg | SolidCraft contributors | original Studio icon master (64 grid): grey cube on a slab with an accent contact patch | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/render_lens.svg | SolidCraft contributors | original Camera icon master (64 grid): concentric lens rings around an accent centre | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/render_library.svg | SolidCraft contributors | original Studio icon master (64 grid): row of three look spheres, middle one accent, on a shelf bar | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/render_light_area.svg | SolidCraft contributors | original Lights icon master (64 grid): slanted flat panel above a dotted accent stem | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/render_light_by_highlight.svg | SolidCraft contributors | original Lights icon master (64 grid): shaded sphere with a ringed highlight target and dotted stem arriving from above | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/render_light_point.svg | SolidCraft contributors | original Lights icon master (64 grid): round source disc with centre dot above a dotted accent stem | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/render_light_spot.svg | SolidCraft contributors | original Lights icon master (64 grid): cone source with curved rim above a dotted accent stem | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/render_lighting_presets.svg | SolidCraft contributors | original Studio icon master (64 grid): two stacked cards, the front one holding a light bulb | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/render_output_size.svg | SolidCraft contributors | original Output icon master (64 grid): picture frame with a diagonal double-headed dimension arrow | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/render_paint.svg | SolidCraft contributors | original Looks icon master (64 grid): flat brush with an accent tip, light ferrule and slate handle | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/render_pick_up.svg | SolidCraft contributors | original Looks icon master (64 grid): eyedropper with a grey bulb, slate crossbar and accent sampling tip | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/render_quality.svg | SolidCraft contributors | original Output icon master (64 grid): three rising bars, the tallest in accent | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/render_replace_all.svg | SolidCraft contributors | original Looks icon master (64 grid): grey sample sphere with an arrow to an accent sphere beside a second one | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/render_results.svg | SolidCraft contributors | original Output icon master (64 grid): stack of two pictures, the front one showing a landscape | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/render_same_look.svg | SolidCraft contributors | original Looks icon master (64 grid): look sphere and three cubes, the two matching cubes ringed | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/render_shot.svg | SolidCraft contributors | original Camera icon master (64 grid): camera body with a lower-right plus badge | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/render_shots_strip.svg | SolidCraft contributors | original Camera icon master (64 grid): strip of three small frames, the middle one in accent | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/render_solo_light.svg | SolidCraft contributors | original Lights icon master (64 grid): point source and dotted stem inside four corner focus brackets | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/render_sun.svg | SolidCraft contributors | original Lights icon master (64 grid): source disc with eight short radial rays | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/render_transparent_background.svg | SolidCraft contributors | original Output icon master (64 grid): grey cube on a checker tile | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/render_update_shot.svg | SolidCraft contributors | original Camera icon master (64 grid): camera body with a lower-right refresh badge | MIT OR Apache-2.0 |
### UI icon masters (continued)
| File | Author | Source | Licence |
|---|---|---|---|
| assets/icons/ui/design/svg/model/compute.svg | SolidCraft contributors | two-tone modelling icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/configuration_table.svg | SolidCraft contributors | two-tone modelling icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/conic.svg | SolidCraft contributors | two-tone modelling icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
@@ -397,6 +428,21 @@ Editable drawing code, native projects and shipped exports are collected under `
| assets/icons/ui/design/svg/flat/shield.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/status_recovery.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/frame_member.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/render_environment.svg | SolidCraft contributors | original Studio icon master (64 grid): dome with a grey cube inside | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/style_shaded.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/style_wireframe.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/style_hidden_edges.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/style_translucent.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/select_faces_edges.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/select_bodies.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/select_features.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/select_parts.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/select_components.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/select_sketch.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/display.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/grid.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/ground_shadow.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/ambient_occlusion.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/placeholder.svg | SolidCraft contributors | two-tone modelling icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/plane.svg | SolidCraft contributors | two-tone modelling icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/plane_angle.svg | SolidCraft contributors | two-tone modelling icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
@@ -546,6 +592,16 @@ Editable drawing code, native projects and shipped exports are collected under `
| assets/icons/ui/design/svg/model/cone.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| assets/locales/ar.json | Abdullah (A-AL-ANAZI) | Original Arabic translation of SolidCraft's English catalog; no commercial CAD text used | MIT OR Apache-2.0 |
| assets/locales/he.json | SolidCraft contributors | Original Hebrew translation of SolidCraft's English catalog; no commercial CAD text used | MIT OR Apache-2.0 |
| assets/styles/ui/themes/cga.json | SolidCraft contributors | original SolidCraft theme file using the 16 colours of the IBM Color Graphics Adapter (black, cyan, magenta, and white of its high-intensity palette 1 for the interface) | MIT OR Apache-2.0 |
| assets/styles/ui/themes/1-bit.json | SolidCraft contributors | original SolidCraft theme file | MIT OR Apache-2.0 |
| assets/styles/ui/themes/1-bit-dark.json | SolidCraft contributors | original SolidCraft theme file | MIT OR Apache-2.0 |
| `assets/locales/pending/navbar-customisation.json` | SolidCraft contributors | Original navigation bar editor translations | MIT OR Apache-2.0 |
| `assets/locales/pending/navbar-slots.json` | SolidCraft contributors | Original navigation slot translations | MIT OR Apache-2.0 |
| assets/locales/bs.json | Tarik Sijercic (tsijercic1) | Original Bosnian translation of SolidCraft's English catalog; no commercial CAD text used | MIT OR Apache-2.0 |
| assets/locales/hr.json | Tarik Sijercic (tsijercic1) | Original Croatian translation of SolidCraft's English catalog; no commercial CAD text used | MIT OR Apache-2.0 |
| assets/locales/sr.json | Tarik Sijercic (tsijercic1) | Original Serbian (ekavian, Cyrillic) translation of SolidCraft's English catalog; no commercial CAD text used | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/finish_edit.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| `assets/locales/pending/sketch-tracking-guides.json` | SolidCraft contributors | Original alignment-guide preference labels and help text | MIT OR Apache-2.0 |
| vendor/truck-shapeops | truck-shapeops 0.4.0, https://github.com/ricosjp/truck (RICOS Co. Ltd.) | Apache-2.0 | robust meshing and nearest-parameter fallback in booleans; degenerate intersection-curve rejection; face and edge history of booleans; trace macro; corner choice at vertices several intersection curves end on; reuse of an operand's meshes (vendor/README.md) |
| vendor/truck-geometry | truck-geometry 0.5.0, https://github.com/ricosjp/truck (RICOS Co. Ltd.) | Apache-2.0 | intersection curves divided from their neighbours' surface parameters; surface second derivatives (vendor/README.md) |
The naming corpus checks persistent names: every name a corpus design stores still finds a face or
an edge, and two builds name its faces and edges alike
(`cargo test --profile ci -p solidcraft-cli --test naming_corpus`).
A geometry change that changes the robustness goldens re-records them in the same pull request:
`ROBUSTNESS_GOLDENS_BLESS="why it changed" cargo test --profile ci -p solidcraft-engine --lib robustness_goldens::`
(see [docs/robustness.md](docs/robustness.md)); CI runs every seed on pull requests that touch geometry.
[docs/mutation-testing.md](docs/mutation-testing.md) shows how to find kernel behaviour these
black-box suites miss.
@@ -221,7 +220,8 @@ Pull requests skip a few heavy tests (one to twelve minutes each), which run in
Check that validation after merging. They are listed in
`.config/nextest.toml` and the [testing page](docs/book/src/dev/testing.md):
- `solidcraft-engine`: `tests::hostile_params_never_panic`, everything in `tests::robustness`,
- `solidcraft-engine`: `tests::hostile_params_never_panic`, everything in `tests::robustness` and
the seeds of `tests::robustness_goldens` (`seed_00` … `seed_95`; pull requests that change the kernel, truck or the goldens run all but the slowest, `SLOW_SEEDS` in `ci.yml`),
- **Parameters and configurations** with unit-aware expressions.
- **Assemblies** with joints, motion studies, contact sets, interference and mass properties.
- **Sheet metal** with flanges, hems, fold/unfold and flat patterns, and plastic enclosure features.
- **Files:** STEP, IGES, 3MF, STL, OBJ and glTF in and out where supported, URDF export, `.solidcraft` designs.
- **Files:** STEP, IGES, 3MF, STL, OBJ and glTF in and out where supported, URDF export, `.sc` designs (`.solidcraft` files from earlier releases still open).
- **Desktop app, browser app, headless CLI and MCP server**, all running the same commands.
The full list is in [docs/capabilities.md](docs/capabilities.md) and [docs/features.md](docs/features.md);
@@ -121,6 +121,12 @@ the clean-room and licence rules, and where help is wanted most. Open work is in
SolidCraft's continuous integration runs on [Blacksmith](https://www.blacksmith.sh), which
sponsors its CI minutes.
## Disclaimer
The files in this repository are provided as is, without warranty, liability or indemnity, and
Tangency Labs and the contributors accept no responsibility for any harmful or unlawful use of
them: read [DISCLAIMER.md](DISCLAIMER.md).
## Licence
MIT OR Apache-2.0, at your option. Developed under the
constUSAGE_RUN: &str=" solidcraft-cli run <script.json|design.solidcraft|part.step> [--in design.solidcraft|part.step] [--out FILE]... [--save FILE] [--quiet] [--results] [--timings] [--profile] [--profile-out trace.json] [--json-out FILE] [--allow-errors] [--validation each_step|each_feature|off]\n solidcraft-cli run --in <design.solidcraft|part.step> [--out FILE]...\n (a timeline feature that fails fails the run and nothing is written; --allow-errors writes anyway)\n (--validation: when SMLib's full check runs on the features computed (diagnostics.validation), default each_feature)\n (--timings prints {timings: [{index, command, ms}], total_ms} for a script, with --results its results too;\n --profile adds each command's profile: {boolean_ms, tessellate_ms, other_ms}, the kernel's share)\n (--profile also records where the time goes, SMLib's calls, kernel operations, features, commands and import stages,\n and prints the table to stderr: count, total, self time and longest per span; --profile-out FILE writes the spans\n as a Chrome trace for chrome://tracing or ui.perfetto.dev; SOLIDCRAFT_PROFILE=1 does the same for any command)\n";
constUSAGE_RUN: &str=" solidcraft-cli run <script.json|design.sc|part.step> [--in design.sc|part.step] [--out FILE]... [--save FILE] [--quiet] [--results] [--timings] [--profile] [--json-out FILE] [--allow-errors]\n solidcraft-cli run --in <design.sc|part.step> [--out FILE]...\n (a timeline feature that fails fails the run and nothing is written; --allow-errors writes anyway)\n (--timings prints {timings: [{index, command, ms}], total_ms} for a script, with --results its results too;\n --profile adds each command's profile: {boolean_ms, tessellate_ms, other_ms}, the kernel's share)\n";
/// `eval`'s lines in the usage.
constUSAGE_EVAL: &str=" solidcraft-cli eval <script.json|design.solidcraft|part.step> [--in design.solidcraft|part.step] [--json-out FILE] [--profile] [--profile-out trace.json] (alias: inspect)\n solidcraft-cli eval --in <script.json|design.solidcraft|part.step> [--json-out FILE]\n (with both inputs, runs the positional script on --in before measuring; does not automatically save the modified design)\n (--json-out FILE writes the JSON that run (with --results, else the measurement) and eval print to FILE instead of stdout,\n as plain UTF-8: a shell's > can re-encode it, e.g. Windows PowerShell adds a byte-order mark)\n (--profile and --profile-out as for run)\n";
constUSAGE_EVAL: &str=" solidcraft-cli eval <script.json|design.sc|part.step> [--in design.sc|part.step] [--json-out FILE] (alias: inspect)\n solidcraft-cli eval --in <script.json|design.sc|part.step> [--json-out FILE]\n (with both inputs, runs the positional script on --in before measuring; does not automatically save the modified design)\n (--json-out FILE writes the JSON that run (with --results, else the measurement) and eval print to FILE instead of stdout,\n as plain UTF-8: a shell's > can re-encode it, e.g. Windows PowerShell adds a byte-order mark)\n";
/// `script`'s lines in the usage.
constUSAGE_SCRIPT: &str=" solidcraft-cli script <design.solidcraft|script.json> [--out script.json]\n (the design's history as a command script that eval and run replay)\n";
constUSAGE_SCRIPT: &str=" solidcraft-cli script <design.sc|script.json> [--out script.json]\n (the design's history as a command script that eval and run replay)\n";
/// `bench-open`'s lines in the usage.
constUSAGE_BENCH_OPEN: &str=" solidcraft-cli bench-open <design.solidcraft | part.step> [--parse-only] [--save OUT.solidcraft] [--display LEVEL [--edit NAME EXPR]] [--ops script.json] [--profile] [--profile-out trace.json]\n (--profile and --profile-out as for run)\n (--ops: after opening, run each command of the script and undo it, timing the command, the\n evaluations it caused and the view it leaves: placing and meshing what changed)\n";
constUSAGE_BENCH_OPEN: &str=" solidcraft-cli bench-open <design.sc | part.step> [--parse-only] [--save OUT.sc] [--display LEVEL [--edit NAME EXPR]] [--ops script.json]\n (--ops: after opening, run each command of the script and undo it, timing the command, the\n evaluations it caused and the view it leaves: placing and meshing what changed)\n";
constUSAGE_SNAPSHOT: &str=" solidcraft-cli snapshot <script|design> [--in design.solidcraft|part.step] --out shot.png [--width W] [--height H] [--view NAME | --camera JSON] [--no-grid] [--no-axes]\n [--body NAME:opacity=0.3,color=#rrggbb]... [--tool NAME]... [--section z=27] [--quality coarse|normal|fine|ultra]\n solidcraft-cli snapshot --in <design.solidcraft|part.step> --out shot.png ...\n (as with run, --in starts from a design and the script runs on top of it; an option snapshot doesn't take is an error;\n --view: iso, front, back, top, bottom, left, right, or the name of a view saved in the design with view.save;\n --camera: {target: [x,y,z], yaw, pitch, distance, fov?} in mm and radians, as the MCP screenshot takes it (docs/mcp.md);\n --no-grid and --no-axes leave the ground grid (on Z 0) and the origin's axes out of the picture;\n --body: how one body is drawn, as appearance.assign takes it: opacity 0-1 (below 1 is see-through, so what is inside\n it shows) and color #rrggbb, either or both, once per body;\n --tool: draw this body solid in the tool colour and every other body see-through, so a cut's or a join's tool body\n is visible inside the material it meets; a later --body overrides it;\n --section: cut the model as inspect.section does, so the inside shows: z=27 (the plane through that world\n coordinate, keeping the smaller side), add ,flip to keep the other side, or XY, XZ, YZ, or its own JSON;\n --quality: the display quality the bodies are meshed at, as the app's setting (default normal))\n";
constUSAGE_SNAPSHOT: &str=" solidcraft-cli snapshot <script|design> [--in design.sc|part.step] --out shot.png [--width W] [--height H] [--view NAME | --camera JSON] [--no-grid] [--no-axes]\n [--body NAME:opacity=0.3,color=#rrggbb]... [--tool NAME]... [--section z=27]\n solidcraft-cli snapshot --in <design.sc|part.step> --out shot.png ...\n (as with run, --in starts from a design and the script runs on top of it; an option snapshot doesn't take is an error;\n --view: iso, front, back, top, bottom, left, right, or the name of a view saved in the design with view.save;\n --camera: {target: [x,y,z], yaw, pitch, distance, fov?} in mm and radians, as the MCP screenshot takes it (docs/mcp.md);\n --no-grid and --no-axes leave the ground grid (on Z 0) and the origin's axes out of the picture;\n --body: how one body is drawn, as appearance.assign takes it: opacity 0-1 (below 1 is see-through, so what is inside\n it shows) and color #rrggbb, either or both, once per body;\n --tool: draw this body solid in the tool colour and every other body see-through, so a cut's or a join's tool body\n is visible inside the material it meets; a later --body overrides it;\n --section: cut the model as inspect.section does, so the inside shows: z=27 (the plane through that world\n coordinate, keeping the smaller side), add ,flip to keep the other side, or XY, XZ, YZ, or its own JSON)\n";
constUSAGE_KERNEL_CASES: &str=" solidcraft-cli kernel-cases <case-dir>... [--json] [--dumps=DIR] [--validation=each_step|each_feature|off]\n (the kernel regression rig, tests/kernel-cases, on SMLib; --dumps: each case's\n failing SMLib calls leave their inputs for replay in DIR/<area>-<case>; --validation: when SMLib's\n full check runs (diagnostics.validation), default each_feature)\n";
out.push(format!("{n} at {x:?}, then {m} at {y:?}"));
}
}
for((r,_),(q,_))ina.refs.iter().zip(&b.refs){
ifr!=q{
out.push(format!("{r}, then {q}"));
}
}
ifa.refs.len()!=b.refs.len(){
out.push(format!("{} stored names, then {}",a.refs.len(),b.refs.len()));
}
out.truncate(cap);
out
}
/// Designs whose names differ from one build to the next today: (design, why). Their
/// differences are printed, not failed on, until the cause is fixed; a stored name that finds
/// nothing still fails.
constUNSTABLE: [(&str,&str);1]=[(
"regressions/closed_sweep_torus_cut",
"the halves of each swept wall lie level along every axis, so which is `#1` and which `#2` follows the order the kernel gives the faces in, which changes from one build to the next",
"Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.5 Mobile/15E148 Safari/604.1";
letmac="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.5 Safari/605.1.15";
letchrome_ios="Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/138.0.7204.156 Mobile/15E148 Safari/604.1";
letchrome="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36";
assert!(apple_touch(iphone,5));
assert!(apple_touch(chrome_ios,5),"every browser on iOS has the same picker");
assert!(apple_touch(mac,5),"an iPad presents itself as a Mac");
assert!(!apple_touch(mac,0),"a Mac has no touch screen");
assert!(!apple_touch(chrome,10),"a touch screen elsewhere filters as before");
assert!(!apple_touch("",0),"hostile input is just not an iPhone");
| AppImage | Uses the Linux 256 px image and `.DirIcon` alias |
| FreeBSD | Shares Linux hicolor images and freedesktop metadata |
| Web | HTML/Trunk and `manifest.webmanifest` stay in `apps/solidcraft-web/`; copies Japanese font/licence to `fonts/`, uses the collected CSS, and copies the Linux 32/256/512 px images to `icons/` for the favicon and installed app |
All ids below are **Open** for owner review, except `render_scene` and `render_image`,
which are existing accepted icons used only as references. No commands, toolbar
entries, browser selectors or runtime request manifests are changed by this draft set.
`render_remove_override` is the one remaining alias (of `render_from_material`);
`render_execute`, `render_environment` and `render_fit_product` now have their own
masters in `design/svg/model/`. Accepted source masters stay unchanged.
Review with `python3 -B assets/icons/ui/design/tools/icon_status.py --serve`,
search `render_`, and select Open. See [STYLE_GUIDE.md](STYLE_GUIDE.md) for
acceptance; drafts do not imply implemented commands.
The finished references in each row were read and previewed in both themes
before authoring. Draft contact sheets (96, 24 and 16 px, both themes) are local
review evidence, not shipped assets. The icons are grouped in five families, each
with one shared design rule.
## Lights
Rule: a slate source glyph with light passive fill, 2.55-unit outlines and a dotted
accent stem (three dots, 4.25 diameter, 7 units apart) showing the direction.
| Meaning | Id | Finished references | Drawing |
|---|---|---|---|
| Light area | `render_light_area` | `box`, `camera` | Slanted flat panel on the canonical axes above the dotted stem. |
| Light spot | `render_light_spot` | `camera`, `material` | Cone with a curved rim above the dotted stem. |
| Light point | `render_light_point` | `camera`, `material` | Round source disc with a centre dot above the dotted stem. |
| Sun | `render_sun` | `sphere`, `camera` | Source disc of the same size with eight short radial rays instead of a stem. |
| Light by highlight | `render_light_by_highlight` | `sphere`, `material` | Shaded sphere with a ringed highlight target; the dotted stem arrives from above. |
| Solo light | `render_solo_light` | `fit`, `camera` | Point source and stem inside four corner focus brackets. |
## Looks
Rule: the lit look sphere (top-left highlight, bottom-right shade) plus one cue that
says what happens to it; receiving bodies stay canonical grey or cube shapes.
| Meaning | Id | Finished references | Drawing |
|---|---|---|---|
| Appearance | `render_appearance` | `material`, `box` | Shaded look sphere on the top corner of an accent-shaded cube: a look assigned to a body. |
| Paint | `render_paint` | `material`, `paste` | Flat brush with an accent tip, light ferrule and slate handle. |
| Pick up | `render_pick_up` | `material`, `copy` | Eyedropper with a grey bulb, slate crossbar and accent sampling tip. |
| Replace all | `render_replace_all` | `copy`, `box` | Grey sample sphere with an arrow to an accent sphere, beside a second accent sphere. |
| Copy look | `render_copy_look` | `material`, `copy` | Look sphere in a square frame, offset from a second frame outline. |
| Remove override | `render_remove_override` | `appearance`, `box` | Alias of `render_from_material`: the custom look is crossed off. |
| Same look | `render_same_look` | `select`, `box` | Look sphere and three cubes; the two matching cubes are ringed. |
| From material | `render_from_material` | `material`, `extrude` | Look sphere struck through above a plain grey slab, so the material shows. |
## Studio
Rule: the standard grey cube stands in the set-up it is lit or placed in.
| Meaning | Id | Finished references | Drawing |
|---|---|---|---|
| Library | `render_library` | `library`, `copy` | Row of three look spheres, the middle one accent, on a shelf bar. |
| Lighting presets | `render_lighting_presets` | `new`, `render_scene` | Two stacked cards, the front one holding a light bulb. |
| Environment | `render_environment` | `environment_map`, `render_scene` | Dome with a grey cube inside. |
| Ground | `render_ground` | `box`, `render_scene` | Grey cube on a slab with an accent contact patch. |
| Backdrop | `render_backdrop` | `render_scene`, `render_image` | Grey cube in front of a tall upright accent panel. |
## Camera and shots
Rule: one chunky camera body with a lower-right round badge for shot actions; the
other camera icons are simple frames.
| Meaning | Id | Finished references | Drawing |
|---|---|---|---|
| Shot | `render_shot` | `camera`, `new` | Camera body with a plus badge: save a shot. |
| Update shot | `render_update_shot` | `camera`, `undo` | Camera body with a refresh badge. |
| Render all shots | `render_all_shots` | `copy`, `render_image` | Stacked camera bodies with a play badge. |
| Shots strip | `render_shots_strip` | `camera`, `copy` | Strip of three small frames, the middle one in accent. |
| Fit product | `render_fit_product` | `fit`, `camera` | Grey cube inside four corner brackets. |
| Lens / field of view | `render_lens` | `camera`, `fit` | Concentric lens rings around an accent centre. |
| Exposure | `render_exposure` | `material`, `environment_map` | Disc split light and accent, with plus and minus marks. |
| Aspect frame | `render_aspect_frame` | `render_image`, `fit` | Wide rounded frame with two opposite corner brackets. |
| Frame guides | `render_frame_guides` | `render_image`, `fit` | Rounded frame with a rule-of-thirds grid and four accent nodes. |
## Output and result
Rule: one picture base (frame with landscape) with a lower-right round badge; the
size, background and quality icons draw their own simple subject.
| Meaning | Id | Finished references | Drawing |
|---|---|---|---|
| Render image | `render_execute` | `render_image`, `camera` | Picture with a play badge: create a rendered image. |
| Render output size | `render_output_size` | `render_image`, `fit` | Picture frame with a diagonal double-headed dimension arrow. |
| Transparent background | `render_transparent_background` | `material`, `render_image` | Grey cube on a checker tile. |
| Quality | `render_quality` | `list`, `library` | Three rising bars, the tallest in accent. |
| Check render | `render_check` | `render_image`, `new` | Picture with a check badge. |
| Cancel render | `render_cancel` | `render_image`, `delete` | Picture with a square stop badge. |
| Export image | `render_export_image` | `render_image`, `copy` | Picture with a large outward diagonal arrow at the upper right. |
| Compare | `render_compare` | `render_image`, `material` | Two grey pictures split by a vertical slider with opposed arrows. |
| Results | `render_results` | `render_image`, `copy` | Stack of two pictures, the front one showing a landscape. |
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.