A pull request now starts by saying what it is in plain words, and shows
anything there is to look at in its description. A file in the diff
doesn't count. The new Picture check fails a PR that adds to the showcase
or docs/screenshots without showing a picture. It isn't a required check.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The editor reused the new-hole Face input, which is multi, so a second pick
appended and OK silently kept the first. Editing now makes that input single,
so a pick replaces the place.
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
`sketch.rectangle.rounded` read `p0` and `p1` with `req_vec2`, so a corner typed as an
expression (`["W / 2", "H / 2"]`) was refused with "`p0` must be a point [x, y]", although the
2-point rectangle it hands the corners to already places expression corners and keeps them
following their parameters. The corners are now read with `corner_arg`, as the 2-point and
center rectangles read theirs; the worked-out values still check the radius against the sides.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
A value typed before its target opened the box 18 px right and 10 px below the
pointer, so its frame covered the geometry a click was about to pick. Near the
viewport's right or bottom edge the clamp then slid the box back over the
pointer, losing the gap where it mattered most. It now keeps a 30 px gap, and at
an edge it opens on the pointer's other side instead of sliding back.
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Convert to Sheet Metal refused a tray made with SolidCraft's own sheet metal, exported and
imported again: its walls run on past each bend's end (notch, tear and round corner reliefs), so
the edges beside the bend cylinder's edge never reach the other face's plane and the face could
not be trimmed ("a face beside a chamfer picked as a bend does not reach the face across it").
Where that edge is round or runs on along the bend's line, the face now keeps its corner and
steps down square to the other plane, a step the bend's setback takes up again; the inset drops
an edge the setback leaves shorter than the tolerance.
Test: trays_of_its_own_sheet_metal_convert_back (notch, tear and round, the walls made in one
flange feature, and notch and round with the short walls in a second one): the volume, the flat's
size and outline area and every bend's length are the source's.
Fixes#7529.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #6452
* Engine: a pattern or mirror copy of a join keeps its ends as drawn (#6452)
The copy's tool is rebuilt against the current state, which already holds
the source feature's own material; the embedded-join-end trim (#2059)
read that material as the target already filling the copy's bottom band
and trimmed it away before the copy matrix was applied. Mirroring or
patterning a join on a hollow part silently lost the floor-level slice.
Copies now skip the trim: their ends are where the source drew them.
* chore: re-trigger duplicate PR check
---------
Co-authored-by: Dahak Builds <dahakbuilds@users.noreply.github.com>
Breaking a dimensioned line left the length on the first piece, which the solver stretched
back to the whole length, skewing the line. A cut on (or a hair from) an end, as where a tangent
arc meets a line, made a zero-length piece with its coincident and collinear ties every time
Break was clicked there.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Redesign General preferences
* Update Preferences regression fixtures and gallery coverage
* Keep measured Preferences faces out of neutral exclusions
* Keep the profile name entry in Preferences search results
The redesigned Profiles section had no search-visible control, so a query
such as "profiles" dropped the whole section and the name field could not
be reached with a search active. Report the entry row as a hit so the
section stays on the results page, and point the typing-focus regression
test at the redesigned "Profile name" field and "Save as new" button. It
still checks that typed profile names stay out of the search box.
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>
The refusal of sketch.project ended in "face of " with nothing after the
"of" when the reference named no body: a picked face, edge or vertex is
found by its geometry, so the body it belongs to is not in the reference.
LinkSource::describe() now drops the "of <body>" when there is no body to
name and names the kind alone ("face", "edge", "vertex", "spun
profile", "isoparametric curve"). A source that names a body keeps its
name.
Only the text changes: the command refuses exactly what it refused before.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A hole placed on the side of a cone with no direction drilled along
the nearest display-mesh triangle's normal, up to 2.6° off the
surface's normal and sideways. Planes, cylinders and spheres already
gave their exact normal; cones and tori now do too, from the surface
the kernel reports for the face.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #11080
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Viewport: the shader compiles in browsers again (#11080)
The ambient occlusion turn added in #9335 mixed `<<` and `|` without
parentheses. naga accepts that, so the desktop app ran, but browsers'
WGSL compiler refuses it: the sc_viewport module was invalid, every
pipeline built from it failed, and the web app drew nothing once a
design was open. The line gets its parentheses, and a test checks every
shader in the repository for the rule.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* Move to Component: a sketch on a face inside the moved part goes along (#11063)
A face attachment stores the placement path it was found through. Moving
the placement into another component changes that path, the stored one
no longer starts at the design, and the move was refused as breaking
the sketch. Face attachments of sketches and construction planes, and
an emboss's face, now follow the move as the session's paths already do.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Move to Component: attachments made through any placement of the old parent follow
A parent placed more than once is one definition: a face attachment made
through another placement of it than the one named also ran through the
moved placement and broke. Every stored path through the moved placement
now runs through its new place. A path that would grow past the 64
placements a design keeps is refused instead of saved unreadable.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
A flange picked on one segment of a stepped base outline ran the base
face's whole length: trimming the picked edge back to the bend line
merged it with the outline's own edge on that line beyond the step, and
the flange took the merged edge. It now stays off the base's edges that
already lay on the bend line, so it spans the segment picked, with no
relief there: the base is already back at the bend line. The same
outline made by cutting notches into a rectangle gave the right flange
already.
Assisted-by: LLM
* Sketch: a dog bone's outline outside it meets outside both notches (#10984)
Where two tangent arcs touch at a cusp, the copies of the two arcs cross
twice, both crossings as far from the cusp, and rounding chose one: on
one side of a dog bone the outline ran both copies on and drew a loop
into the bone. Such a tie now takes the crossing that cuts both copies
short.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Dog bone test: the outline goes outside, away from the picked bottom line
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Rename: a refused inline rename keeps the editor and says why (#10654)
commit_rename dropped the command's Result, so a refusal closed the editor
with no message and left the old name. It now returns the error, shows it in
the status line, and the inline editor stays open.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Rename: keep the command's status line when the editor stays open (#10654)
The command completion already explains the refusal; do not overwrite it with
the raw engine error.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Extrude started with the sketch still open ends the sketch on its way. With
"If a sketch ends: Return to the previous view", that end starts a camera
animation, and turn_for_extrude read the camera through settled_cam() - the
target of that animation - so the view saved as "before Extrude" was where the
sketch end was already turning, not where the command was clicked. The view is
now taken before the sketch is finished.
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
The check that lets a key go only over HTTPS or to this computer ended the host part at `/` or
`?`, while the HTTP client also ends it at `#`, so the two could disagree about the host. It now
ends where the client ends it, and over plain HTTP the host part must be exactly this computer's
name with at most a valid port: no user name, backslash or stray character.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #9914
Co-Authored-By: Claude <noreply@anthropic.com>
* test: Move/Copy copy of a picked component (#9914)
Tests for a component picked in Move/Copy with Create Copy: it is copied as an
independent component placed with the move, the original stays, one undo removes
the copy; a body picked with Create Copy stays a body copy; a component picked
without Create Copy moves its bodies as before.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: Move/Copy copies a picked component as a component (#9914)
With Create Copy ticked and the component pick mode on (or a Browser component
row), a picked component is copied as an independent component (Paste New,
component.paste_new) and the copy moves by the move's transform (occurrence.move
in the world). Bodies picked still copy as bodies; without Create Copy a component
still moves its bodies. The copy and the bodies are one undo step.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: solid.move copies of components reply like a move and roll back (#9914)
A copying move with components replies as a solid.move does: the bodies' feature reply,
with the copies listed in occurrences. The bodies are mapped to their frame before any
copy is made. The solid.move help documents occurrences. A copy that fails part way
changes nothing and leaves no undo step.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: Move/Copy takes a picked component whether or not Create Copy is ticked (#9914)
A component picked in the component pick mode, or a Browser component row, goes into
the Move bodies input as a component, so Create Copy can be ticked after picking it.
Without Create Copy the component still moves its bodies. Not while editing an
existing move. The Browser model tests read the bodies the command would move.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: Move/Copy preview draws a copied component where the copy lands (#9914)
The fast preview took the component's bodies from the listed occurrences as well as the
bodies, drawn moved by the move, so a component copy previews at the moved place
instead of the scratch design's unplaced states. Test: a component-only copy and a
mixed copy preview where the copy lands.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: a copying move refuses bodies that are not a list (#9914)
With components, a bodies value that is not a list was ignored; without components it
is refused. It is refused now, before anything is copied.
Co-Authored-By: Claude <noreply@anthropic.com>
* test: an edited move never takes a component pick, through the click (#9914)
Clicks the component in component pick mode while an existing move is edited: the pick
stays a body. Fails when the editing condition is dropped.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: Move/Copy preview takes components only for a copy, and the mixed test can fail (#9914)
The engine refuses `occurrences` without `copy`, so the preview leaves
such params to the full preview too. The mixed-copy test now gives the
component a taller box, so its height shows the component was drawn.
Co-Authored-By: Claude <noreply@anthropic.com>
* docs: component_pick no longer needs Create Copy ticked (#9914)
Co-Authored-By: Claude <noreply@anthropic.com>
* test: a component picked with its parent is copied once (#9914)
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* claim: #9284
* Localization: Russian "Close draft" reads as a draft analysis (#9284)
The report form's "Close draft" button said «Закрыть анализ уклонов»
(close draft analysis). It closes the report draft: «Закрыть черновик»,
the word the Russian catalog already uses for this draft.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Hole: a hole moved off the material says it removes no material (#10701)
A hole whose position, parameter or sketch point moves it off the part
cut nothing and said nothing: the feature had no warning, and a
parameter sweep listed the value as fine. A hole whose tool is clear of
every body it cuts now warns "the hole removes no material" ("1 of 2
holes remove no material" for several), as a pattern does of its
copies. The check compares boxes only, from the meshes the hole already
found its face on, so a large hole pattern costs no extra booleans.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Hole: a hole wider than the part says it removes all of it (#10702)
A through hole wider than the part removed the whole body with no
message, also when a parameter widened it later. The hole now warns
"the hole removes all of Body1" when a body it cuts is gone after the
cut.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The card's area is as tall as it was the frame before, and the first
frame laid the body out shorter than it ends up, so the scroll area
stayed short and the Hatch row (and Flip, for a tilted plane) was cut
off. The card may now grow to the height it is allowed.
The offset is shown to the design's length decimals and a tilted normal
to three places, as the pill shows them.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #10127
* Assemblies: a construction axis follows its component when the component rotates (#10127)
A construction axis or point built on a body now goes with that body when
Move/Copy moves or turns it. Each construction records the bodies its picks
lie on; the Move feature (not a copy) carries along every construction whose
bodies it all moved, with the same transform in the construction's component
frame. One that also reads something fixed stays where it is.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Markus Kaltenbrunner <kaltenbrunnermarkus@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
sheet.fold refused any fold whose bend zone wasn't a rectangle. It now
crosses the sheet's own outer edges, slanted or on any curve: the outline
is cut exactly across the zone, the panels keep their real outlines, and
flanges and other folds still go on them. The flat pattern's DXF keeps the
curve as drawn. A sharp corner in the zone stays refused.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
A permission question counted as one of SolidCraft's own tools when its title merely contained
"solidcraft". Now the whole title must be a tool of the chat's own MCP server, named as
`mcp__solidcraft-app__<tool>`, and its kind must be `other` or not given. Anything else goes through
the user's mode and remembered answers, or is asked in the panel.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #10704
* Section Analysis: view.pick skips edges and vertices in the removed half
near_edges now applies the same section `kept` predicate the mouse picker
uses (viewport::pick, since #6722), so an agent picking through view.pick /
ui.pick no longer gets edges and corners the user cannot see.
---------
Co-authored-by: Dahak Builds <dahakbuilds@users.noreply.github.com>
A client could connect and send nothing, or only blank lines, and hold one of the 16 connection
seats for as long as it liked. All reads before the token now share one five-second deadline; the
connection is then closed without a reply. An authenticated connection may idle as before.
`solidcraft-cli mcp --connect` used to open a connection at start and hold it until the first tool
call. It now only checks that the app is there, and the first call connects.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
On Windows the token file is restricted with `icacls`, which may not finish in time (#6145). That
counted as success everywhere, and is safe in the default folder, the user's own local app data.
A folder picked with SOLIDCRAFT_CONTROL_DIR may be readable by others, so there a timeout now
removes the still empty file and returns an error. The default folder behaves as before. The
decision is a small function of its own, tested without running icacls.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
A design's lock file went through the same writer as the design, which follows a symbolic link
so a linked design is saved where it lives. The lock now has a writer of its own that refuses
anything at its path but a regular file and replaces only that name. Reading a lock takes only a
regular file and no more than a lock's size. Saving a design through a link works as before.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Also points the links at tangency-labs/solidcraft. The old list asked for reports of file access outside the allowed folders, and for crashes.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Selectable transcript labels do not request TextEdit focus, so the global
clipboard handler also copied, cut and pasted selected CAD geometry while
the user worked with chat text.
Keep keyboard ownership with the visible chat after a pointer press until
the next press outside it. Release it when the panel is hidden, and do not
use selections from other native viewports as a global CAD shortcut guard.
Cover real label selection, clipboard events, fallback key events, quick
select-all, hidden panels and deliberate return to the CAD viewport.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #10689
Co-authored-by: Cursor <cursoragent@cursor.com>
* Edit Hole: seed the face where the hole is now (#10689)
Project the stored position onto the drilled face along the hole direction when opening the editor, matching evaluation, so OK works after a parameter moves the face.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
* Holes and cuts: count the bodies a cut works on in the cost tests
A test helper, cut_work, adds a feature at the end of a design and reports
how many bodies its cut ran booleans on and which bodies it left exactly
as they were (same geometry, key and face names). A new test pins that a
cut still goes through the boolean where its tool touches a face, runs
along an edge, passes a hair beside a body or drills into a dome.
The sheet metal hole (#10226) and cosmetic thread (#10227) work reuse the
helper for their own shortcuts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Holes and cuts: leave bodies the tool cannot reach untouched without a full cut
A cut ran a whole boolean against every body of its component, and for a
body its tool does not reach that boolean only handed the body back: about
7 ms per body, so a hole in a design of 200 blocks took 1.3 s. Now a body
whose box lies clear of every tool's box is left as it is. Both boxes are
their meshes' grown past how far the exact faces can curve out from them,
the same test sheet metal cuts already use, so a tool that touches a body,
grazes it or drills into a dome still goes through the boolean. Bodies
shown in another frame, mesh bodies and Intersect (which removes the
bodies it misses) are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Cosmetic tap markings: look for their cylinder only in nearby bodies
A tap marking (a Thread feature, or the marking of a tapped hole) looked
for the cylinder at its point in every body of the design, and each
search meshes the body afresh: with many bodies and many markings that
grew quadratically (200 markings on 200 cylinders: 33 s of a 33 s run).
A body is now passed over when its box, from the mesh it keeps and
grown past that mesh's deflection, lies farther from the point than the
search itself would look (a twentieth of the body's size, plus its own
mesh's deflection). The search finds no face there, so the marking and
everything else stay the same.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #5388
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch: rebuilding a projection keeps the sketch's own curves on its points (#5388)
When a projected face changed shape, update_link rebuilt the link with
drop_link_entities, whose remove_points also deleted every curve using a
projected point: a circle the user drew on a projected hole's centre vanished,
and the extrude on it lost its profile. Before the projected points go, the
sketch's own curves on them move to a free point at the same place.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Selecting a plane in the view now draws the grid on it, so the plane a
sketch would use shows before the button is clicked. Deselecting returns
the grid to the ground.
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
A Cut To Object on a curved face ran the tool through the target and kept
the tool less the body, the piece at the start. From outside a curved wall
to its inside face that piece is the air before the wall: the cut took
nothing, or the tool lay in the curved face and the boolean failed on the
coincident faces.
Where the target's material ends at the picked face under the whole
profile (rays over the profile cross it once, material before, none
after), the tool now ends flat halfway across the gap beyond the face. It
takes what lies before the face and meets no face of the body. Otherwise
the trim is as before.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Save As: refuse a name that opens as another kind of file (#9745)
file.save and file.save_as wrote a design container under any name, so a
design saved as part.step was refused when opened again and part.stl opened
as a broken mesh. A name doc.open reads as a STEP/CAD exchange file, a mesh,
a 2D drawing, a point cloud or a SolidCraft drawing is now refused, pointing
to .sc and, for export formats, to file.export. Names that open again as the
design (.sc, legacy extensions, .json, anything else) save as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Save As: check the folder scope before the design name (#9745)
A path outside an agent's folders is refused for that first, whatever its
extension, as before; the design-name check runs after it in `write`. Fixes
solidcraft-mcp tests::tool_calls_stay_inside_the_agents_folders, which saves
`x.step` outside the agent's folders and expects the scope refusal.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #10673
Co-authored-by: Cursor <cursoragent@cursor.com>
* AppImage: point the taskbar at the installed icon PNG (#10673)
Install hicolor icons before writing the desktop entry, refresh the icon cache, and set Icon= to the absolute 256×256 path so Cinnamon and similar panels show the mark instead of a thin strip.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
* claim: 10331
* Assemblies: a second planar joint turns its part a half turn to meet (#10331)
A second planar joint between two boards that mates faces pointing the
same way needs the board turned a half turn on the first joint. The loop
solve starts exactly turned away, finds no way round, and left the new
joint as a conflict; Move then said the part has no movable joint axis.
A new loop-closing joint now tries its own angle, each angle on the
joint path between its two parts, or one of each, a half turn round, as
the first joint of a chain moves its part into place, and keeps the
first that closes the loop without a new conflict. Faces that can never
meet stay a conflict and nothing moves.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Move test: give the blocked part faces that can never meet (#10331)
The blocked-Move test from #10509 used two boards whose second planar
joint mates two faces pointing the same way. That joint now closes its
loop with the board turned a half turn, so the scenario no longer
conflicts. Mate the yellow front against the red top instead, which no
turn on the first joint can bring together.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Markus Kaltenbrunner <kaltenbrunnermarkus@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #10688
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Feature editor: Undo, Redo and the History bar cancel an open editor first (#10688)
Undo, Redo and History steps changed the design under an open feature
editor, whose OK and Cancel then refused it. They now cancel the editor
first, putting its marker back, as starting another tool already does.
While an editor is still opening they wait for it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
A relation given no values already started from where its parts sit
(relation_values), so the part moves no further than it must. A typed joint
(planar, revolute, cylindrical ...) added between parts another joint
already relates started its values at 0 instead, so the loop solver began
far from the current placement and could report a conflict for a joint that
agrees with how the parts are placed.
Seed every loop-closing joint, typed or relation, from the current
placement when no values are given. Joints in an open chain and joints with
explicit values keep their initial values as before.
* claim: #10316
* Insert Part: ASME/ANSI inch hex cap screws, hex nuts and SAE/USS washers (#10316)
Part of #10316. New standard-part families, tabled in inches as the
standards give them and built in millimetres: hex cap screw (ASME
B18.2.1) and hex nut (ASME B18.2.2), 1/4 to 1 in UNC and UNF from the
thread catalogue, and plain washers SAE and USS (ANSI B18.22.1 Type A).
Cap screws take the standard inch lengths, 1/2 to 8 in, defaulting to
the first at or above 2.5 d.
Lengths read as fractional inches in Insert Part, Change Part Size, part
names and part numbers. Insert Part gains a Unit system list (Metric,
Inch) above Type; it opens on Metric. parts.library reports each
family's system.
Inch parts get no cosmetic thread: solid.thread takes only metric
designations (#9571), and the old path would have made a wrong metric
mark. 9/16 waits for the thread catalogue; split lock washers and
carriage bolts follow in later PRs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The editor accepts revision changes that only changed what is shown,
counted by the display revision, but only hiding and showing counted
there. Section Analysis, the surface analyses, continuity, the
selection, the status pill catching up and view.render_select bumped
the revision alone, so turning a section off with an editor open made
OK refuse ("the design changed while the feature editor was open") and
Cancel leave the timeline rolled back. They now go through
refresh_display, as visibility changes do.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #10852
* Circular pattern: the axis origin takes length expressions and follows them (#10852)
solid.pattern.circular with axis {origin, dir} kept its origin as plain
numbers, so a pattern about a parameter-driven point stayed where the
point was when the pattern was made: the seed moved with the parameter
and the copies did not. Expressions were refused (#248).
The pattern's origin is now a point of expressions, as a Mirror plane's
origin is: numbers or length expressions, evaluated on each rebuild, and
renamed with their parameters. Plain numbers are saved as numbers, as
before. timeline.edit sets pattern.origin as expressions, and editing the
pattern in its dialog keeps them. One pending format change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* claim: #9660
Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>
* Move bodies of differently turned components in the world together (#9660)
A Move feature moves each of its bodies in the body's own frame by one
vector and one turn. `solid.move` mapped the world vector and axis into
the frame of the first body listed only, so a body in a frame turned
otherwise (a part inserted with a non-default direction, moved together
with a block of the root) moved along its own axes.
When the bodies listed lie in frames that turn differently (or, for a
turn, also sit in different places), the move is now split into one
Move feature per frame, each with the vector and axis mapped into that
frame, in one undo step. Bodies sharing a frame still make one feature.
Fixes#9660
Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>
---------
Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>
A surface extruded from a fit-point spline is one B-rep face per span,
joined by hidden seams; the viewport picks and highlights it as one face
(Mesh::surface_patch). sketch.intersection_curve turned a face pick into
the single nearest face, so the curve stopped at that span's ends.
project3d::mesh_of now returns the picked face's surface patch and
mesh_intersection keeps the triangles of every face in it. Body sources
and Project to Surface are unchanged.
* claim: #10306
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019FAWPuFoh9qxn2uS2j53QZ
* Language: a Chinese system tag goes by its script, then its region (#10306)
macOS lists its languages with a script (zh-Hant-TW), which no catalog
matches exactly, so the base zh (Simplified) was picked for a Traditional
Chinese system. Hong Kong and Macao tags got Simplified the same way.
A Chinese tag now picks zh-TW for the Hant script, zh for Hans, and
without a script zh-TW for the TW, HK and MO regions, zh otherwise.
Other languages keep the exact tag, then the base language.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* claim: sectiontilt
* Section Analysis: tilting the plane no longer rebuilds the outline index every frame (#10373)
The section outline cut through an index of slabs along one normal, rebuilt
whenever the normal changed: tilting a Section Analysis rebuilt it every
drag frame (about 0.15 s on a 2.5 million triangle part). The index is now a
grid of cells holding each triangle by where its box sits, with the box
around each cell's triangles, so it serves every normal. It finds the same
segments, in the same order and to the bit, as cutting every triangle.
The viewport keeps one index per shown body mesh, no longer per normal.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Markus Kaltenbrunner <kaltenbrunnermarkus@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
`sheet.perforate` takes an optional `sketch` (and `profiles`): the field's
outline. Holes go only inside its closed profiles, out of their inner loops,
`margin` clear of its edges, on a grid from the outline's lower-left corner.
On a folded sheet the outline maps through each panel the sketch lies on and
is clipped to it, so it never reaches a flange in the flat; a sketch on no
face of the sheet is refused. A mirrored copy mirrors the outline, and an
edit that names no sketch (the Perforate dialog has no outline) keeps it.
Without a sketch the field covers the whole sheet as before.
The feature saves `sketch` and `profiles`, a sheet's perforation its outline
in flat coordinates: one pending format note, no migration.
Assisted-by: LLM
* claim: #8822
* Tangent Plane: line it up with a reference plane, at an angle (#8822)
`construct.plane.tangent` takes an optional `reference` (a plane or planar
face) and `angle`. On a cylinder, cone or torus the plane touches where the
face turns towards the reference, turned by the angle about the face's axis,
on the side of the pick; on a sphere it is parallel to the reference. The
plane follows the reference when it moves. The Tangent Plane dialog gets a
Reference plane input and, once one is picked, an Angle field; editing the
plane brings both back. Saved as a pending format change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #9074
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* OBJ export: welded as STL and 3MF are, so a curved body opens again closed (#9074)
The OBJ writer joined vertices only when their coordinates were equal bit
for bit, but each face of a curved body writes its edge points again a few
ulps apart, so neighbouring faces shared no vertices and the file reopened
as an open mesh with no volume.
It now welds each body as the STL and 3MF exports do (threemf::weld_tagged,
with the same tolerance), and every corner keeps its normal: the normal of
the original corner it came from, or for a point a side was split at, the
normal interpolated across the triangle.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* Ambient occlusion: the radius follows what the frame draws (#9327)
In an empty design the radius came from the model's box, which is empty, so a
preview got the smallest radius and the depth map's precision showed as specks.
It now comes from the box the depth range uses, and with nothing to draw there
is no occlusion.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Ambient occlusion: the term drawn once and blurred by depth (#9327)
The lit pass worked the term out for every fragment it shaded, under a pattern
that changed from pixel to pixel, which read as grain. The term now has its own
texture, drawn once for each pixel of the prepass, with a fixed 4 x 4 pattern, and
a 4 x 4 blur that keeps to each pixel's plane takes the pattern out without
crossing an edge. The lit pass reads the result. The prepass and the term share
their pipelines and draw walk.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #9373
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Timeline folders: editing a feature in a folder leaves no stale id, and such designs open again (#9373)
timeline.redefine runs the edited feature's command on a scratch copy with the marker just after
the feature, and takes the new definition from the feature that run makes. Inside a timeline
folder that trial feature joined the folder too; it was removed from the feature list but not
from the folder, and its id, handed back, was the next feature's. Each edit left that id in the
folder (the same one, edit after edit), naming no feature or, once a later feature took it, a
feature twice, and the reader then refused the design: "damaged design: invalid feature in a
timeline folder". The trial feature now leaves the folders as it leaves the feature list.
The reader drops a folder entry naming a feature or joint the design doesn't have, or one a
folder already holds, and opens the design: folders only group the timeline, so nothing else is
lost. A folder damaged in itself is still refused.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Report focused keyboard ownership from the shared text editor, matching the other Preferences fields. Regress real Name-field typing, focus retention, unchanged search text, and saving the resulting profile.
A mirror of placed bodies follows its source, so each frame of a drag evaluated
it and made new copies with empty mesh cells. A reflection can't share its
source's triangles, so every copy was tessellated from nothing, and so was
anything an evaluated feature after the mirror made.
Keep the first copy of a body with its source: each later copy is a proper rigid
move of it, so a world state draws them from one set of triangles. The model
keeps each copy's own lazy mesh. A feature evaluated again that gives the same
solids (an import) keeps its meshes.
A mirror of placed bodies no longer searches its copies' revolved faces when no
later feature can build on bodies. That depends on the design alone and is part
of the mirror's placement digest.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Since #10183 the app follows the system's language, and the tests followed it too, so a test
naming an English word failed on a PC set to another language. CI's runners read as English
and never saw it. In tests the system's language is now English: `cfg!(test)` for this crate's
own tests, as the engine's folders do, and the harness for the tests outside it. The preference
stays `system`, and the app is unchanged.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: 10368
* Web offline test: each copy of a real build gets a file of its own (#10368)
With --site, the six builds were copies of one real build that differed
only in index.html. The overlapping-downloads step holds back the files
the next build lacks, so nothing of v5 was held: its download finished
before v6 launched, v6's worker then retired and deleted it (correctly,
no page used it), and the step waited 240 s to see it complete.
Successive deployments differ in more file names than that (Trunk names
files after their contents). Each copy now gets own-<build>.txt, like
the stand-in's build-named script, so v5's own file is held as the step
intends.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Markus Kaltenbrunner <kaltenbrunnermarkus@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Typing a parameter's name now lists every parameter whose expression
uses it, and a value finds the parameter that holds it. The filter
still reads names, comments and groups, and a text parameter's text.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
A straight line of another sketch, picked into the Project dialog, came
in as a named sketch curve, which the projection preselection did not
know: it refused with "select geometry or bodies that can be projected"
although the OK button was live. The same reference the external pick
stands for is now made for it, so the line projects (#10291).
Co-authored-by: vdirienzo <vdirienzo@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* wip: the diagnosis of #10181 (the bug reproduces on current main)
sketch.project has .enabled(in_sketch) (crates/engine/src/cmd/sketch_project.rs:24)
and in_sketch (crates/engine/src/cmd/mod.rs:285) answers 'no sketch is being edited'.
Pressing P outside a sketch goes keymap::press -> sketch_tool_for
(crates/ui-egui/src/keymap.rs:451-457) -> app.start("sketch.create") with
d.then_start = Some("sketch.project"): the sketch IS created, but the Project
dialog opens and the command is refused, so the dialog shows the red row and the
'Create Issue...' button, which opens the Report a bug form over the viewport.
The probe here reproduces the bug on the CURRENT main (two variants). It is a
diagnostic probe, not the regression test: it must be replaced before a PR.
* test: a context error offers no Create Issue button (#10181)
The Project panel showed the red row for a CONTEXT error ('sketch.project is not
available right now: no sketch is being edited') and offered the 'Create Issue...'
button, which opens the Report a bug form over the viewport. A context error is
not something to report, so the panel must not offer it.
RED: the test fails with the exact message of the issue.
* fix: a context error is not something to report (#10181)
The Project panel offered "Create Issue..." for the error the engine
raises when the command is not available in the current context
("`sketch.project` is not available right now: no sketch is being
edited (Create Sketch or Edit Sketch first)"). That refusal is guidance
about the context, not a failure of the design, so there is nothing to
report: ending the sketch under an open Project panel should not invite
the user to file a bug.
error_rows already spared the missing-pick errors ("select ...", "pick
...") the same way. It now spares the context refusals too, using the
engine's own kind rather than the message text: CommandSpec gains
disabled_refusal, which is the engine's own Disabled refusal for the
command in the current session, and the UI matches the dialog's error
against it. A real failure of the command (an invalid face, say) still
shows the red row with "Create Issue...".
* style: rustfmt the test of #10181
The formatter joins the two asserts of the test onto one line (the
repository sets max_width = 150 and use_small_heuristics = Max). The
tokens of the file are unchanged: the diff is the line breaks of the
asserts and nothing else.
* Angle fields: a decimal point typed into an angle stays (#10582)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Angle fields: a decimal point typed into an angle stays (#10582)
Edit Canvas applies its Angle box at each keystroke and keeps the angle
in radians, so the box gets back a hair off the value it set; egui's
DragValue takes that for a change made elsewhere and rewrites the text
being typed, dropping a decimal point just typed (-3.7 became -37).
angle_field now hands the DragValue the value it set while the value it
is given is within 1e-9 of it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* Tangent Arc: a first click off a curve's end is refused at once, saying where to click (#10653)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Tangent Arc: a first click off a curve's end is refused at once, saying where to click (#10653)
The Tangent Arc tool took any first click as its start; a click on a
line between its ends snaps to a place that is no point yet (on:l3:...,
mid:l1), and the second click then failed with "unknown sketch point"
naming that internal reference.
The tool now refuses such a first click at once, with its own hint as
the error, asking the command's rule (tangent_arc_starts_at, factored
out of arc_tangent: the picked point or one at the same place, and a
drawn curve leaving it). The command refuses a snapped start with its
existing message that start must be the end point of a line or arc.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* Tests: the autosave scenario crashes once its autosave has landed (#10690)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Tests: the autosave scenario crashes once its autosave has landed (#10690)
s25_autosave_recovery 'crashed' right after the extrude; under load the
autosave of it, written on a worker thread, had not landed, so the next
launch recovered the earlier autosave without the body (step 16).
The harness's crash restart now runs frames, bounded, until the active
autosave has caught up (Autosaver::caught_up: no write running and the
last autosave holds the design as it is). The app's own ticks still do
the writing, so an autosave that stopped writing still fails the test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* claim: #9087
* Control channel: report the dialog as data, the open windows, and what ui.start opened (#9087)
`ui.inspect.dialog` (and `ui.editFeature`'s reply) was the dialog's Rust Debug text, which
changed with every refactor and couldn't be parsed. It is now an object: the dialog's command,
title, the feature an edit rebuilds, its selection inputs, its error, and the commands OK runs
(or what it waits for). `hover` in `ui.inspect` and `ui.selection` is a selection-like item
instead of Debug text.
`ui.inspect` gains `windows`: the tool windows open beside the model (Parameters, Bill of
Materials, Design Properties, Sketch Report, material card review) by the command that opens
each, and the Help menu's (About, Updates, Shortcuts, Crash Reports, Report, Messages) by their
`ui.help` item. Before, an agent couldn't tell these windows were open.
`ui.start` and `ui.editFeature` reply with what they opened (tool, dialog, windows), the status
line, and `pending` when the start waits for the history and opens on a later frame.
Scenario `dialog` checks match words of the dialog's state from the app directly, so the
scenario files are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #10675
* agent chat: Copy message on each sent prompt and assistant text block (#10675)
A visible Copy message button on each copyable transcript message and a
right-click menu with the same action. Assistant replies copy their
original Markdown, prompts copy the text as entered; picture-only prompts
and thoughts get no action. Hover-only message areas keep partial text
selection working.
* agent chat: complete Copy message locales and tests (#10675)
* agent chat: route the message copy menu through ui_kit::Container (ui-containers gate)
---------
Co-authored-by: Dahak Builds <dahakbuilds@users.noreply.github.com>
* Sketch: Remove Spline Point takes one fit point out of a spline (#10783)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch: Remove Spline Point takes one point out of a spline (#10783)
sketch.spline.remove_point takes one fit or control point out of a
spline. The spline keeps its id and runs through its other points, which
stay where they are. The point goes with the constraints on it, unless
another curve uses it. A closed spline's closing point, an end joined to
other geometry, the last 2 points (3 when closed), and projected, offset
or patterned splines are refused with a message.
The right-click menu of a spline point, picked alone, offers it beside
Reverse Spline Direction.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* claim: #10703
* Sketch: do not tie expression-placed points to the origin with an automatic Coincident
A point whose coordinates are parameter expressions follows those
expressions. When they came to 0 at draw time the point landed on the
origin and connect_points tied it there with a Coincident, exactly like
a snapped click; the next parameter change then conflicted with the tie.
connect_points now skips points that carry exprs; plain-number points
keep the snap.
---------
Co-authored-by: Dahak Builds <dahakbuilds@users.noreply.github.com>
* Face names: one name on two bodies is no split face (#10699)
A hole placed on the top of one of two bodies an extrude made, or on a
piece of a split body, warned "face `F2:top` was split; using the piece
nearest the picked point": the face name is on both bodies, and any
name found more than once counted as split. A name on several bodies,
one face on each, now finds the face the point lies on without the
note; pieces on one body, or a point on none of them, still say split.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Hole: look for a face name shared by two bodies on the body the point is on (#10699)
The lookup over every body counts a name on several bodies as split on
purpose (a copy's face is not a piece of the source's, see
`the_same_face_on_a_copy_is_not_a_piece_of_the_named_bodys_face`), so
the first change, which made it unique there, is undone. A hole whose
face name is found split while its point lies on one of those faces now
looks again on that body alone: one face of the name there is the face,
whole; pieces on that body still say split.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The residual of a length dimension is the magnitude of the distance between
the line's two ends. At zero the magnitude has no derivative, so the rank
analysis read the constraint as if it were not there and the sketch reported
a degree of freedom that was not there.
At zero the two ends of the line meet, so the residual says it with the
vector between them, the way a coincidence does. That is the treatment a
distance of zero already gets (upstream #4397); the length family was left
out of it.
Regression test: crates/sketch/src/tests.rs, zero_length_keeps_the_line_constrained.
Co-authored-by: Homero Thompson <homero@local>
* claim: #8266
* Computing toast: a Cancel that gives up on the move (#8266)
The Computing toast for a history command (marker move, undo, redo,
suppress, reorder) or a delete's check computing off the window thread now
has a Cancel button. It drops the job and the commands queued behind it;
the design stays as it was, since a deferred command only runs for real
once its evaluation is in. A feature edit applying keeps its dialog's own
Cancel, so its toast shows none.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: thocsmith <193096300+thocsmith@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #10620
* widen dialog form-card label columns to the measured label width (#10620)
* fix compile of the grow tests
* grow the card only past the label cap and tag the form scope in place (#10620)
* keep scope_grow crate-internal so the gallery scan stays element-only (#10620)
---------
Co-authored-by: Dahak Builds <dahakbuilds@users.noreply.github.com>
* claim: #10510
Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>
* Sketch: dragging a centre rectangle's constrained centre point keeps the rectangle's size
A centre rectangle's centre sits on the middle of its diagonal, and a
press there picked the diagonal's midpoint before the centre point. The
drag then took the diagonal by its middle: with the centre free the
rectangle moved along, but with the centre held (on the origin, on a
line) the diagonal turned and stretched about it, and the rectangle
grew or shrank with the pointer, many times its size.
A drag that starts on a line's middle where a point is held by a
Midpoint constraint now drags that point, as a drag that starts on the
point does. A centre held on the origin stays there and the rectangle
keeps its size; a centre on a line slides the whole rectangle along it;
a free centre still moves the rectangle. Clicking the middle still
selects the line's midpoint for constraints.
Fixes#10510
Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>
---------
Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>
With a section on, Slice in the Sketch Palette lit up but the model was
still cut only by the section: the viewport took the sketch's plane only
when there was no section. Slice now takes it whenever it is on in the
sketch being edited; the section comes back when Slice is turned off or
the sketch closes. A slice is hatched as it is without a section.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #10874
Co-authored-by: Cursor <cursoragent@cursor.com>
* Insert drawing: name the sketch size cap when a file is too dense (#10874)
A large SVG/DXF failed with opaque \"sketch is too large\". Say the 20000 point/curve limit and that scale alone does not reduce entity count, and refuse dense drawings before adding them.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
`face_of` called `Body::faces`, which tessellates the whole body, on every
solve. Face origins went through the cached `ModelBody::joint_faces`, at
the same tolerance, but edge origins never did, so one edge origin on a
large body cost a drag or a Drive Joints step far more than the solve.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #9874
* Add shared Preferences cards, figure painters and gallery entries to ui_kit
* Preferences: Arabic, Hebrew and Vietnamese carousel labels
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Use the app's selection colours in the Preferences cards
* Measure Preferences figure inks and use shared palette metrics
---------
Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
A corner dialog copied the preview's error into its own, where nothing
cleared it when the preview recovered. The dialog already shows the
preview's error, so the copy is gone.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Tests: io's byte mutator moves to a testkit crate, a dev-dependency only (no behaviour change)
`solidcraft-testkit` (the Testkit class xtask/src/layers.rs reserves) holds the mutator and the
no-panic check unchanged: the same seeds, mutations, budget and doc comment. io's reader fuzz
tests use it as a dev-dependency and give the same case counts; the mutator's own test moves
with it. The co-editing wire's decoders will fuzz with it too (#5184, CE6).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Cargo.lock: testkit at the workspace version
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>
A pull request outside crates/kernel, crates/geom and vendor/truck* is no longer labelled for a
kernel word in its title. Issues and discussions keep the title check.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #7481
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Coil: step arrows on the Revolutions field (#7481)
The Coil dialog drew Revolutions as a plain expression row, the one
count without the step arrows the pattern quantities have. It is now a
count row from 1 to 200 turns (the helix's limit); a fraction still
types in, and an arrow steps it to the next whole turn.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
A chamfer's warning quoted the full generated names of fourteen split edges,
122,701 characters (#8541). Names over about 80 characters are now quoted by
their first 48 and last 24 characters.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Offset Plane and a cosmetic Thread have no live preview, so their edits' OK ran on the window
thread and recomputed every later feature there. Any edit now goes through the off-thread
transaction. The editor's pinned original is used only while the dialog previews, as
open_feature_edit keeps it: a Thread switched from Modeled to cosmetic edits the design as it is,
so a body hidden meanwhile doesn't refuse it as a changed design.
* claim: #4986
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Press Pull on a chamfer's face changes the chamfer's distance (#4986)
A chamfer's face is flat, so solid.press_pull and the Press Pull start
path in the UI took it for a planar face and extruded it. Both now look
for a fillet's or chamfer's face before asking whether the face is
planar, so a chamfer's face reopens the chamfer and its distance grows
by the distance pulled, as a fillet's radius already does. A first pick
with Press Pull already open is routed the same way.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Press Pull on a blend face puts the arrow on the edge clicked
A fillet or chamfer reopened by Press Pull from one of its faces drew
its radius or distance arrow on the feature's first stored edge, which
can be a corner the user did not click and cannot see (the Sample
Plate's fillet, from the far corner). The click's point now goes with
the reopened dialog as a hint that is never saved, and the arrow sits
on the feature's edge nearest it, at the point on that edge nearest the
click. Opened any other way (the history, Edit Feature) the arrow stays
on the first edge as before.
A click point that is not finite is not kept, and a point on a flat face next to a chamfer still extrudes that face (tested).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The live preview of extruding a picked face built its walls from the
face's display triangles and drew a line up the wall at every boundary
point, each wall piece shaded flat. A round face (the Bolted Flange's
flange) looked like a polygon while extruding. Lines now go up only
where an edge of the face ends and the outline turns there more than
its curves do, and a wall's normal turns with it between smooth pieces.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Colour popover: a held step button keeps stepping, and R, G and B have tooltips (#10201)
The step buttons beside each colour number sensed clicks only. egui
stops counting a press as a possible click once it outlasts its longest
click (0.8 s), so the held button stopped repeating after 8 steps. They
now sense drags too, which keeps a long press down on the button until
it is let go.
R, G and B now carry tooltips (Red, Green, Blue) as H, S and V already
did: a single letter doesn't name the colour in every language.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Colour popover: the R, G and B tooltips in Vietnamese too, a complete catalog (#10201)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Hole: reference Distance shows the document precision (#10693)
A measured reference distance showed the raw float (10.000000868569572 mm).
The field now reads at the document's length precision while Refs keeps the
measured value for the model, so OK still sends what was measured.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Hole: restored literals use the document precision (#10693)
Round every literal reference distance on Edit Feature, including comma
decimals and exact whole values; expressions stay as written.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
* ui_kit: search_picker, a filtered dropdown with a custom entry
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Agent chat: endpoints list their models with names, say when listing fails, and OpenClaw and OpenRouter presets (#2582)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Agent chat: a typed model goes to endpoints and to agents that offer a model option
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Agent chat: the header's model pickers search and take a custom model
The popup can't grow once it is open, so search_picker now fixes the height of what changes below
its box when it opens: the custom row or a late list could otherwise end up out of reach.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* AI preferences: pick an endpoint's model when adding it and on its card (#2582)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Agent chat: model picker rows read from the left, and a refused typed model says which
From running it: list_picker centres its text, which reads badly for a long list of ids, and an
agent's own refusal can say no more than "Internal error". The search box also asks for a repaint
while it waits for the focus, so an idle window does not stop short of it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Agent chat: docs for the model picker
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Agent chat: an endpoint's answer that is no model list says so, an empty list too, and editing lists with the kept key
A 200 without a `data` array (a web page where the endpoint should be, as OpenClaw's gateway
serves until its chat endpoint is enabled) was cached as a successful, empty listing, so the
picker stayed blank with nothing to say and never asked again. Now it is an error with its own
note, an empty list says so, and both are asked for again the next time.
The Edit dialog lists its endpoint's models when it opens, with the kept key read from the
keychain on the listing's thread, as the card does; the key stays out of the field.
The method-not-found test expects the refusal notice that names the model, as every refused
model gets since this branch.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* Agent chat: models from several providers fold under their providers inside the picker
As Chemiseblanc does in #10187: with two or more groups, `search_picker` draws each group as a
header with a chevron, the name and how many of its choices are listed, and a click folds or opens
it. Before anything is typed only the current value's group (and the one the caller asks for) is
open; a group with a match opens once something is typed; a fold made by hand stays until the popup
closes. Enter takes the first row shown, so never one folded away. A lone group keeps its plain
heading.
The header's model picker feeds its providers in as groups, so the separate provider drop-down
goes; the control channel's `provider` now says which group opens first. A header click gives the
focus back to the search box, so typing goes on filtering.
The dialog's listing test checks what kind of listing it got rather than translated words, which
the listing thread may render in another language than the test's.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* Agent chat: rustfmt the provider groups
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* Agent chat: an endpoint's models named `Vendor: Model` fold under their vendors
OpenRouter lists `openai/gpt-6.1-terra` as "OpenAI: GPT-6.1 Terra". A listing whose ids read
`vendor/model` and whose names read `Vendor: Model` now names its providers, so the picker folds
it under its vendors like a multi-provider agent; the rows read the model's name, and the id stays
in the tooltip and in the search. An agent's own `Provider: …` word still comes first. One vendor
is one flat list, as before.
The Add dialog's picker has no current model to prefer, so with nothing to prefer every group
starts open; folding is a click away.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- snap_tangent_arcs : chaque extrémité d'arc prend la ligne la plus proche
- une ligne ne prend qu'une extrémité d'un arc ; l'autre passe à sa ligne suivante
- une ligne plus loin sur le contour est ignorée quand l'extrémité touche déjà une autre pièce
- tests : 4 unitaires (project_order.rs) et the_silhouette_of_conic_rounds_closes
Co-authored-by: P'tite Tête <asso@ptitetete.com>
* claim: #1486
* Compare picture: overlap and edge offsets of the model against a reference picture (#1486)
inspect.compare_picture lays the model's silhouette over the part found in a
PNG or JPEG and slides it to the best overlap: overlap (0..1), the model's
edges against the picture's in mm at nine stations, and an overlay PNG. The
MCP tool compare_picture runs it and hands the overlay back as an image block.
inspect.silhouette grows a reusable silhouette::outline for the model side.
* chore: re-trigger checks (defaults line already present in PR body)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* i18n: carry the Compare Picture label in Arabic, Hebrew and Vietnamese too
The complete-catalog guard (every_complete_catalog_carries_every_english_message)
failed because the pending file covered only en/ja/zh; ar, he and vi are
complete catalogs and need the key as well.
* Compare picture: one uniform scale, capped fit, sturdier part finding
Review on #6416: fit no longer stretches the axes apart (width is the default,
both scales uniformly by width and reports aspect_mismatch); the fitted model
takes the same MAX_GRID cap as the picture; the border flood seeds only from
border pixels near the border's main colour, reads alpha, retries at a
stricter step, and says which case failed; the slide searches the whole
placement range instead of a tenth around the centre; the model's holes get
the picture's fill rule; crop becomes crop_px / crop_fraction with explicit
units. Pins #1486's bracket (overlap 0.865, bottom -10 mm at the upright) and
adds a regression test per problem.
* Compare picture: alpha only for a see-through border, part_fills_crop, outline-only wording
Review re-test on #6416: one translucent backdrop pixel no longer switches the read to
alpha — alpha decides only when most of the border is see-through, and any ink is then
part, so a part drawn at alpha 100 survives; part_fills_crop: true takes the crop box as
the part itself, for one view cropped tight off a board where the part takes most of the
border and the flood would keep the backdrop strip instead; a third backdrop pass compares
each pixel to the border's main colour, so a white part on (245, 245, 245) JPEG at quality
75 separates; the command, tool and docs descriptions now say the compare is outline only:
holes are not compared. A banded light backdrop stays a follow-up. A test per case.
* MCP compare: format the picture tests
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Dahak Builds <dahakbuilds@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
* Render results: keep up to eight completed renders and select or discard them
* Render results: Live and render compare with a divider, and render all shots
* fix: use comparison theme tint and reveal reset test targets
* fix: restore Unicode expectations in render workspace tests
* claim: #6528
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Lost GPU: the per-frame probe opens a command list, as the upload would (#6528)
On a reset D3D12 device the empty submission before a frame's uploads
went through (it only signals a fence), and the first queue write then
panicked inside wgpu-core (PendingWrites::activate unwrapping Lost).
The probe now submits a command buffer with a debug marker: finishing it
makes the same begin_encoding call, through a path where wgpu loses the
device and runs its lost callback instead of panicking. The picture
capturer's probe does the same.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #7159
* Measure: a fillet's torus or a sphere reports its radius (#7159)
The face of a fillet round a circular edge is a torus and a ball's face a sphere, which
Measure listed by area only. Their face item now carries radius, diameter and surface
(and the ring radius of a torus); they stay out of Shape, so distances are unchanged.
Fixes#7159.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
* claim: #9111
* Preview: new surface is tinted whatever its size, and the tints can be turned off (#9111)
A preview decided which faces of a changed body were new surface by asking
whether their triangle centroids lay on the old body, within a tolerance that
follows the part's size. A round or chamfer small beside the part never leaves
that tolerance, so it was drawn as if it had always been there, and sampling at
a fixed step told alike faces apart by nothing but how many triangles they had.
The kernel already records which faces an operation generated, so `build` takes
that record alongside the geometric test. The test stays because a body carries
the history of whatever last made it, which is not the previewed operation when
a parameter change re-evaluates the design: the record only ever adds faces to
the answer. The vote itself now asks about every centroid of a small face,
spreads a large face's samples without a resonant step, and calls a face new
above an eighth of its samples, which a blend tangent on both of its sides
reaches with the one row of facets in five that is clearly off the old surface.
Graphics gains two settings, both on as they come, so nothing changes out of
the box: Tint new surface in a preview draws a changed body as itself when it
is off, working out no face at all, and Show removed material in red leaves the
see-through cut and the ghost of the shape a feature replaces fully
transparent, drawing only their outlines.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* claim: #9084
* Edit Extrude: close on the first Escape (#9084)
An edit dialog whose value names a parameter (`T` in Edit Extrude) opened with
that field focused and its completion list showing the longer names (`tan(`),
unarmed. The list took the first Escape, so the dialog needed two.
An unarmed list now leaves Escape to the window until the box is typed in, as
it already leaves Enter and Tab; once typed in, Escape dismisses the list first
as before. Its hint drops "Esc dismiss" in that state.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #9827
* Sketch Offset: an Instances count stacks offsets one behind another (#9827)
Offset made one offset at a time, so a set of evenly spaced outlines meant
running it again on the curves it had just made, once per ring: a separate undo
step and a separate driving dimension each time.
`sketch.offset` now takes `instances`, a whole number or a unitless expression
from 1 to 100 (1, today's behaviour, by default). The per-group work the command
did inline becomes `offset_rung`, and `offset_ladder` runs it once per instance,
each rung offsetting the one before it the same distance again, so instance n
stands n distances from the curves picked. Every rung of a side is driven by
that side's own dimension, so editing it moves the whole set and keeps the
spacing even, and each side of a Two Sides or Symmetric offset grows its own
ladder. A rung the shape has no room for refuses the whole command and names
which instance it was.
A later rung reads its side from the geometry, through the rung's own
normal-distance annotation, rather than from the distance's sign: an output's
stored ends need not run the way the chain through them is walked, and a
reversed walk reads the same sign as the other side. A circle is the exception
and keeps the sign, because it offsets concentrically with no chain to walk, and
a step past a ring smaller than the distance lands beyond the centre, where the
circle's "outside the current radius" side test reads as the outward side: an
inward ladder turned round and grew again (1, 4, 1, 4 mm) instead of running out
of room.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* Sketch Offset: the Instances row in the offset window (#9827)
The offset window gets an Instances row under Distance: a whole number with step
arrows, 1 by default, so the count can be walked up a ring at a time and the
preview follows each step. The count is not kept in the design, as Mirror's and
Scale's values are not, so the window sends the number it evaluates to rather
than the text.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* UI kit: a count's step arrows are not text (#9827)
The arrows stand inside the editor, which asks for the text cursor across its
whole rect, so hovering one showed an I-beam and the arrows read as part of the
number. They take the ordinary cursor back after it, and the pointer is read
from the arrow's rect rather than from the hover the editor underneath competes
for, which keeps the hover highlight in step with it.
The pattern Quantity rows share this field and had the same thing.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* Sketch Offset: Instances label in the complete catalogs and the other shipped languages
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch Offset Instances: translations for the complete catalogs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
* claim: #9873
* Add the Preferences design reference: pages, figures, measurements, tokens
* Fix the selection look in the design reference to match the app
File › Export STEP…, STL…, 3MF…, OBJ… and URDF… were UI actions around
the shared file.export, so Keyboard Shortcuts had no row for them.
file.export_step, _stl, _3mf, _obj and _urdf now do what those items do
when started from a key, command search or the menu (a save picker; Save
As Mesh for STL and 3MF), and take a path when called with JSON. The File
menu shows their keys. Each has a contract and a reply type
(schema/file.rs).
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #10600
* Sketch: a value box must not swallow a click that lands on a sketch point (#10600)
The construction line tools' Length box waits beside the pointer. When the
pointer reaches the reference line's own endpoint it can land under that box
(the box is anchored to the tool hover, which freezes while the pointer rests
on the box), and the box's text edit takes the click, so the tool never sees
it. A release over the tool's own value box or construction/normal switch that
lands on a snapped sketch point now goes to the tool; a click on the box away
from any sketch point still edits the box.
---------
Co-authored-by: Dahak Builds <dahakbuilds@users.noreply.github.com>
* claim: Assemblies: use lookup maps when building the world state
* Assemblies: use lookup maps when building the world state
world_state_reusing, made again after every geometry command in a design
with components, filtered all bodies for every placement, searched the
timeline for every sketch and construction of a placed part, and searched
all bodies twice per tap and placement. It now builds the feature to
component map, the bodies of each component (in body order) and each
tap's component and body offset once per call. The result and its order
are unchanged.
On a 100-part assembly placed 5 times (800 sketches, 200 taps) one world
state drops from 124 ms to 9.5 ms (release, median).
A test-only counter of searches through all features or all bodies
checks that the work no longer grows with the sketches and taps.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Markus Kaltenbrunner <kaltenbrunnermarkus@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
A join, cut or intersect without explicit targets reads where every body
of its component is placed. Its placement digest searched the model for
each of those bodies by name, then the timeline for its component and the
placements for that component's frame: O(B^2 + B*F) per feature, on every
evaluation, also when every feature comes from the cache.
The bodies, their components (Document::model_body_components) and each
component's frame are now looked up once per digest. The bodies are still
hashed in name order with the same values, so every digest stays the same
bit for bit and saved caches stay valid.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #10393
* Per-component History hiding: flag, commands, Browser menu, bar toggle (#10393)
* Carry the new History-hiding strings in the complete catalogs (#10393)
---------
Co-authored-by: Dahak Builds <dahakbuilds@users.noreply.github.com>
* Sketch: a construction line with a length can be a normal line (#10258)
`construction: false` on a construction line command that has a
`length` makes the segment a normal sketch line, ready to close a
profile, instead of construction geometry. The default stays
construction, and an infinite line is always construction.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch: a length box in the construction line tools (#10258)
The horizontal, vertical, parallel and perpendicular construction line
tools show a Length box beside the pointer from the start. It stays
empty (shown as infinite) and leaves the keyboard to the tool's
shortcuts until a digit is typed; then the box takes the keyboard.
Without a length the tools work as before: one click, an infinite line.
With a length the first click sets the start and a second click the side
the segment runs to, sent as the command's `length` and `near`, so the
segment gets its length dimension. The angle tool has a Length box
beside its Angle box. A typed length keeps the start's snap, and the
preview draws the segment the click will create.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch: a construction/normal switch in the construction line tools (#10258)
A Construction | Normal switch sits under the Length box of the
construction line tools. Normal sends `construction: false`, so a
segment with a length becomes a normal sketch line, ready to close a
profile, without converting it afterwards. The switch starts at
Construction with each start of a tool. An infinite line is always
construction, so without a length the switch is greyed out and its
tooltip says why. It belongs to the tool alone: the Sketch Palette's
drawing line type (X) is left as it is.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: Sketch drag: drop the second document copy per frame
* Sketch drag: drop the second document copy per frame
edit_solved copied the document to edit it, then stored it with
`*s.doc_mut() = doc`. The command's undo snapshot still holds the old
document, so doc_mut copied the whole design a second time only to
overwrite it. Store the edited document with Session::set_doc instead,
which forgets the same caches and puts it in a new Arc. The design lands
at a new address as before and the command's end records it for the
crash journal (#7502).
A live drag frame on a design of 200 sketches (20k curves) drops from
19.2 ms to 9.4 ms; 50 sketches: 2.3 ms to 1.7 ms.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Markus Kaltenbrunner <kaltenbrunnermarkus@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: Move: say a part's joint conflicts instead of 'no movable joint axis'
* Move: say a part's joint conflicts instead of 'no movable joint axis'
When a joint of the selected part conflicts with the others (the solver
can't close its loop), Move found no axis and said "This part has no
movable joint axis.", pointing at joint types and limits. It now names
the conflicting joint and suggests editing, suppressing or deleting it.
Parts with no free joint value and no conflict keep the old message.
Refs #10331
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Markus Kaltenbrunner <kaltenbrunnermarkus@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #10484
* Sketch: Intersect can pick sketch lines and model edges (#10484)
The Intersect dialog only took faces or whole bodies, so a click on a
line of another sketch (the sides of a rectangle crossed by a plane
through its middle) picked nothing, although sketch.intersect already
cuts edges and sketch curves with the sketch plane. The pick list now
takes faces, edges and other sketches' curves, and the selection filter
reads Geometry | Bodies like Project's.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Markus Kaltenbrunner <kaltenbrunnermarkus@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #10471
* Show given names in bidi display order; find RTL prefs labels typed in reading order (#10471)
crate::bidi ran over catalog text only, so a multi-word Hebrew or Arabic
body name showed its words left to right in stored order, and Preferences
search compared display-order labels against reading-order queries.
given_name now returns right-to-left names in display order (browser rows,
tabs, status bar all draw names through it), and text_matches also scores
the query after the same bidi transform.
---------
Co-authored-by: Dahak Builds <dahakbuilds@users.noreply.github.com>
* Configuration rules: Then says it lists only the table's columns, and how to add one (#10532)
A rule drives or restricts a column of the table only, so Then leaves out a
feature without a Suppress column or a parameter without a column, where
When lists them all. A note under the list now says so and points at Add
Column.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Configuration rules: the two notes in the complete catalogs (Arabic, Hebrew, Japanese, Vietnamese, Chinese)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: View: work out consumed sketches and hidden bodies once per revision
* View: work out the sketches features use once per design
Each sketch_shown call walked every feature of the design to tell whether
the sketch is used, and the viewport asks about every sketch several
times a frame. The used sketches are now gathered into a set once per
design (keyed on the design itself, so any edit reads them again).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* View: look hidden bodies up in a set
body_visible walked the hidden-bodies list for every body asked about, and
the viewport asks about every body several times a frame. The hidden
bodies now join the hidden joints and folders in the view's visibility
index, built when the list changes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Markus Kaltenbrunner <kaltenbrunnermarkus@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: View: work out consumed sketches and hidden bodies once per revision
* View: work out the sketches features use once per design
Each sketch_shown call walked every feature of the design to tell whether
the sketch is used, and the viewport asks about every sketch several
times a frame. The used sketches are now gathered into a set once per
design (keyed on the design itself, so any edit reads them again).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Markus Kaltenbrunner <kaltenbrunnermarkus@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: Canvas: store reference image data as shared text so edits stop copying it
* Canvas: store reference image data as shared text so edits stop copying it
Every edit clones the document, because the undo history still holds the
old one, and the canvas image was a plain String: each of the up to 200
undo states owned its own copy of the base64 text. Canvas::data is now
Arc<str>, like the shared STEP text, meshes and point clouds; serde's rc
feature writes it as the same JSON string, so design files stay byte for
byte the same.
A 4.2 MB image with 250 edits: peak memory 1.09 GiB -> 42 MB, run time
1.16 s -> 0.12 s.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Markus Kaltenbrunner <kaltenbrunnermarkus@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Browser: a folder that holds only a new group shows it (#10526)
New Group from the root's context menu made an empty group in the Bodies
folder, but the Browser draws no folder without items, so in a design
with no bodies yet nothing appeared. A folder that holds groups is now
drawn with them, even before anything is in it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Browser: test that a new group in an empty folder shows and takes a dragged body (#10526)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: Extrude dialog: work out the automatic operation once, not every frame
* Extrude dialog: work out the automatic operation once, not every frame
An open Extrude window with the automatic operation on asked the engine
for `auto_operation` every frame. That probes the bodies with
`Mesh::contains`, rays through every triangle, so a large part cost tens
of milliseconds per frame with nothing changing.
The window now keeps the answer with a key over what it depends on: the
extrude's parameters (participants included), the design's revision and
id, and the active component and occurrence path. The latch still runs
every frame as before.
Open Extrude over a 1M-triangle mesh body: 29.8 -> 0.87 ms per frame
(headless harness, median of 3).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Markus Kaltenbrunner <kaltenbrunnermarkus@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: Extrude dialog: keep the taper limits while the profile is unchanged
* Extrude dialog: keep the taper limits while the profile is unchanged
The taper handle is drawn every frame while the extrude window is open,
and every frame it worked out the inradius of each picked profile and of
each of its holes: a grid search over every side, about 200 ms a frame
for a plate with 64 round holes. Only the height changes between frames.
The radii are now kept in the window's state with the profiles they were
worked out for, and worked out again only when the profiles differ. The
limits come from the same arithmetic, so they are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Markus Kaltenbrunner <kaltenbrunnermarkus@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #10016
* Sketch: a Line chain survives Undo (#10016)
Undo could take away the segment the chain was drawing on from, and every
further click was then refused with "unknown sketch point". The chain now
remembers where each segment started and goes back to the last one still there.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: Raytracer: map faces to looks once
* Raytracer: map faces to looks once per body
Preparing a body for the CPU raytracer walked the body's face look list
one entry per work step for every triangle, so a body cost T x (F + 1)
steps (each through the job host) instead of T. The face looks are now
sorted once per body, keeping the first entry per face as before, and
each triangle finds its look with one binary search. Pixels are
unchanged; with 10 000 faces and 50 to 500 face looks, preparation
drops by 40 to 88 %.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Markus Kaltenbrunner <kaltenbrunnermarkus@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: Opening: validate the document once when a model cache is present
* Opening: validate the document once when a model cache is present
Opening a design that keeps its model parsed and validated document.json
twice: reading the model cache checked it before decoding the cached
bodies, and read_document, which follows straight after, runs the same
migrations and validation and fails the open when they fail. The open
path now reads the model without that check; geometry(),
read_design_geometry and the preview keep it. A broken document fails the
open with the same error as before.
3,000-feature design with its model cache, bench-open parse step:
426 -> 293 ms (median of 8, heavy machine load). Saving the opened
design gives a byte-identical file.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Markus Kaltenbrunner <kaltenbrunnermarkus@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #10483
* ViewCube: an option to invert the curved roll arrows (#10483)
A new Navigation preference, "Invert the curved roll arrows"
(invert_curved_roll_arrows, off by default): with it on, the two curved
arrows at the cube's corner roll the view the other way within the
screen plane - the clockwise arrow rolls counterclockwise and the
counterclockwise arrow rolls clockwise. The sibling of the cardinal-arrow
inversion from #10286, which left these arrows alone.
---------
Co-authored-by: Dahak Builds <dahakbuilds@users.noreply.github.com>
* claim: Mesh imports: compare by pointer before comparing contents
* Mesh imports: compare by pointer before comparing contents
MeshData derived PartialEq, so the dirty check and the autosave check
walked every coordinate and index of a scanned mesh after each edit,
although the document and its saved copy share the same arrays. Compare
shared arrays by pointer first and value by value otherwise, as Points
and Colors already do. About 1.9 ms per comparison for a mesh of one
million triangles goes to nothing.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Markus Kaltenbrunner <kaltenbrunnermarkus@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: Sketch: use a lookup map for curves in pattern upkeep
* Sketch: use a lookup map for curves in pattern upkeep
Pattern upkeep runs before every solve, each drag frame included, and
looked up every copy curve and its arc points with a linear search over
all ids. Dragging another line in a sketch with a 500-copy slot pattern
spent about two thirds of each frame there. The ids now resolve through
maps built once per call; the result is the same to the bit.
The solve bench gains a circular slot pattern section.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch: hash sets for the stale-pattern check before each solve
The check whether a pattern lost a seed, copy or relation built three
ordered sets of all ids before every solve. Only membership is asked, so
hash sets give the same answer; a drag frame in a sketch with a 500-copy
pattern takes another third less.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Markus Kaltenbrunner <kaltenbrunnermarkus@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: Sketch: use a lookup map for curves in pattern upkeep
* Sketch: use a lookup map for curves in pattern upkeep
Pattern upkeep runs before every solve, each drag frame included, and
looked up every copy curve and its arc points with a linear search over
all ids. Dragging another line in a sketch with a 500-copy slot pattern
spent about two thirds of each frame there. The ids now resolve through
maps built once per call; the result is the same to the bit.
The solve bench gains a circular slot pattern section.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Markus Kaltenbrunner <kaltenbrunnermarkus@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Projecting a whole body fitted its outline from mesh edges: a round wall seen
edge-on has triangles leaning a hair either way, so the outline zig-zagged
between both rims and broke into two-point pieces (lines), and seams where two
faces' triangles don't share vertices counted as open, outline edges. Faces
that face one way all over (planes, cylinders and cones about the sketch
normal) now decide for all their triangles, near vertices are welded, and a
seam between two faces facing the sketch is no outline. A STEP hook went from
159 lines to 10 lines and arcs.
A single rounded or chamfered rim of a cut curved face is a spline that only
follows its circle; read as Bezier pieces it came in as twenty splines. An
edge read as spline pieces that stays on a circle within 1e-5 of its radius,
in a plane parallel to the sketch, now comes in as that arc.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Measure: datum planes, axes and points, at exact distances
The Measure tool took faces, edges, vertices and sketch geometry; a datum
plane or axis could not be picked into it, and a point set of an infinite
plane or axis would measure the wrong thing anyway.
Describe origin and construction planes, construction axes and construction
points as items, accept PLANES, AXES and CONSTRUCTION_POINTS (and the origin)
in the Measure dialog, and give the new shapes exact closest points:
- a point and a plane: the perpendicular distance;
- a point and a line: the perpendicular distance;
- two planes: parallel distance, or zero where they meet;
- a plane and a line: zero where they cross;
- two lines: the skew distance.
The panel and the quick readout show a plane's point and normal, and an
axis's point and direction.
Test: a point 10 above the XY plane, 5 from the Z axis, and a plane and an
axis that meet.
* Select: a straight sketch line is a curve in an input that takes curves
Adding axes to Measure's input made a straight sketch line match the "axis"
rule before the sketch-line rule, so a picked external sketch curve became a
SketchCurve instead of the ExternalSketchCurve Measure records. The axis rule
now applies only to inputs that take axes on their own (Move's direction and
the like), not to inputs that also take curves.
Test: measure_input_fits_external_sketch_geometry, and the Move axis picks.
* rustfmt: format the new selection filter and measure code
* Measure: drop an unused normalisation in the skew-line distance
---------
Co-authored-by: clarkliu <277482660@qq.com>
* Frames: test that a renamed cut-off piece keeps its name in a rebuild from the features alone (#10438)
solidcraft-cli eval rebuilds a saved design without its saved model; the
rename made after a cut then lands on the first line's member. The test
reproduces it on main; the fix follows.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Frames: a renamed cut-off piece keeps its name when the design is rebuilt from its features (#10438)
Since #10132 a cut's pieces past the first belong to the cut and the piece
that keeps the body's name to the body's own feature. Ordering the pieces
by their faces then moved the names among them but left each body's
feature behind, so the cut-off piece said the frame member feature made
it. body.rename stored its new name there, in the first line's slot, and a
rebuild from the features alone (solidcraft-cli eval of the saved file)
named member A after it. A name now moves together with the feature it
belongs to.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
With several bodies to send (after Add all bodies, say), the Print face
input refused every face. It took a print face only when exactly one
body was sent; any other pick was cleared at once, with a note in the
status bar only. Each body's print face then had to be set with Ctrl+B,
or by sending that body alone.
The input now holds the print face of each body being sent. A face
picked in it becomes its body's print face, in place of the one it had.
A click on a body's print face takes it out and clears only that body's.
A face of a body that is not being sent is refused with a message
(`ui.print_face_body_not_sent`, in place of `ui.print_face_one_body`).
Each change is still one `body.print_face` command, so it undoes on its
own. Picks in this window are no longer widened to the other pieces of
the face's surface, so a click on a print face takes it out rather than
adding its neighbours.
The user guide (guide/files.md) says so. The new message is in
assets/locales/pending/print-face-each-body.json, in all 31 languages;
the old one is removed from the catalogs.
Test: dialogs_save_mesh::tests::the_print_face_input_takes_one_face_for_each_body_sent
(fails without the change: the second body's face is refused).
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch: Break Link in the context menu of projected geometry (#10417)
Projected curves and points (model edges and vertices, another sketch's
curves) could only be unlinked through command search, although Trim and
other refusals say "use Break Link on it". The sketch context menu now
offers Break Link when the selection holds projected geometry; text keeps
Explode Text.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch: test Break Link in a projected edge's context menu (#10417)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Browser: Features folders scrolled out of view skip their mark
A folded Features folder shows the worst error or warning mark of the
features it holds. Working that out scans the design's whole history, and
from 16 features builds a table of all of them: per placement, on every
frame, whether the row is on screen or not. In a long tree of placements of
a part with a long history this was most of the window's frame while
orbiting.
A row scrolled out of view paints nothing (`tree_row` only takes its space),
so its mark is now worked out only when the row is on screen, as the
placement rows already do for their own state. The rows on screen show the
same marks.
Refs #6083
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Scale tests: time each frame of an orbit around a large design
`scale_tests::orbit_frame_cost` (ignored, manual) turns the view with the
right button over a design (a STEP file inserted as the app opens one, a
design file, or the generated assembly) with the model scene built, as the
app builds it once a renderer is there. Per frame it times the window
thread's frame and its named steps (`frame_watch`), egui's tessellation,
and the renderer's prepare, draw recording and GPU work on an offscreen
device (`OffscreenFrames::frame_parts`); without an adapter it times the
window thread alone. `painted` fingerprints the tessellated picture once
the view rests, to compare two builds.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
On XZ a sketch's y runs along world -Z, so a value written as a height lands
below the plane; only the modelling skill's reference said so. The command's
parameter text now gives the axes of all three origin planes (XY: +X, +Y;
XZ: +X, -Z; YZ: +Y, +Z), and a test pins them.
Assisted-by: LLM
`component.insert_design` links a design saved as a .sc container or as plain
JSON (`.json`), but `component.relink` refused every path that was not a
container ("choose a saved .sc design"), so a component linked to a JSON
design could not be pointed at another one. Relink now takes any design path
Insert Design takes (`is_design_path`: .sc, a legacy extension or .json); the
file is read by the same `design_bodies`, which already reads both. The Relink
Source file dialog opens on a filter that lists .json among the designs, too.
Assisted-by: LLM
* claim: #10258
* Sketch: construction lines with a length (#10258)
The horizontal, vertical, angle, parallel and perpendicular construction
line commands take an optional `length`: the line becomes a construction
segment from `at`, running towards `near`, held by a length dimension
that follows its expression. Without `length` the line stays infinite,
as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Markus Kaltenbrunner <kaltenbrunnermarkus@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- In entry_row (crates/ui-egui/src/browser.rs), use live rather than cached for the property
- Ensures eye toggle dynamically fades or unfades rows for sketches and bodies without requiring a cache rebuild
- Add regression test in browser_card_tests
Fixes#9953
OpenSteps splits doc.open of a design into steps for a caller that must
keep working meanwhile (the web app, which has one thread): start reads
the file, each step evaluates the next feature by moving a copy's history
marker one feature on (so every result before it is reused), and finish
puts the design in the session's place with doc.open's reply. The next
feature's imported STEP text is offered first, so a caller can read it
in parts. Session::opened and opened_at are split into small helpers so
both paths build the session the same way; doc.open is unchanged.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Saving a design with its model cache asked, for every state, for each sheet-metal body it holds,
and built that sheet's JSON record each time only to find it written already. A design of 906
states with 300 sheets spent most of its save there. The sheet's text written straight from it
is the key now; its record is built only the first time. The records and the file are the same.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Find a saved model's sheets by their text, not a record built per state
Saving a design with its model cache asked, for every state, for each sheet-metal body it holds,
and built that sheet's JSON record each time only to find it written already. A design of 906
states with 300 sheets spent most of its save there. The sheet's text written straight from it
is the key now; its record is built only the first time. The records and the file are the same.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Write the model cache manifest without copying the model's records
The manifest was built as a JSON tree, which copied the whole model's records (13 to 33 MB of
records in large designs) before writing them, and a cache too big for the file wrote it again for
each try at fewer states. The manifest is now written from the records where they are, its fields
in the sorted order the tree wrote them, so the file is the same byte for byte.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #10063
* docs: add a design approach and workflows chapter to the user guide (#10063)
Adds guide/design-approach.md after the window tour: the parametric idea, principles for designs that change well, the timeline and rollback marker, bodies vs components and occurrences, common workflows with links, a term map for users of other CAD software, and where to find help.
* docs: match the chapter to the app's labels and sketch colours (#10063)
Checked in the web app (0.29.0): the timeline strip is headed History, the toolbar shows New Sketch and Parameters for Create Sketch and Change Parameters, and fully constrained geometry turns white in the default dark theme rather than dark.
* docs: tighten the design approach chapter's wording (#10063)
The glossary covers most, not all, of the chapter's terms; Redo moves forward; the timeline is shown as the History strip; Report a bug drafts a report for the user to file and sends nothing itself.
A flat node's roundoff allowance took a hypot per control point. Twice
|x| + |y| bounds each hypot from above with room to spare for rounding,
and rounding keeps sums monotonic, so whenever the flatness test passes
with that larger allowance it passes with the exact one too. Only nodes
whose chord distance comes near the 0.5 px bound (or with a non-finite
term) now take the exact hypots, and they decide as before.
Same result for every node: the reference comparison test still matches
bit for bit.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
A caller that only needs the bodies' names (assembly occurrences included,
e.g. "Body2 (Part:2)") had to measure every body, or read document.inspect,
which lists definition bodies only. `inspect.measure {names_only: true}`
returns each world body's name and its definition's name, honouring the
`bodies` filter; an unknown name is refused before anything is measured.
The MCP `measure` tool passes it through, directly and as a batch step;
`summary` leaves such a list as it is, since there is nothing to narrow.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #8876
* Add optional viewport-only Kawase blur and native UI composition
* Fix vendored panel hook imports and deprecated float constant import
* Drain texture updates in the full GPU blur fixture
* Hold the blur evidence fixture camera fixed across radii
* Address blur compositor lint checks
* Share animated Render preview background with blur source
* Use resolved Design background for blur source
* Exercise blur rendering independently of software adapter policy in CI
* Use stock egui hosts instead of vendoring the full crate
* Preserve feature-window open handling with stock egui hosts
* Target the native Window in close-handling regression fixture
Show F2 beside Rename on context menus where F2 already renames the
target (bodies, features and sketches, construction planes, components and
occurrences, browser groups, and canvases). Saved views and joints remain
without a shortcut label because F2 does not rename them without a menu.
When a context menu is open, pressing F2 routes through press_shortcut to
rename the menu's target (such as a clicked browser group or canvas) and
close the menu, rather than renaming the viewport selection.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Gemini 3.8 Flash <noreply@google.com>
Each node of the display curve sampler recomputed a control point's
seven clip distances for every clip plane it tested (up to 7 times for
the reject test, 3 more for the depth test, once more for the cut), and
projected each point to the screen twice. They are now worked out once
per node into arrays and read from there.
Most nodes are split rather than drawn. A node is flat only when every
control point lies within the pixel bound of its chord after adding a
roundoff allowance that is never negative, so a point already farther
than the bound decides "split" on its own; the allowance (four hypot
and divisions per point) is now left out for those nodes.
Same values in the same order: the reference comparison test from the
previous commit still matches bit for bit.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The doc comment of the ground shadow had slid onto build_sketch_scene when
that function was added above ground_shadow. Give it back to ground_shadow
and describe what build_sketch_scene builds.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch Coincident: a selected line midpoint is a point it takes (#10311)
A selected midpoint goes to the command as {midpoint: line}, as
Horizontal/Vertical's alignment takes it, but Coincident wanted a point
id and refused it with "b must be a point or curve". The command now
puts a point at the line's middle, tied there by a Midpoint constraint
(or uses the one already tied there), and makes that point coincident
or puts it on the other curve, in one undo step. The toolbar button and
the context menu send a selected midpoint that way.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch Coincident: test a line midpoint in the command, the toolbar and the context menu (#10311)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Engine: open a design a feature at a time (#10198)
OpenSteps splits doc.open of a design into steps for a caller that must
keep working meanwhile (the web app, which has one thread): start reads
the file, each step evaluates the next feature by moving a copy's history
marker one feature on (so every result before it is reused), and finish
puts the design in the session's place with doc.open's reply. The next
feature's imported STEP text is offered first, so a caller can read it
in parts. Session::opened and opened_at are split into small helpers so
both paths build the session the same way; doc.open is unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Web app: open a design a few features a frame, with progress and Cancel (#10198)
On the web, a design to open is now a staged file load like a STEP file
(#1376): its features are evaluated for about 30 ms a frame through
OpenSteps, a feature's imported STEP file is read a solid per frame first
and held so the feature finds it read, the progress card counts the
design's features, Cancel works between steps, and the meshes are made
a few per frame afterwards. The desktop app keeps its worker thread.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Web app: design links and the files window open designs with progress and Cancel (#10198)
Design links (?open=URL, as embedded viewers use), the files window and
designs opened from the computer went through the one-shot open_path,
which froze the page on a large design. They now use the staged open; a
design link becomes an unsaved copy under the link's name once the open's
reply arrives (SolveApp::open_path_answered).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Engine: open a design a feature at a time (#10198)
OpenSteps splits doc.open of a design into steps for a caller that must
keep working meanwhile (the web app, which has one thread): start reads
the file, each step evaluates the next feature by moving a copy's history
marker one feature on (so every result before it is reused), and finish
puts the design in the session's place with doc.open's reply. The next
feature's imported STEP text is offered first, so a caller can read it
in parts. Session::opened and opened_at are split into small helpers so
both paths build the session the same way; doc.open is unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Web app: open a design a few features a frame, with progress and Cancel (#10198)
On the web, a design to open is now a staged file load like a STEP file
(#1376): its features are evaluated for about 30 ms a frame through
OpenSteps, a feature's imported STEP file is read a solid per frame first
and held so the feature finds it read, the progress card counts the
design's features, Cancel works between steps, and the meshes are made
a few per frame afterwards. The desktop app keeps its worker thread.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
A sketch holding a text whose words come from a text parameter is left
unsolved after the words change and its links then move far, with the
warning "the sketch constraints conflict (k1, ..., k10)". The same two
changes the other way round solve.
Repro: a sketch with three projected points, a construction rectangle on
them and a centred text along its far side, the text's words given by a
text parameter. Change the parameter's words, then move the source
geometry about 280 mm. Without the word change it solves; with it, the
sketch warns of conflicting constraints.
Other words are other outlines: the text is built anew with new points
and curves. The solve that walks a sketch from its stored state to a far
link move (and the check for sides the jump turned over) refused the two
sketches as not the same entities, and the single jump left alone failed.
`Sketch::stored_with_rebuilt_texts` gives the stored sketch as this one
has its entities when they differ only by rebuilt texts: the texts'
entities as they are now, every other point, radius, arc turn and
constraint value as stored. `Document::solve_sketch` walks from that, so
the points pair up again. Sketches that differ in anything else are
refused as before.
Test: sketch_project::side_tests::a_label_follows_its_source_far_after_a_text_parameter_gave_it_other_words
(fails without the fix with the conflict warning above).
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The pixel-bounded curve sampler that draws sketch curves and profile
outlines ran again on every camera move, and each subdivision step
allocated: every span's control points were a Vec, splitting one in
half built a Vec per de Casteljau level, each node's projection was
collected into a Vec, and every drawn part grew from two vertices.
The control points now live inline in a fixed array (`Hull`, at most a
degree-9 Bezier), the halves are worked out in place with the same
`mix` level by level, the projection goes into a reused array, and a
new part starts with room for eight vertices. The arithmetic and its
order are unchanged, so the points are the same bit for bit.
Tests: `samples_the_same_points_bit_for_bit_as_the_reference` keeps the
previous sampler as a reference and compares paths, quality and budget
use over every segment kind, slanted and far-off planes, orthographic
and perspective views from far out to far in, clipped and unclipped,
with ample and spent budgets. Ignored timings:
`sampler_cost_against_the_reference` (render) and
`sketch_scene_cost_while_the_camera_moves` (ui-egui).
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The web build cannot start the open-meshing thread. Its fallback counted
every body done but listed none ready, so whatever the file load opened
on the web drew no bodies and kept "Loading parts n/n" up for good. The
fallback now lists every body ready and the viewport meshes as it draws,
as the fallback meant to. Found while opening a design through the file
load in the browser (#10198).
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #10218
* Assembly: move sketches to the part they're drawn on (#10218)
`component.move_sketches.to_parts` moves each given sketch, or every sketch in the design, whose
plane is an exactly attached face of a part's body into that part through
`component.move_sketches`, as one undo step. Sketches with no single part to go to stay where they
are, and the reply lists what moved and what stayed, with the reason. The Sketches folder's menu
offers it for the folder's sketches (or the selected ones), and a sketch's menu for that sketch or
the selected sketches; the status bar says how many moved and why the others stayed.
`component.move_sketches` now keeps a face-attached sketch where it is on its face: the sketch is
authored in the new component's placement (the face's own when the component owns the face), and
its picked point and repair plane are carried into it. Before, a face-attached sketch moved out of
the design was left without an author placement and failed to evaluate.
* Tests: the moved part is free to move; face anchor path
* Tests: move the to_parts test to the component command tests
* Shorter label: Move Sketches to Their Parts
* claim: #4581
* View angles card: per-axis zero buttons and locks (#4581)
The readout card under the orientation gizmo already showed and edited the
view's yaw, pitch and roll in degrees. Each axis row now also has a zero
button that turns that angle to 0 degrees at once, and a lock: a locked
angle stays put while orbiting, turning with the arrow keys or using a 3D
mouse, and its field and zero button go inert, so a symmetrical view can
hold its yaw and roll at 0 while only the pitch is adjusted.
* clippy: drop needless borrow in keep_locked call
---------
Co-authored-by: Dahak Builds <dahakbuilds@users.noreply.github.com>
Fold the pending design format changes (align-axis-mask, equation-variable-unit, link-file-digests) into format 46.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch Ellipse: Major radius and Minor radius boxes while drawing (#10279)
The Ellipse tool showed no value boxes. After the centre it now shows
Major radius, after the end of the major axis Minor radius; a typed value
places the point at that size and becomes the ellipse's dimension, as for
a circle's Diameter. sketch.draw.dimensioned takes major_radius and
minor_radius for sketch.ellipse, so Dimension new shapes covers it too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch Ellipse: test the typed radii in the tool and in sketch.draw.dimensioned, and say so in the guide (#10279)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch Ellipse: rustfmt the new engine test (#10279)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: faster highlight with many bodies selected
* Viewport: find selected bodies by name once per highlight build
With every body of a large assembly selected, each hover change built the
highlight again and looked every selected body up along the world's bodies
(`ModelState::body` scans them), which is quadratic: about 2.2 million name
comparisons on 2,116 bodies, some 3 ms of every hover change.
The selected bodies, faces and edges are now found in a map by name, made at
the first lookup. A name held by more than one body finds the first, as the
scan did, so the same bodies are lit in the same order.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Viewport: measure hover changes with many bodies selected
An ignored scale test: every body of a design selected (a file named by
SOLIDCRAFT_SCALE_DESIGN, else a generated grid of 1,440 boxes), the pointer
moving from face to face, timing each hover change's highlight build headless
and as the app builds it on a GPU (bodies drawn from their plans, and from the
model scene's blocks). Run with `--ignored --nocapture`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Markus Kaltenbrunner <kaltenbrunnermarkus@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Keep full names in the tooltip only when the name cell clips them, and keep
the double-click rename hint without stacking a second popup. Cover short
and long names, favorites, and double-click input with a regression test.
Co-authored-by: Codex <noreply@openai.com>
* claim: #10254
* ViewCube: an option to invert the directional rotation arrows (#10254)
A new Navigation preference, "Invert the view cube rotation arrows"
(invert_navigator_arrows, off by default): with it on, an arrow around the
cube orbits the camera the way the arrow points, as in Fusion and Inventor,
instead of rotating the cube toward the side the arrow sits on. Left and
right, and up and down, trade places, and each tooltip names the action
actually performed. The curved roll arrows are not inverted: they name the
rotation they perform, not a direction.
---------
Co-authored-by: Dahak Builds <dahakbuilds@users.noreply.github.com>
* claim: consolidate Gruvbox contrast variants
* Consolidate Gruvbox variants into light and dark contrast choices
* Check both original Gruvbox viewport gradient endpoints
* Reset Gruvbox scenario state before independent theme checks
* claim: #10215
* Sketch: an option to make the part active when sketching on its face (#10215)
Preferences › Sketch › "Sketching on a part's face makes that part active", off by default.
When it is on, a sketch started on a face of another component's body activates that component
at the picked placement first and is made there, as after an explicit activate. The part stays
active, so what is built from the sketch lands in the same part.
For scripts the same is `sketch.create` `activate_part: true`; the reply's `activated` names the
component and placement. On an error the active component goes back to what it was.
* Tests: a free part, no edge projection, the sketch on the face plane
* claim: #10151
Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>
* Sketch: clicking the running tool's button ends it, so clicks select again (#10151)
The lit ribbon button of the running sketch tool started the tool over
when clicked again, so it gave no way out: every click in the view kept
drawing or measuring, and Ctrl+click never reached selection. Esc and
right-click already end a tool, but the pressed button looks like the
natural switch.
A second click on the running tool's button (large ribbon buttons and
the constraint grid) now ends the tool. Persistent analysis toggles keep
their own on/off. The old behaviour stays behind the Sketch preference
"Clicking the running tool's button ends it" (ribbon_click_ends_tool).
Fixes#10151
Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>
---------
Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>
* claim: #10083
Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>
* 3D mouse: turns a rolled or upside-down view the way the cap is pushed (#10083)
In the default upright (turntable) orbit the cap's Spin rolls the view,
but its Tilt and Turn went straight into yaw and pitch, which ignore the
roll. Rolled upside down, every push moved the model the opposite way on
screen; rolled on its side, tilt moved it sideways.
The device now takes Tilt and Turn back through the view's roll before
the orbit, for the cap and for a device app over the navigation bridge.
An unrolled view behaves exactly as before; Free orbit was already
view-relative and is unchanged.
Fixes#10083
Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>
---------
Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>
* claim: #6606
* Sketch: held solves that cannot succeed give up sooner (#6606)
A status-only solve (the held solves of a drag) only asks whether it
succeeds. When LM sits in a least-squares valley of conflicting
constraints it crawled on to its 500-iteration limit, and a held solve
runs LM up to three times. Such a solve now leaves a component once the
iteration has stalled far from a solution: over the last 20 iterations
the sum of squared residuals fell by at most 1e-5 of itself while the
largest residual is still above 1e-4 mm.
Successful solves are unaffected: on about 62,000 LM runs that succeeded
(the sketch and engine tests and 120 generated sketch scripts with 2,900
drags) the slowest still fell by 1.4e-3 over any 20 iterations while
that far off. Solves with an analysis keep iterating (their report names
the violated constraints), and live frames are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Markus Kaltenbrunner <kaltenbrunnermarkus@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- sketch.rectangle.center and two_point: a corner given with expressions is a fixed point that follows its parameters, as with sketch.line
- sketch.circle.center: a radius or diameter naming a parameter is a driving dimension; a string naming none is just the number; bare numbers read in the design unit
- A radius that is not a number is refused with its own message and no longer passed over for diameter
- sketch.circle.two_tangent: a string radius reads bare numbers in the design unit
Co-authored-by: P'tite Tête <asso@ptitetete.com>
* claim: #10079
* Browser: a click just past a feature's name selects it instead of opening its sketch (#10079)
The Edit Profile Sketch button on a browser feature row began 2 points after
the name, so a click that landed just past the name, or a press on the name
that slipped a few points before its release, opened the profile sketch.
The button now starts 8 points after the name, wider than egui's 6-point
click slop; the gap selects the row like the name does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Markus Kaltenbrunner <kaltenbrunnermarkus@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #10138
* Follow the system language on a first start (#10138)
The language preference defaults to a new 'system' value: the OS locale
(sys-locale) is matched against the catalog list - exact tag, base
language, then English - and a System choice sits at the top of the
Preferences language list. A saved language still wins.
* Drop the explicit deref clippy flagged; regenerate THIRD-PARTY-LICENSES.txt for sys-locale
---------
Co-authored-by: Dahak Builds <dahakbuilds@users.noreply.github.com>
The three sketch chamfers take `points: [corners]` (a point reference or
{a, b} each), as sketch.fillet does: every corner is cut alike in one
edit, and a corner that can't be cut, or is named twice, refuses them all.
The Chamfer window sends every picked corner instead of refusing a second.
Claude-Session: https://claude.ai/code/session_01PVKpSVXR9iiwSM9B25kpvq
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Navigation: an NX mouse mapping
NX's CAD1 scheme drags the middle button to rotate, middle + left to zoom and
middle + right to pan. The mouse mappings here hold one button at a time, so
the preset takes the middle drag with Shift (pan) and Ctrl (zoom) for those two
drags, leaves the right button to the context menu, and fits on a middle
double-click as NX does.
The help text says which NX drags the modifiers stand for.
* Navigation NX note: translations for the complete catalogs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Navigation NX note: use the message id nav.nx.help in the pending file
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: clarkliu <277482660@qq.com>
Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #9570
* Cross-fade the viewport grid between levels while zooming (#9570)
grid_step switches the spacing in a step (1, 2, 5 times a power of ten), so
the whole grid swapped at once as the view zoomed through a switch and
popped over and over while scrolling. Near a switch the drawn grid now
draws the outgoing and incoming levels together, weighted by a smoothstep
over about 19% of zoom either side, each level's lines on their own places
on the plane so a fading level never shifts. A line both levels share is
drawn once; its colour and width blend between minor and major.
grid_step itself is unchanged, so sketch snapping, dash lengths and the
tools keep their spacing; the dominant drawn level is always grid_step's.
The viewport and the headless picture share grid_lines, so they still draw
the same grid.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Fade out the fine grid before a non-nested coarser level appears (#9570)
The levels go 1, 2, 5 times a power of ten. From 1 to 2 and from 5 to 10
every coarser line is also a finer one, but 5 is not a multiple of 2, so
cross-fading the 2 and 5 levels drew lines at 0, 2, 4, 5, 6, 8, 10 mm at
once, which looked restless.
At that switch the finer level's own lines now fade out over the first
half of the cross-fade and the coarser level's own lines fade in over
the second, so the two are never drawn together. The lines both levels
share (every 10 mm, the finer level's major lines) stay at full strength
throughout, so the grid never goes blank. The cross-fade spans the same
zoom range as before, and the nested switches cross-fade as they did.
grid_levels keeps its shares (adding up to 1, grid_step's level the
stronger one); grid_lines draws a line only one level has as strongly
as that level's own lines. The headless and viewport tests now check
half strength at the nested 5 to 10 switch and no 2 or 5 mm line right
at the 2 to 5 switch; a new test zooms through 2 to 5 in four decades.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #10091
* View cube and axis gizmo: a standard view centres and fits the model (#10091)
A new Navigation preference, "Fit the model after a navigator view change"
(`navigator_fit`, off by default): with it on, a face, corner or arrow of
the view cube and an axis tip of the gizmo look at the middle of the model
from the one distance that fits its bounding sphere, so Home, an opened
design and every view chosen on a navigator share one zoom and one centre
and only the direction changes. Off, both navigators turn in place as
before.
"Home and Fit size" (`home_fit_size`, 10–100 %, default 100 %) is how much
of the view between the panels and the controls the model fills when Home,
Fit, an opened design or a navigator view change frames it; 100 % is the
framing as before.
`view.navigate` takes an optional `fit: true` with a direction or a turn,
so programmatic callers get the same framing.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
* Assistant: the agent list and the boxes under the switcher keep the panel's margin (#10172)
The agent switcher above them sits in the panel's side margin, but what
the panel draws under it before a connection (the Agent row with Add
agent and Refresh, the agents found, what isn't installed, the New chat
confirmation and the sign-in box) started at the panel's very edge, and
Refresh was cut off at the right. They now keep the same side margin.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Assistant: test that the agent list keeps the panel's side margin (#10172)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Assistant: the margin test passes the labels without a needless borrow (#10172)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #4376
* Selection: a freehand lasso that takes what is wholly inside it (#4376)
The SELECT panel gets a Lasso checkbox (UiState::lasso_select, ui.set
lassoSelect). With it on, a primary drag from empty space draws a
freehand loop; the release selects through the same path as the window
box: items whose points are all inside the loop and no segment crosses
it, so a concave loop leaves out a curve that bulges out of it. Shift
and Ctrl add as with the box. ui.drag takes via waypoints (and scenario
drag steps take more than two points) so agents and tests can draw it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: thocsmith <193096300+thocsmith@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Arabic: isometric view wording in the Extrude view preference
* Arabic: equation curve range unit option and note read naturally
* Arabic: tanween spelling in the new bold-font and delete-first messages, menu and option names quoted in the graphics card note
* claim: #5019
* sketch offset: chain lines whose corners are coincident, not only shared
Lines drawn one by one meet at their own points held by Coincident
constraints. The offset walked shared points only, so each such line was
offset on its own, to the left of the way it was drawn: a closed
rectangle came apart, both horizontals moving up. The same split shows
next to a fillet or chamfer, whose other corners stay Coincident pairs.
Chain ends held by a Coincident constraint as one joint, the way
Sketch::chain() and axis_implied already do, so a closed outline offsets
as one closed shape.
Fixes#5019.
* test(sketch): cover coincident offset loops after trim and blends
* refactor(sketch): share and cache coincident endpoint groups
* claim: #1936 (Fillet, Chamfer, Extrude, Shell and Draft edit dialogs)
* Fillet, Chamfer, Shell and Draft: a stored number edits with its unit (#1936)
Their edit dialogs showed a value stored as a bare number as it was, so a
fillet made with a radius of 2 opened as "2" and a chamfer angle of 30 as
"30", where the other dialogs, and since #2107 the Plastic ones, read
"2 mm" and "30 deg". The Fillet radius, the Chamfer distances and angle,
the Shell thickness and the Draft angle now go through shown_expr, as the
Plastic dialogs do.
Extrude is left as it is: its editor keeps an unchanged expression exactly
as stored (extrude_edit_tests), and writing the unit would change it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Offset Face, Stock, Offset Plane, Plane at Angle, Pipe and Emboss too (#1936)
The rest of the dialog_descriptors! table's length and angle fields that
copied the stored text as it was now go through shown_expr as well: the
Offset Face distance, the Stock margin, the Offset Plane offset, the Plane
at Angle angle, the Pipe diameter and wall, and the Emboss depth. Scale's
factor has no unit and still shows as stored.
The test also edits an offset face, a stock and an offset plane.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Revolve, Thread, Rib and Web too (#1936)
The custom edit paths that copied a stored length or angle as it was now
go through shown_expr as well: the Revolve angle, the Thread length, and
the Rib and Web thickness and depth. The test also edits a revolve.
The Rectangular and Circular Patterns stay as they are, like Extrude:
their editors keep an unchanged definition exactly as stored
(preselect_tests::feature_mirror_and_patterns_reopen_as_feature_replays_even_for_body_creators).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* claim: #9234
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Colour popover: step buttons, H/S/V fields and recent colours (#9234)
Every colour field of the app opens the same ui_kit popover. It gains:
- step buttons beside each number (R, G, B and the new H, S, V): a press
steps once, holding repeats, for live fine-tuning;
- H (degrees), S and V (percent) fields on the same gamma HSV as the square;
- a row of the last 12 colours picked in any colour field. A colour joins
when an edit is finished; fine-tuning in one opening replaces its entry.
The list is saved with the app's settings (`recent_colors`), hostile
saved entries dropped and the list capped.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Recent colours: keep the shared list outside ui_kit, crate-visible only (#9234)
The store and the per-frame sync with the saved settings move to
crate::recent_colors; the ui_kit colour popover only shows the list and
adds finished picks. No plain pub escape from the gallery's ui_kit rule.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch drag: a step that throws points far from the sketch counts as failed
A held step of a drag that moved a point more than 1000 times the sketch's
size plus the drag's way was taken as solved, so a drag could leave points
around 1.4e6 mm away while reporting it reached the cursor. Such a step now
fails like one the solver cannot solve, and the cursor correction refuses it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch drag: a step that presses a line to no length counts as failed too
An end dragged past the point where its line would have to turn over (a
line held at its midpoint and by an angle, dragged past the midpoint; a
rectangle's corner dragged across the opposite side) pressed the line to no
length, where it has no direction left for its constraints. A step leaving a
line under 1/1000 of its length at the drag's start now fails like one that
throws points far away, so the drag stops just short of it (48 such drags in
4000 random sketches, none with this).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #7854
* MCP batch: refuse a step with an unknown key (#7854)
A step's parameters under another key (`args`) were dropped and the
command ran without them. The batch is now refused before its first
step, naming the key.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* MCP batch: a step carrying `expect` is refused, saying it is the batch's argument
#9904 gave batch an `expect` argument and tested that a step carrying one runs with
nothing checked. That is the silent drop this branch refuses: the step is now refused
before it runs, and the error says `expect` belongs to the batch.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Light a feature's faces after a later edit renamed them
Selecting a feature in the Browser or the timeline lights its geometry on the
model, by the face names the feature gave (`F<feature>:…`). A later edit that
moves or renames those faces (Offset Face, Replace Face, Delete Face) leaves
them named after whatever last touched them, so selecting the earlier feature
lit nothing.
When no face carries the feature's name, compare the model states either side
of it instead: a face whose name is new, or whose area, centroid or plane
moved, is a face the feature changed. Each is found on the body as it is now
by its name, or by a parallel plane and nearest centre, so a face a later
feature moved again is still lit.
The Browser row of the feature a picked face belongs to already lights; that
is unchanged (a body row lights too, as #3009 asks).
Test: a box raised twice with Offset Face; selecting the first raise lights
the face the second one renamed.
* Browser: light the feature of a picked face, not its whole body
Picking a face in the view lit the body's row as if the body were selected,
which in NX is not what a face pick shows: the Part Navigator marks the
feature the face belongs to. Faces, edges and vertices no longer light their
body row; the feature row of the picked face shows as selected instead
(`picked_feature`), while the session's selection stays the face or edge, so
Replace Face, sketches and the rest keep the pick they take.
The live face of the view still lights the face itself; the timeline already
marked the feature of a picked face and is unchanged.
Tests: the browser's 178 tests pass (selection arrays unchanged; only the
rows lit differ).
---------
Co-authored-by: clarkliu <277482660@qq.com>
The second fillet of a side whose radius is half its length needs all
that is left of the line, and was refused as too large. Such a fillet
now takes the whole line: the line goes, the arc ends where the line did
and is tangent to the curve that goes on from there. A 20 x 10 rectangle
filleted with r = 5 becomes a slot, a 10 x 10 square a circle. Several
corners in one call keep their arcs by id, as removing a line renumbers
the curves.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #7507
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch Fillet: a corner held by Coincident takes a fillet when a dimension measures to it (#7507)
Before rounding a corner whose two lines meet only through a
Coincident constraint, Fillet and Chamfer weld the two points into
one, but not when anything else referred to the second line's point,
such as a dimension measuring to the corner: the fillet was then
refused ("the lines must share a corner point"). The point is now
welded as well, and what referred to it refers to the corner, the
same place. A point that a constraint names together with the corner,
or a linked point, is still not welded.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Two selected lines that stop short of each other or cross were refused
with "the lines must share a corner point". Now each line's end on the
shorter side of their meeting point moves there first (extending a line
that stops short, cutting a line that runs past), and the corner is
rounded or cut as before. Parallel lines, ends held by other geometry
and lines whose length is held are refused and change nothing.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
An offset's ends follow its source curves on every solve, so a break's new
ends were pulled back: a broken offset arc came out in three pieces going
the long way round, and the solve moved curves the break never touched.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The offset sets an offset circle's radius from its source after the solve,
but the solve still counted that radius as a free unknown, so a
dimensioned circle and its offset reported 1 DOF instead of 0. Lines and
arcs were not affected: the offset already holds their end points. The
solve now holds an offset circle's radius too.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #10057
* Pattern and Mirror dialogs: explain Object Type, and say why a sketch is not taken (#10057)
The Object Type row of the Rectangular, Circular and Path Pattern and Mirror
dialogs gets a tooltip saying what Bodies and Features are and that a sketch
is neither. A sketch clicked for their objects, in the view, the Browser or
the History, now shows a hint naming what to pick and what to do with a
sketch instead of being ignored.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #8706
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Properties: measure faster, show progress, and stop when closed (#8706)
Properties on a large assembly measured every placed copy of every body
afresh (transform plus three fine tessellations each), so it took minutes.
#5203 (in #8103) already moved the measurement beside the window; this
builds on it.
- A rigid placement reuses the body's own cached measurement: volume, area
and counts unchanged, centre placed, box from the placed corners (quarter
turns) or from the body's shared mesh placed (other turns). Scaled or
mirrored placements are still measured as placed.
- Bodies are grouped by component once and the subtree traversal groups
occurrences by parent once (both were quadratic).
- The measurement beside the window is watched by a Progress: the card
says "Measuring... n of N bodies" with a bar, and closing it (or
switching designs) cancels the worker at its next body instead of
leaving it running.
- component.properties and occurrence.properties are answered on a copy for
control-channel requests too, as inspect.measure is.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Properties: count the measuring work instead of timing it in the reuse test (#8706)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
A switch between design tabs cancels the open dialog and tool of the design
left and shows MODEL. With the new Interface preference "Keep dialogs and
tools open when switching designs" (keep_work_on_tab_switch, off by default)
each tab keeps its dialog (with its picks and values), its editing state, its
tool and its toolbar tab, and has them back when it is shown again. A feature
still computing after OK is cancelled as before. A feature edit's saved source
revision follows the view refresh of the switch, so its Cancel still restores
the history.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #9743
* Update Links: linked designs that changed while the tab was away come back as one undo step (#9743)
Coming back to an assembly's tab read each changed link in an Update Links
run of its own, so each became its own undo step: one Undo took back one
component and left another copy of the same part at the new version.
component.update_links now takes `components`, a list, and goes on past a
design it cannot read: that link keeps its bodies, is named in `failed` and
gets a warning. The tab return sends one request with every changed link,
records as read only the ones that were, and says on the status line which
one could not be read.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Delete: the delete runs with what its check computed ahead (#9917)
With the history rolled back, a delete builds the history after the
marker to see what it breaks, once in the check that runs off the window
thread (Session::ahead) and once more in the delete itself, on the window
thread. The check's results were lost on the way: the dry run's scratch
session and the whole-history copy dropped what the model keeps, and the
check's kept results never reached the delete. The scratch and the copy
now keep what their model keeps, and the delete (at once or after the
confirmation) runs with the check's kept results, so it finds the
history there instead of building it again.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Delete: test that a confirmed delete runs with what its check kept (#9917)
With the history rolled back, deleting a side of the plate's outline is
checked off the window thread and confirmed first; the confirmation
carries the check's kept results, and the delete adds nothing to them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #10050
* Hide the origin point behind bodies like the axes and planes
The origin point is a flat dot painted after the 3D scene, so it always sat
over bodies while the axes and planes respect the through-bodies rule
(#2697, #2618). Ask the meshes the same question the depth test asks the
axes: is a nearer kept face in front of the origin along the view ray.
Fixes#10050
---------
Co-authored-by: Dahak Builds <dahakbuilds@users.noreply.github.com>
* Sketch: a centre rectangle's centre lines up with the axes when dragged (#10086)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch: a centre rectangle's centre lines up with the axes when dragged (#10086)
A centre rectangle's centre is held at the middle of a construction
diagonal (Midpoint), on none of its curves. Dragged by the centre, the
rectangle's own lines stayed snap targets and held the centre at their
middle, so it never lined up with the origin; dragged as a whole, the
centre wasn't a moving point, so only the corners lined up.
drag_snap::moving_points now takes all dragged entities at once and
treats a point held at a line's middle as going where the line's ends
go: it moves with a line whose two ends move, and when it is dragged
its line and the shape round it are no targets.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* claim: #10066
* Keep one dialog field active at a time (#10066)
Pick fields drew their accent ring from the dialog's active-input index
while text editors drew theirs from egui keyboard focus, so a Circular
Pattern dialog could show Axis and Quantity active together. A pick
field now drops its ring while a text editor holds the keyboard, and
clicking a pick field (or its clear button) takes the keyboard back.
Selection routing (d.active) is unchanged.
* Format editor_has_focus onto one line
---------
Co-authored-by: Dahak Builds <dahakbuilds@users.noreply.github.com>
* Navigation: wheel and trackpad gestures go on under a value box (#10061)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Navigation: wheel and trackpad gestures go on under a value box (#10061)
Any area above the model took navigation's wheel input, so a two-finger
pan, orbit or zoom stopped as the pointer crossed a floating value box
(Extrude's distance), and none could start over one. Model-anchored
canvas areas (Surface::Canvas: value boxes, a dimension's editor,
readouts) scroll nothing: navigation's wheel, trackpad pan and orbit and
pinch now go on under them. Their clicks stay theirs, and with value
stepping on the wheel stays with the value under the pointer.
Container::area records the canvas areas it builds; navigation asks
whether the top layer under the pointer is one (container::is_canvas).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* Rename Body: a pasted placement's body renames by the name the browser shows (#10017)
The browser lists the bodies of a component's second placement by their
placed names (`KB (K:2)`) and the context menu's Rename sends that name,
but body.rename looked it up only among the model's own bodies and
refused it ("no body"). It now takes a placed copy's name as the body it
shows, as appearance.assign does (#9740): the body is renamed in its
component, so every placement shows the new name.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Rename Body: test renaming a pasted placement's body by its placed name (#10017)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Rename Body: the test also hides the copy first and undoes the rename (#10017)
A copy hidden by its placed name stays hidden under its new name, and
one undo brings both names back (checks from the review on #10026).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #9079
* Modify ▸ Delete with nothing selected asks for a pick instead of a parameter error (#9079)
Starting `timeline.delete` from the ribbon, the command palette or the
control channel ran it with no parameters, so the notice showed the
engine's check ("`features` must list features, or `groups` timeline
folders"). It now deletes what is selected, as the Delete key does, and
with nothing selected shows a hint saying what to pick, changing nothing.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #10005
Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>
* F2 renames a group clicked in the Browser (#10005)
A group has no selection of its own: clicking its Browser row only
records it as the picked folder, which V uses. F2's rename_selection
looked only at the selection, picked components and canvases, so on a
group it did nothing, or renamed a body that was still selected.
F2 now takes a clicked group row first, as V does, and opens the same
Rename box as the group's context-menu Rename, which commits through
browser.rename_group. This works for groups inside components as well.
Fixes#10005
Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>
---------
Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>
* claim: #7974
* Viewport: dark parts keep their shape: linear-light shading and a sky fill (#7974)
Preferences > Graphics "Keep the shape of dark parts" (off by default)
shades the viewport in linear light: the key light and eye fill over a
sky fill that grows as a face turns up, plus the 4% of that sky every
dielectric reflects (a little more toward grazing), encoded to sRGB
once. Near-black faces keep a gradient; light and mid-grey faces stay
within a few levels of the sRGB shading. The world's up direction and
the switch ride in a new `sky` vec4 at the end of the viewport uniform,
and the frame key, so turning it on redraws. Off, the shader takes the
old path unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Preferences: a shortcut recorded from a search result takes the key (#10043)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Preferences: a shortcut recorded from a search result takes the key (#10043)
A Preferences search lists matching shortcuts with their key buttons,
but a recording started there only counted while the Shortcuts page
itself was open, and the page skipped its recorder during a search: the
key went into the search box and ran its command behind Preferences.
keymap::capturing now counts a recording while a search shows, and the
recorder runs for it; clicking back into the search field stops the
recording, so the search keeps its own typing. The recording's message,
a taken key's Use anyway / Cancel choice and the next-step hint show
above the search's shortcut matches too (outcome, shared with the page).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* fix(ui): disable bold in Edit Text when font has no bold face (#9940)
- Check whether font has bold face via font_data_styled() before enabling bold toggle
- Automatically uncheck bold and disable bold button with explanatory tooltip for built-in font
- Prevent sending bold: true in sketch.edit_text when font lacks bold face
- Add unit regression test in sketch_text::tests
Fixes#9940
* style: format choice.rs in ui_kit
* i18n: move sketch_text.no_bold to pending locale catalog with complete translations
* test(sketch_text): assert disabled bold on built-in font without error (#9940)
* claim: #9970
Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>
* Agent chat: no error when an agent does not serve session/set_model (#9970)
An ACP agent can list models in the unstable `models` field of its
session/new reply without serving `session/set_model`; it answers
JSON-RPC -32601 and the Assistant showed that as an error each time the
model was switched. Agents that offer the model as a configuration option
already go through session/set_config_option. A -32601 reply to
session/set_model is now logged at debug level instead of shown; every
other error still shows.
Fixes#9970
Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>
---------
Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>
* Delete: the check in a rolled-back history builds the history once when nothing breaks (#9917)
With the history rolled back, the check before a delete (which features
the delete would break) built the whole history twice, before and after
the change, with the marker removed. On a large design each build ran
every boolean after the marker: deleting twelve splines took half a
minute on the window thread. Only a feature that fails after the change
can be newly broken, so the history after it is built first, and when
nothing fails there the history before it isn't built at all.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Delete: test that the check in a rolled-back history still reports what breaks (#9917)
Deleting a hole in the plate changes the plate and every cut after the
marker and breaks none of them: the check reports nothing. Deleting a
side of the outline breaks the plate, and the check still says so, with
a cache too small to keep a state.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #8341
Co-authored-by: Cursor <cursoragent@cursor.com>
* Graphics: keep a preferred card when surface marks it incompatible (#8341)
Prefer the saved adapter by exact then case-insensitive name, including when only listed as surface-incompatible, and explain in Preferences when another card is still in use.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Format and clippy
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* feat(engine,ui): allow restricting point-to-point align to chosen axes (#9946)
* style: apply cargo fmt across crates
* fix(clippy): use filter then map instead of bool::then in filter_map
* claim: History: build the row plan once per revision and draw only the visible rows
* History: build the row plan once per change and draw only the rows in view
The vertical History worked out its rows from the design on every frame: for each row it
searched every folder's last feature (cubic in features and folders), cloned every name, and
looked up each row's result, sketch and selection by a linear search. It also drew and
hit-tested every row, including the ones scrolled out of view.
- The row plan (features, folders' depth, extent, header places, contents and joints, the rows
with listed sketches, which sketches have patterns) is built once per design change, keyed
by session, revision, marker, counts and the "Sketches: under their feature" choice, and kept
in the egui context; the visible order is kept beside it until a folder or fold opens.
- A feature row outside the scroll area's clip takes its place and publishes its handles, but
is not drawn or made interactive; one being found, dragged or focused, or under an open
menu, is drawn as before.
- Selection and results are collected once per frame; folder rows read their contents from
the plan.
- The horizontal strip collects the picked features (selection and dialog items) once per
frame and writes a chip's tooltip only while it shows.
Tests compare the plan with the per-frame searches it replaced on tangled folders in every
fold state, the kept plan with a fresh one after each kind of change, and the History drawn
with every row against the culled one (same places, same text in view, fewer rows drawn).
An ignored test times the panel on a large design.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Drop a needless borrow
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>
* claim: #8749
Co-authored-by: Cursor <cursoragent@cursor.com>
* Recover egui chrome after a long pause between frames.
Sleep/wake can leave the window black while the GPU device stays up: re-upload
the font atlas, clear cached icon textures, and repaint when the frame clock
jumps so the ribbon and panels are not stuck blank (#8749).
Co-authored-by: Cursor <cursoragent@cursor.com>
* Format
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch: a driven dimension nothing fixes any more takes a value again (#10018)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch: a driven dimension nothing fixes any more takes a value again (#10018)
A dimension placed while a constraint fixed what it measures goes in
driven, and stayed driven once that constraint was deleted: a
double-click only said it has no value to edit. The double-click now
tries making it driving on a scratch copy; when nothing else fixes it,
the value box opens at the measured value, and Enter makes it driving
(sketch.toggle_driven) at the typed value (parameters.change) in one
undo step. A refused value goes back to a checkpoint, so it stays
driven. While something still fixes it, it stays read-only as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* claim: #9945
* Sketch: Project gives its new curves the selected line type (#9945)
The sketch palette gives the drawing line type (construction, centerline, infinite) to the
curves a SKETCH › CREATE command reports under `curves`. Project reports what it made per link,
`links[].curves`, so its curves were never found and stayed normal lines whatever the palette
said. `apply_mode` now also reads the links' curves, so a projected line becomes construction,
centerline or infinite and a projected circle construction, in the projection's own undo step.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMbqRqYimvY26QzcvrojSC
---------
Co-authored-by: sascha2101 <221609842+sascha2101@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #9489
* Sketch: the Line tool can switch to a tangent arc and back without changing tool (#9489)
With Preferences > Sketch > "Rest on a line's last point to switch to a
tangent arc" on (off by default), leaving the chain's last point and resting
back on it for 0.35 s makes the next click a tangent arc, by the existing
sketch.arc.tangent command, and the same gesture goes back to lines. A label
at the cursor says which. sketch.arc.tangent's end can now name an existing
point, so an outline drawn this way closes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch: the cursor label names the command in words (#9489)
The label asked the catalog for a message id that doesn't exist, so it showed
the id. It now takes the command's translated label, and the test checks the
words it paints. Moved clear of the pointer's own badge.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Show/Hide put every sketch it showed in `shown_sketches`, the list of
finished sketches kept shown although a feature uses them. Hiding and
showing a sketch nothing used yet put it on that list, so once an
Extrude used it, it stayed shown instead of hiding itself as a sketch
never hidden does. Show now marks only a sketch a feature already uses;
one nothing uses is shown anyway and leaves the list alone.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: view corner Cancel cancels an unfinished tool first (#6709)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* View corner Cancel: cancel an unfinished tool before the sketch (#6709)
With OK and Cancel in the view's corner on, the cross took the whole sketch
even with a line half drawn. It now takes the steps one Esc takes first (a
drag, Select Other, the tool's points, then the tool) and cancels the sketch
only when nothing is unfinished. Esc and the cross share cancel_unfinished.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #9333
* Equation curves: the variable can carry its range's unit (#9333)
A curve can set variable_unit "range" (or tick the new box in its window): the
variable is then a length, so a ^ 2 / (4 * t) is a length when a is one. Start
and end must both be lengths, or one a length and the other a plain number in
the design's unit; anything else is refused. Saved only when set, as one pending
format change; without it curves behave as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: Engine: lower the per-command cost of evaluating large designs
* Engine: lower the per-command cost of evaluating large designs
- The fingerprint texts of features are kept in one store shared by every
worker, not per worker: a command evaluated ahead runs on a new worker each
time, where the per-worker store was always empty and every feature was
written out again. Same texts, same hashes.
- Pinning body names after a command keeps, per feature, the names of the
bodies it made with the state they were read from, so it no longer searches
every result's bodies (features x bodies per command), and finds results by
their index before searching.
- Pinning fingerprints the timeline again only from the first renamed feature
(or a pattern or mirror of features replaying one) when the model proves its
fingerprints are the design's; the fingerprints are those of the full pass.
- The evaluation cache's byte count looks bodies and sketches up instead of
searching them per body (bodies squared per feature). Same counts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #9176
* Preferences: turn the view to isometric when Extrude opens, and back afterwards (#9176)
Two new preferences on the Design page, next to "Extrude to a picked target", both off by
default:
- `extrude_view_isometric`: each time Extrude opens, the view turns to the isometric view, so
the depth can be seen right after a sketch seen face-on.
- `extrude_view_return` (only with the first): when Extrude closes, by OK, Cancel or Esc, the
view goes back to where it was before it turned, keeping the projection as the end of a sketch
does (#7038). The check is per frame, so every way the dialog closes is covered.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMbqRqYimvY26QzcvrojSC
---------
Co-authored-by: sascha2101 <221609842+sascha2101@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* MCP: batch and execute take expect {volume, bodies, bbox} and flag a mismatch (refs #3380)
An optional `expect` argument on the `batch` and `execute` tools: the volume
(with a relative `tol`, default 0.002), body count and bounding box (0.05 mm)
the agent computed before building. Once the call has gone through the server
measures the design as the `measure` tool does and the reply gains
`expectation: {ok, measured, expected, mismatch}`. A mismatch flags the tool
result `isError` with the mismatch text first; the design is kept as built.
A malformed `expect` is refused before anything runs. Without `expect`
nothing changes, and a batch step carrying one is a step with a stray key.
In 29 logged agent sessions every agent computed these numbers beforehand
and then spent a `measure` call plus a comparison by hand after every stage;
one missed a 0.25 mm skin until it compared. Volume and body count are
kernel-independent checks.
Refs #3380
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* MCP expect: filter then map for the bbox coordinates that are off (clippy)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* MCP: expect's coordinate text helper no longer shares a name with the JSON reader
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: bherbruck <tenbee91@gmail.com>
* claim: Save: hard-link the backup copy and check for unsaved changes once per revision
* Save: keep the backup as a hard link and check for unsaved changes once per revision
The backup of a save is the old file under a second name (a hard link renamed over the
old .bak) instead of a copy written out again; where links are refused (FAT, shares,
cloud folders) or the old file cannot be flushed, it is copied as before. Every point a
save can stop at leaves the design file whole and the backup the version before it.
Session::is_dirty compared the whole document with the saved one each time it was asked,
which the window does every frame for every tab. The answer is kept per revision and pair
of documents; the cache holds both, so neither can change in place while it stands, and
doc_mut lets it go first so edits stay in place.
On the rover in two unsaved tabs an idle frame takes 15.0 ms instead of 19.3 ms; a save
with its backup of the 11.8 MB file writes in 9.6 ms instead of 18.9 ms.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #9811
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Agent chat: the system note reaches agents beyond the Claude adapter
The note only went out as `_meta.systemPrompt.append`, an extension of the
Claude adapter, so Codex and Antigravity never saw it. It now also goes in
the standard ACP `systemPrompt` field for agents that offer it, and in
`_meta.antigravity.systemPrompt.append` for Antigravity.
Fixes#9811
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- 247 help texts (help.*) and 95 UI messages: theme options, viewport status, quick measure, personal sheet-metal rules, analysis details, sketch report
- Terms aligned with fr.json; no-break space before : ; ? ! as in the catalog
- Pending file, folded on main after merge
Co-authored-by: P'tite Tête <asso@ptitetete.com>
* claim: #9919
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Extrude: with every body hidden, a new extrude stays New Body instead of turning to Cut (#9919)
Smart Extrude passed the shown bodies as `participants` to keep hidden
ones out (#6901), but with none shown that list was empty, and the
engine reads an empty list as the whole scope: a profile on a hidden
body chose Cut, which has no shown body to change, so nothing was
previewed and OK did nothing. With no body shown it now chooses New
Body.
The operation it latches for a side is also decided again when a body
is shown or hidden while the window is open, as a new start already
does (#467): showing the body an extrude goes into turns it to Cut,
hiding it again back to New Body.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Claim #9916
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Report a bug: the window can be dragged by its title (#9916)
The window was pinned at the top middle of the screen (ui_kit::Window
anchor), over the model, in a sketch or not. It now starts there and can
be moved (`placed`), as the file-load card can.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
The `initialize` instructions were one paragraph of command shapes. Replace
them with version-neutral guidance: read `skill_reference solidcraft-modeling`
first, plan the numbers, build in a few `batch` calls, the primitive routes
and the fillet-shell-inside-holes order, what follows a parameter, `measure`
after each stage with a coalesced parameter check, and `timeline.edit` /
`parameters.change` / `edit.checkpoint` to fix instead of rebuilding. The
Assistant's system note gets the same pointer; docs/mcp.md says so.
`instructions_name_only_real_tools` now checks the command ids as well as the
tools, and `instructions_recipe_builds_a_parametric_shelled_box` builds a part
the way the instructions describe and checks its volume, axes and parameters.
Refs #3380
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
* MCP: measure summary and body_topology faces: cylinders for small verification replies (refs #3380)
`measure {"summary": true}` replies one short object per body (name,
volume_mm3, bbox, faces, closed) with body_count and total.volume_mm3,
as a tool and as a batch step. `body_topology {"faces": "cylinders"}`
replies only the cylindrical faces, the kernel's split halves merged
by radius and axis line into {radius, origin, axis, extent, names},
and no edges. Both defaults are unchanged. The merge uses only what
model.faces and model.edges already reply.
Measured on a 13-feature enclosure, body_topology is 74 KB and measure
1.1 KB per body; agents filter them with scripts, and seven of 29
benchmark runs asked for exactly a cylinders-only listing.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* MCP: keep body_topology's cylinder merge next to check_parameters' without a name clash
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: bherbruck <tenbee91@gmail.com>
* Claim part of #9738
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Assembly: default names skip names in use (part of #9738)
Contact sets, motion studies and exploded views were named by how many
there were, and a component's placements by how many it had, so after a
delete the next default name repeated one still in use, and commands
given that name acted on the first. They now take the first number no
item has, as joints' and the app's dialogs' default names already do; a
placement keeps the next number when it is free.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* claim: #9858
* 3-Point Circle keeps snapped clicks as coincident constraints (#9858)
The tool sent only the three clicked positions, so a click snapped to a
point or curve left no constraint and the circle was free once drawn.
Each click now goes as its snap, as the 3-Point Arc's do, and the engine
adds a PointOnCurve for each referenced point, as the centre circle's
through click does (#3099).
* Fix#9858 test: assert the clicked midpoint point, not the line end
---------
Co-authored-by: Dahak Builds <dahakbuilds@users.noreply.github.com>
A new tool that checkpoints the design, sets every value in one
parameters.change, reports measure, errors and cylinder axes, and
restores the checkpoint, so the loop set_parameter / measure /
body_topology / set back costs one call and one rebuild.
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
* Claim #9739
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Linked designs: one changed while the assembly was closed is read when it opens (#9739)
A link now keeps the SHA-256 of its design file's bytes as the assembly
last read them (DesignLink::file_sha256, a pending format change), set by
Insert Design, Relink, Extract to File and Update Links. component.links
reports `changed` from it, hashing a file again only when its time or
size changed (vfs::design_path_stamp, behind the design-path rule). The
app treats a link it meets for the first time whose file changed as not
read yet, so opening an assembly, in its own tab or in place of the blank
design, reads those parts again as a tab coming back does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Linked designs: read what changed last when a design opens in place of the blank one (#9739)
A read is taken up only by the design revision it started from, and
adopt bumped the revision after starting it, so the read was dropped.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Adds the last 79 German translations for messages that exist in en.json
but are missing from de.json, in a new pending file as described in
assets/locales/pending/README.md. Covers the agent chat tool summaries,
the history, sending to a slicer, Perforate, the window system and the
web app.
Adds 130 German translations for messages that exist in en.json but are
missing from de.json, in a new pending file as described in
assets/locales/pending/README.md. Covers the Render studio (lighting,
environment, sun, depth of field), bounded volume and interior scattering,
saved shots, the timeline messages, thread direction and the agent chat
and endpoint settings.
* claim: #9860
Co-Authored-By: Claude <noreply@anthropic.com>
* Sketch panel follows Dialog buttons at top (#9860)
The Sketch panel drew Finish Sketch, Look at and Slice in a footer under
its options even with Preferences > Interface > Dialog buttons set to
Top. Panel::palette now reads the same published preference as the
feature cards and draws its footer row under the header, above the
options, when it is Top. With the preference off nothing changes.
Changes a default: no
Co-Authored-By: Claude <noreply@anthropic.com>
* Docs: Dialog buttons at top also moves the Sketch panel's Finish row (#9860)
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* claim: #9742
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019FAWPuFoh9qxn2uS2j53QZ
* joint.drive: a suppressed joint stores nothing; the reply says what was asked and why a value differs (#9742)
Driving a suppressed joint answered moved: false with "H is suppressed"
but wrote the values into the joint, with an undo step, so the part
jumped when the joint was unsuppressed. A suppressed joint outside any
motion link now stores nothing.
The reply gains `requested` (the values asked for, before limits and
links) and `held`: each driven value that ended up elsewhere, with why
(a motion link sets it, as a screw's travel follows its turn; a limit;
contact; a closed loop). A drive that moves nothing gives the first held
value's why as `reason`, so driving a screw's distance alone no longer
answers moved: false without one.
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>
* claim: #9735
* Sketch: conflict messages name real constraints, not the refused one's id or a false 'fully constrained' (#9735)
* fmt + clippy: contains over iter().any (#9735)
---------
Co-authored-by: Dahak Builds <dahakbuilds@users.noreply.github.com>
* Add German translations, batch 7 (#7237)
Adds 130 German translations for messages that exist in en.json but are
missing from de.json, in a new pending file as described in
assets/locales/pending/README.md. Covers the parameter list setting and
the Render panel pages: GPU material overrides, nested dielectrics, saved
optics, output size, physical background, presentation filters, preview,
quality budget, regrade and the result area.
* German batch 7: render.result_sequence follows the catalog's Checkpoint term
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: bherbruck <tenbee91@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #9740
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019FAWPuFoh9qxn2uS2j53QZ
* Body names: inspect.interference refuses an unknown body; appearance reads take a placed copy's name (#9740)
inspect.interference dropped a name that is no body, so a misspelled or
wrongly cased name left that body unchecked and two overlapping cubes
answered count 0. It now refuses the name, as inspect.clearance does,
naming the closest body when one is near enough to be a typo or another
case (cmd::no_body, on the existing edit_distance).
appearance.chain, appearance.list and appearance.replace's scope take a
placed copy's name (KB (K:2)) and a placement's (K:2) for the body and
component they show, as appearance.assign already does, through two
Session helpers: own_body_name (on body_frames) and component_or_placement,
which appearance.assign now uses too. A name that is neither is still
refused. select.set already refuses an unknown body on main (#9737).
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>
The last 106 commands on the backlog get the two-sentence help under
their tooltip, in English, German, Japanese, Chinese, Arabic, Hebrew and
Vietnamese: Surface, Sheet Metal, Plastic, Inspect, Construct,
Configure, Parameters, Library, View and the few single ones (Bill of
Materials, Frame Member, Pattern on Path, Convert Mesh, checkpoints).
With that the backlog is empty, so it goes: the test is now
every_toolbar_command_has_help, and the docs that mentioned the backlog
say so. The help tests use sketch.drag, which is not on the ribbon, as
the command without help.
* claim: #9715
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Assemblies: mass, centre of mass and inertia per occurrence and rigid group (#9715)
assembly.mass_properties (and Session::mass_properties) gives, for every
occurrence and every rigidly-joined group, mass (kg), centre of mass (mm) and
the inertia tensor about it in world and frame axes (kg mm2), the input
the multibody dynamics work needs. Bodies are measured as Properties
measures them (kernel volume and centre, mesh gyration), once per body,
then moved by each placement and combined by the parallel-axis theorem.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #9619
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Settings: edit an added AI agent (#9619)
Agents you added in Preferences > AI get an Edit button between Test and
Remove. It reopens the Add agent dialog filled with the agent's name and
command, or its protocol, preset, endpoint, model and request timeout, and
Save changes the entry in place: same id, permission override and
always-allowed tools. The stored key is never read back into the dialog;
an empty key field keeps it (still only sent to HTTPS or this computer),
a new key replaces it. The control channel gets `edit_dialog`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
On Linux the window always had no OS decorations and drew Windows-style
caption buttons in the application bar. SOLIDCRAFT_TITLEBAR=system, or
"titlebar": "system" in graphics.json, now opens the window with the
desktop's own title bar and buttons (server-side decorations where the
compositor offers them). The default is unchanged; Windows and macOS
are unaffected.
Claude-Session: https://claude.ai/code/session_01Gnm1n2PVPqghMuR9kHWXfh
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #9258
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch: paste sketch geometry copied in another design (#9258)
Sketch geometry copied or cut in a sketch (sketch.copy / sketch.cut) now pastes into a sketch of another open design with Ctrl+V or the context menu, at the cursor or at the copied coordinates, with the constraints and dimensions among it. A dimension whose expression names the copied design's parameters keeps its value, in its unit; a text keeps its numbers. SketchClip::for_another_design prepares the clipboard; the app keeps the last sketch copy beside the cross-design body/component copy.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #9293
* Assistant: a sign-in confirmed in one tab holds in every tab (#9293)
The latest sign-in evidence per agent is window state: keep it across
tab switches, new tabs and replaced designs instead of resetting it with
the tab's conversation, so a new tab no longer marks the agent as signed
out when discovery's login probe is older than the chat's proof.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #9060
* Ribbon tooltips: help for the Sketch tools (#9060)
84 sketch commands get the two-sentence help under their tooltip, in
English, German, Japanese, Chinese, Arabic, Hebrew and Vietnamese, and
leave the backlog: every Create tool (lines, rectangles, circles, arcs,
slots, polygons, splines, conic, ellipse, text, points, the 3D sketch
tools, Project, Intersect and the other reference tools), every Modify
tool (Trim, Extend, Break, Offset, Fillet, the chamfers, Move, Scale,
Transform, Blend Curve), the patterns and Mirror, every constraint,
AutoConstrain, Sketch Dimension, Create/Edit/Finish/Cancel Sketch and
copy, cut and paste. The help tests use inspect.zebra as the
undocumented example, since sketch.trim has help now.
joint.create's flip text and the assemblies guide said two origins at one
point with the same z stay as they are; a point origin without x takes its
x from z, so a part turned about z turns back. Say so, and point to giving
x on both origins or joint.as_built.
edit.checkpoint and edit.restore_checkpoint now say that restoring or
forgetting uses the checkpoint (and later ones) up and that only the last
16 are kept, and the "no checkpoint N" error says why. The editing and
mechanisms skills no longer read as if a checkpoint can be restored twice.
Co-authored-by: thocsmith <193096300+thocsmith@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The browser asks about every joint of the design on every frame, and
the answer walked the list of hidden joints: a nested STEP assembly
hides thousands of them, so a frame cost grew with the size of the
design.
Session keeps the hidden joints and folders as sets and answers from
them. The sets are built when the view changes and kept until it
changes again: the revision says the design changed, and the lists
say the view changed under it (the view is replaced outside the
engine too, without a revision of its own).
The behaviour is unchanged: the hidden joint stays hidden, the shown
one stays shown, and undo and redo keep the hiding right.
* claim: #9641
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Insert SVG: say why a drawing has nothing to import, and read UTF-16 files (#9641)
An SVG with nothing SolidCraft draws (only an embedded picture, shapes
pulled from another file with <use>, or not an SVG at all) was refused with
"invalid parameters for sketch.insert_svg: the drawing has no geometry
SolidCraft reads". It now names the file and says why, pointing pictures at
Insert Canvas and Canvas to Sketch. SVG files with a byte-order mark or in
UTF-16 now read, and a gzip-compressed (.svgz) file is refused clearly.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Insert SVG: decode UTF-16 with as_chunks (clippy)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #9041
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Render: start the GPU path tracer setup only when the GPU image method is chosen (#9041)
Entering the Render workspace started the GPU path tracer setup even with
the default Raster image method. The setup compiles the path shader, which
some Vulkan drivers (Mesa RADV/ANV, NVIDIA) take gigabytes over: the memory
climb reported while orbiting in Render. The setup now starts only while
Render is open with the GPU path method.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #9730
* Preferences: unapplied changes no longer reach the settings file while the dialog is open (#9730)
* Preferences: hold unapplied changes out of the settings file while the dialog is open
While prefs_window.snapshot is set, SolveApp::prefs() writes the snapshot's
values - the ones Cancel restores - so the periodic autosave, quit, window
close, web save and control channel can no longer persist a change that was
never applied. Apply re-baselines the snapshot; settings the dialog cannot
change keep saving live. Adds regression tests and updates the tests that
pinned the old behavior.
Fixes#9730
* prefs: bind the snapshot's shadow outside json! so the ui-containers guard reads it
---------
Co-authored-by: Dahak Builds <dahakbuilds@users.noreply.github.com>
* claim: #9686
Co-Authored-By: Claude <noreply@anthropic.com>
* Edit Joint: joint.edit takes the two sides
joint.edit accepts a and b in the form joint.create takes them (read by the same origin_param), so a joint can move its attachment without being deleted. A side left out keeps its place. The two sides must stay on different occurrences, checked as joint.create does. Limits, values, expressions and the name are kept; a relation's values start again where its parts are.
joint.edit gets an input contract and reply type (schema/joints.rs), and leaves schema/unconverted.txt.
Co-Authored-By: Claude <noreply@anthropic.com>
* Edit Joint shows both sides and picks them again
The Edit Joint dialog now shows the two sides in the same selection inputs as Joint, each pre-filled with the face or edge of its own component under the origin. OK sends joint.edit a or b only for a side picked again, so an untouched side keeps its origin exactly.
Co-Authored-By: Claude <noreply@anthropic.com>
* Edit Joint: a moved side turns the part as joint.create would
joint.edit kept the joint's old angle when a side changed, so the part could turn by a half turn about its axis when the new origin's x lay the other way. When a side moves and no angle is given, the angle is worked out again as joint.create does (the least turn from where the part is now); a relation's angle is its tilt and stays.
The mechanisms reference says joint.edit takes a and b.
Co-Authored-By: Claude <noreply@anthropic.com>
* Edit Joint: pre-fill sides by name, send the angle only when changed
A side shows the face or edge its snap names, the piece nearest its stored pick when the name has several, else the one nearest that pick; it no longer searches outward from the frame origin, which could show the plate beside a hole for the hole's wall. A point or frame side shows nothing. OK sends the angle only when the user changed it, so a side moved without it turns the part as joint.create would.
Co-Authored-By: Claude <noreply@anthropic.com>
* Edit Joint: a moved side leaves an angle expression alone (#9686)
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* claim: #9465
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Face attachments: a piece of a face its certificate finds the other pieces of keeps that certificate (#9465)
The nightly action matrix has refused a sketch and an offset plane on the
sensor enclosure lid's underside (`examples/sensor_enclosure :: Lid/F2:top`)
since #8278. The lid's lip, joined below it, parts the underside into a ring
and a panel; the ring rests on the case and is named after the case's top,
so the lid's cap certificate (via certified_pieces) finds only the panel.
#8278 then refused the certificate because it doesn't find the picked face,
and the history-name fallback can't take a name made by another body's
feature.
A face that is no pattern or mirror copy and lies on the same oriented plane
as the pieces the certificate finds is another piece of that face: the
certificate stands for it, as before #8278. Copies (the #8278 case) still
fall back to their own history name.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #8510
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Measure: drag the labels away from the part (#8510)
The distance and X/Y/Z labels in the view follow a drag of the pointer and keep
a leader to what they measure; a double-click or a new measurement puts them
back. A press on a label picks nothing, starts no box selection and opens no
Select Other.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #9105
* Sketch: alignment guides from any hovered point while drawing (#9105)
While a drawing tool waits for a point, hovering a line or arc end, a
rectangle corner or a free point wakes the same temporary horizontal/
vertical tracking guide as a circle centre or line midpoint. Idle
hovering keeps centres and midpoints only. Preference
sketch_tracking_points (on) turns it off.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #9130
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Browser: a Joints folder under each placement lists the joints that attach it (#9130)
Each component placement in the Browser gets a Joints folder (closed at
first) listing the joints whose Component 1 or Component 2 is that
placement, each beside the placement it joins it to. The rows are the
root folder's joints: click picks, the eye hides the glyph, double-click
edits and the menu is the joint's menu. The root Joints folder is
unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #8275
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Settings: written to disk in the frame they change, not only at a clean exit (#8275)
eframe saves app.ron every 30 s and when the window closes normally. A
change made since the last save was lost whenever the app didn't close
normally: ended from the Task Manager, stopped by a sign-out or shutdown,
or crashed or hung in the graphics driver on the way out. The next start
then showed the old settings (View navigator back to Axes, Display quality
back to Normal).
The desktop app now writes its settings and flushes them in the frame they
change (frames with input and the one after), as the web app already does
(#8644). Unchanged settings are not written again.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Settings: write at most once a second, and only after input that can change them (#8275)
Review: pointer motion came in every frame while the mouse moved, so the
settings were serialized every frame; and some settings change in every frame
(a panel's width while its edge is dragged), which would have flushed app.ron
on the UI thread each frame. Only clicks, keys, text, paste, wheel and pinch
count as input now, and the file is written at most once per second: a change
made in between waits, a repaint is asked for when its second is over, and it
is written then, so an idle app still writes it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #9062
* Sketch drag: a line is never shrunk to a point to follow the cursor (#9062)
A line's end on a horizontal construction line through the origin, dragged
up off it, made the construction line vanish. A horizontal row is
b.y - a.y, which a line a hair long meets at any tilt, so the drag's held
solves met the point-on-line by shrinking the construction line: to 1e-5 mm
on the first frame, 1e-7 mm by the tenth, and the end went up.
A drag step (held solve, the tangent-ends-held solve, and each cursor
correction pose) that shrinks a line or circle from over 1e-3 to under
1e-4 now counts as failed, as a step that wraps an arc does. The end then
stays on the construction line, which keeps its length.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #9034
* Assistant: say when an installed agent can't start, and how to reinstall it (#9034)
An agent whose program is found but fails its `--version` check without
printing a version (npm's opencode.exe left as a placeholder when the
package's setup script did not run, which Windows refuses with "not
compatible with the version of Windows you're running") was listed like
a working one, and connecting only said "The agent stopped." with the
system's message.
Discovery now keeps why the program did not start: the spawn error
(Windows' `%1` replaced with the program) or, through a `.cmd` shim, the
first stderr line or the exit status. A probe that times out still says
nothing. The picker shows "Installed, but it does not start: ...", and
connecting first explains it with the agent's install command and Refresh.
The control channel status carries `problem`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #9696
* Show an edited Free Move its stored turn as the X/Y/Z angles that remake it (#9696)
* cargo fmt
---------
Co-authored-by: Dahak Builds <dahakbuilds@users.noreply.github.com>
* claim: #9142
* Exploded View: grey it out until the design has two components (#9142)
On a part made of plain bodies (the sample plate) Exploded View opened its
dialog and failed on OK with "an exploded view needs two or more
occurrences". The command is now disabled until the design has two or more
occurrences, and its tooltip says why and which commands make components.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #9624
Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>
* Control channel: document.inspect reports undo, redo and path in the app (#9624)
The app answers document.inspect on a scratch copy of the session, which
has no undo or redo history and no file path, so the reply always said
undo 0, redo 0, path null. Take those three values from the live session
and put them into the reply.
Fixes#9624
Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>
---------
Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>
* claim: #1044
* Continuous orbit turns about the Orbit around point (#1044)
With Orbit around set to the point under the cursor (or the origin), Continuous
orbit still turned about the view's centre. Cursor orbits move that centre off
the model, so the model swung across and out of the view while it spun. The
spin now turns about the origin, or the model point in the middle of the view
(else the middle of the bodies in view), held from start to stop.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #9457
Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>
* Keep the sketch pane still at fractional display scaling (#9457)
Right-docked cards were placed by their right edge from last frame's
measured width, which flipped by one physical pixel each frame at
scales such as 125 % in the Docked layout. Place them by their left
edge from the width they are held to instead.
Fixes#9457
Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>
---------
Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>
* claim: #9731
* Sketch Extend: only an end on a circle counts as held (#9731)
grow_line's on-circle test was true for every point inside the circle; test distance to the circle instead. Also pin the new end point of a held-end extension onto the curve it stops on, as the free-end path does.
* Fix regression test harness usage and held-end setup (#9731)
---------
Co-authored-by: Dahak Builds <dahakbuilds@users.noreply.github.com>
construction_visible checked the item's own hide and its folder, but not
the placement its component is drawn at, so hiding Part:1 left Part's
planes, axes and points on screen. It now asks feature_placement_visible,
the check sketches already made; the placement path is computed in one
place for both.
* Arabic: colour key wording in the viewport analysis status help
* Arabic: tanween and direction-neutral wording in the browser eye position note
* Arabic: outline wording and no direction mark in the print face highlight tip
* Arabic: sketch term in the constraint glyph note
* Arabic: checkbox wording and discard verb in the parameters apply-live option
* claim: #9680
Co-Authored-By: Claude <noreply@anthropic.com>
* Browser: drag over the eyes to show or hide many rows at once (#9680)
Press an eye and drag up or down over other eyes: each eye the pointer passes
takes the first eye's new state (shown or hidden), and the whole drag is one
undo step. A plain click is unchanged. Dragging near the list's top or bottom
edge scrolls it. A component's eye now goes through browser.visibility, so it
is undoable like a body's.
Co-Authored-By: Claude <noreply@anthropic.com>
* Browser: fix the eye drag's undo merge and layering (#9680)
Merge the drag's steps from the undo depth noted at press, when the design
and visibility still match, instead of searching the history. Run the merge
every frame from logic(), so a release under an overlay is never missed. An
eye under a popup or tooltip no longer takes part in the drag. Adds a
regression test for a drag from the Origin's eye after a show.
Co-Authored-By: Claude <noreply@anthropic.com>
* Browser: say when the eye drag's undo merge gives up (#9680)
Co-Authored-By: Claude <noreply@anthropic.com>
* Gallery test: the eye-drag state key is not a renderable element
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: bherbruck <tenbee91@gmail.com>
* claim: #9060
* Ribbon tooltips: help for the Assemble and Insert tools and the Create primitives (#9060)
56 commands get the two-sentence help under their tooltip, in English,
German, Japanese, Chinese, Arabic, Hebrew and Vietnamese, and leave the
backlog: every Assemble tool (components, joints, contact, motion,
position), every Insert tool (files, images, 2D, library, links) and the
Model > Create drop-down primitives (box, cylinder, sphere, torus, cone,
coil, pipe, thread, rib, web, emboss, boundary fill, bounding solid).
Their drop-down rows now show a tooltip as well. The help tests use
sketch.trim as the undocumented example, since solid.rib has help now.
* claim: #2153
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Zebra: flat faces keep one stripe colour while the view turns (#2153)
A face whose normal lies in the upright plane through the view (every
horizontal face) reflects straight ahead, where the zebra's sideways
coordinate is zero: exactly on the edge between a black and a white
stripe. Rounding then picked one or the other from frame to frame, so
the face flickered while the camera orbited. Straight ahead is now white.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Fold the pending design format changes (browser-nested-groups, point-cloud-properties, print-faces) into format 45.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #9741
* motion.list gives study keys in motion.study units
Angle keys came back in radians while motion.study takes degrees, so an
agent that listed a study and wrote it back changed every angle by a
factor of 57.3, silently. Convert key values on the way out like
joint.list does (degrees for angles, mm for distances), give each key a
unit and its joint's name, and add a round-trip regression test.
---------
Co-authored-by: Dahak Builds <dahakbuilds@users.noreply.github.com>
* claim: #9407
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Section caps: a body touching another along a curved face keeps its own cap (#9407)
Seen through a cut, a far wall and another body's face lying on it (a shaft
in its bore, a ring in its housing) are the same surface faceted two ways, so
the marking pass let the other body's face win in streaks. The section's
passes now first count the faces behind the cut in the stencil (back +1,
front -1): where the count says the cut runs through a body but a front face
won, the back faces are drawn again pulled toward the eye by a few pixels and
take the cap back.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #9279
* Parameters: an option to hold edits until Apply or OK (#9279)
"Apply each change as you leave its field" (on by default, remembered in
the preferences as parameters_apply_live). Off: an expression edit is
checked when its field is left but waits, without a preview; Apply or OK
sets every held edit in one parameters.change {changes}, so the design
rebuilds once and one undo takes them back. Cancel, the close button and
Escape drop them. Turning the option back on applies what waits.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Docs: the parameters_apply_live preference in ui.prefs (#9279)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Parameters: the hint says edits wait for Apply or OK when they do (#9279)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Adds 130 German translations for messages that exist in en.json but are
missing from de.json, in a new pending file as described in
assets/locales/pending/README.md. Covers the remaining CPU lighting and
reflection controls, render diagnostics, lens and film effects, GPU path
tracer options, HDR source details, the resource library and the raw
linear EXR options.
* claim: #9144
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* DXF export: the full release 2000 file structure strict readers need (#9144)
The view and flat pattern DXF left out parts of the AC1015 file that a
strict reader expects: the CLASSES section, the VPORT, VIEW, UCS, APPID
and DIMSTYLE tables (with *Active, ACAD and Standard) and the ACAD_GROUP
dictionary under the root dictionary. They are written now, along with
$DWGCODEPAGE; the active viewport looks at the whole drawing.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #8776
* Crash reports: a window opened after startup no longer labels the run "during startup" (#8776)
egui-wgpu reports "window surface" and "surface configure" for every native
window, also the ones opened after startup, such as the agent chat in its own
window. Each one overwrote the journal's "running" phase, so a later unclean
exit was reported as "during startup (surface configure, N s after launch)".
crash::startup_step records a startup step only until the app runs, and the
app's egui-wgpu hook calls it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #9472
Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>
* Browser: the recover-unsaved-work card always shows Recover and Discard
A window's scrolling body was only offered the height the card had the
frame before. A card first laid out while the page was still tiny (a
browser canvas not sized yet) kept a body of no height for good, so only
its title showed and the required choice could not be made. The body is
now offered the room the screen leaves and shrinks to its content, which
also covers the desktop recovery card and every other system window.
Fixes#9472
Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>
---------
Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>
Adds 130 German translations for messages that exist in en.json but are
missing from de.json, in a new pending file as described in
assets/locales/pending/README.md. Covers the quick measurement, a few
Preferences labels and a large part of the Render panel (adaptive
sampling, surface aging, camera background, CPU lighting and environment).
* claim: #9565
Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>
* feat(browser): reorder features by dragging rows of the History in the browser
Let users drag features in the History shown in the browser to move them, with the same drop bar and checks as the History at the left, while the rollback marker still drags on its own.
refs: #9565
Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>
---------
Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>
* Add Finnish (fi) UI localization (#9626)
A fi catalog ("Suomi") translated from en.json, all 4801 messages,
registered in LOCALES between es and fr, with
finnish_catalog_is_registered_and_translates_keys, the language listed
in docs/localization.md (registered and partial) with its terms, and a
pending attribution row.
Fixes#9626
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Finnish: the 3D mouse Restore defaults button gets its own label
It shared its text with Restore defaults elsewhere on the page, so the layout test found
the wrong button.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Janiz-zzz <267742026+Janiz-zzz@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
* claim: #9492
Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>
* fix(viewport): keep running when the view area comes out inside out
Make the layout beside the docked Assistant give way instead of closing the app when a window reconfigure leaves the view area inverted.
refs: #9492
Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>
---------
Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>
* claim: #3305
* fix(agent-chat): support ACP configuration options (#3305)
Handle current select options for model, thinking effort and mode, with confirmed snapshots and legacy compatibility. Full quality gate attempted; draft delivery authorized with unrelated geometry/property/timing failures documented in PR #3867.
* fix(agent-chat): keep configuration selectors inline (#3305)
Use compact value pickers and bounded wrapping to preserve conversation space; verified at normal and narrow widths with the installed OpenCode V2 server.
* style(agent-chat): format tool events
* claim: #9358
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Parameters: the expression field opens wide while you edit it (#9358)
A parameter's expression that is too long for its cell opens over the
cells to its right while it has focus, wraps instead of scrolling, and
shows the live value or error under it. Enter still applies it and the
field returns to its column; a short expression stays in its cell. The
add row's expression field does the same.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
On a loaded runner the hover alone has taken 70 ms against a 190 ms index, which failed
unrelated pull requests. Waiting for the index would still cost all of it.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #6106
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Extrude: a sketch picked in the Browser or History takes all its profiles (#6106)
With Extrude, Revolve or Emboss open, a plain click on a sketch row in the
Browser or on a sketch in the History now fills Profiles with every profile of
that sketch (the island inside another profile's hole is left out). Before, the
click only changed the selection, which the dialog never reads.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
* claim: #2227
* 3D mouse: optional orbit while a sketch is open (#2227)
A new Navigation preference, "Turn sketches with the 3D mouse"
(space_mouse_sketch_orbit, off by default), lets the cap and a device
app turn the view while a sketch is open and seen square on, as the
mouse already can. Off keeps today's behaviour: pan and zoom until the
view is tilted away from the sketch (#6687). Restore defaults on the
3D mouse section resets it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* 3D mouse: keep the settings list in order after the rebase
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>
* claim: #9495
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Assistant: say when a reply hit its length limit or was declined, not that the model lacks tools (#9495)
The API client never read why an answer stopped, so a Claude reply cut
off at max_tokens while thinking (its thinking text is empty by default),
or a refusal, ended the turn as normal with nothing said. The chat then
blamed tool support: "The model didn't use SolidCraft's tools".
Reply now carries the stop reason: Anthropic stop_reason max_tokens and
refusal, or OpenAI finish_reason length and content_filter and a message
refusal. Any other value is a normal end. The turn ends with that reason,
which already keeps the tool-calling notice away, and the chat shows
what happened in place of the raw "(max_tokens)", for ACP agents too. A
tool call cut off at the length limit has incomplete input, so it is
neither run nor kept in the history.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Hole: each dropdown option shows the hole it makes while hovered
Extents, Hole Type, Hole Tap Type and Drill Point list names only, so seeing
what an option makes meant picking it. Hovering an option now shows a small
section of that hole beside the list, drawn by the dialog's own section
painter from a copy of the form with the option applied.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhpMr6L9PR8SJXjM6MbQrD
* Hole: give the dropdown previews a gallery entry
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: danieldimitrov86 <danieldimitrovdev@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
While Frame shows the picture's frame, the preview projects into that frame
rather than the viewport, but Fit Product and the fit on entering Render
framed the product for the viewport's free area, so a picture taller than
wide cut the product at its sides. The fit now uses the viewport layout
seen from the frame (the frame as the viewport, the free area inside it
with the usual 16-point margin).
Entering Render fitted before the saved setup replaced the draft, so a cold
workspace with a portrait setup fitted for the old draft's aspect; the fit
now runs once the setup in effect is loaded.
render.camera.fit takes the picture's aspect (0.000001 to 1000000) through
Camera::fit_aspect, which publishes only a valid fitted camera: an aspect
that would divide the distance into infinity is refused and the previous
camera, document and undo stay as they were.
* Claim #9479
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Preferences: an option to keep value boxes out of the view (#9479)
Design > "Show values beside their handles in the view", on by default.
Off, a command's value box (Extrude's Distance, Fillet's Radius, a hole's
reference distances...) isn't drawn over the model; the command window's
value field takes the keyboard that opening the window or a pick gives the
box, its text selected, so a number typed after a pick still sets it. The
handles stay and still drag the value.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Preferences: the value box option in Arabic, Hebrew and Vietnamese too (#9479)
The complete catalogs must carry every English message.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* claim: #8941
* Render: a size the CPU methods refuse names the profile that renders it (#8941)
The Shots strip offers 4K UHD whatever the method. Under the default CPU
raster, and under CPU ray tracing's default Compact profile, Render was then
refused with English engine text that didn't say how to render that size.
The refusal is now translated and names the smallest CPU resource profile that
admits the size (with CPU ray tracing, for raster, which has no profiles). The
size lists mark such presets. Nothing is switched: method, profile and
resolution still change only when chosen, and GPU path tracing is unaffected.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #8563
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Parameters: sort each section by name (#8563)
The Name heading of every Parameters table steps through A to Z, Z to A and the
listed order. Each section sorts on its own: Favorites, User Parameters and each
model group (the groups keep the design's order). Names compare in natural order
(d2 before d10, letters ignoring case). The choice is kept in egui's persisted
memory like a section's open state. While sorted, a user parameter's move arrows
are disabled. The sortable heading and natural_cmp live in ui_kit::table.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Parameters sort: gallery entry for the sortable heading, natural_cmp private
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Browser: nest groups and reorder components
Groups can hold other groups (up to 8 levels) and be reordered with the
items around them. Sub-components can be reordered too, through a new
`components` list in `browser_order` that is not drawn as a folder.
Document: `BrowserGroup` gains `parent`; group items and `browser_order`
lists hold `BrowserEntry` values (a number is a group, a string an item).
One pending format file; older designs read unchanged. Loading and
refreshing drop what the lists name that is gone, and cut cycles and
over-deep nesting to the top level.
Engine: `browser.group` takes `parent`; `browser.move`, `browser.order`
and `browser.ungroup` handle groups. A change to only the browser's
groups and order no longer re-evaluates the model.
UI: nested rows, dragging a group row (top of a row: before it, the rest:
into it), New Group inside a group, and dropping a placement on the top
of a sibling to reorder it.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Fixed formatting
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Fixed unintended side effect in render scene browser test
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Preserve group order in render workspace
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Increased test coverage
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Updated clipboard test from upstream main for group item type
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
* claim: #9281
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch text: a number parameter in the words shows its value (#9281)
A text's words expression can now name a number parameter: `'W' + Width`
reads "W85 mm" (lengths in the design's units, angles in degrees, up to
three decimals). `number(Width)` drops the unit and `number(Width, 2)`
fixes the decimals. The words and the cut follow a parameter edit in the
same rebuild, a rename reaches inside number(), and deleting the parameter
is refused while a text shows it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch text: a Text starting with = builds its words from parameters (#9281)
In Edit Text, a Text that starts with = is the words' expression
(='W' + number(Width)): OK keeps it, the dialog shows it again with its =,
and an untouched OK no longer turns it into typed words. Typed words still
replace it, and a saved text that happens to start with = stays typed
until it is edited. The field's tooltip says how.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch text: the formula hint in every complete catalog (#9281)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #9246
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Expressions: Down arrow in an empty field lists every named parameter (#9246)
In an empty expression field, Down opens the completion list with every
named parameter (not just the first eight), armed on the first one; Up and
Down walk it, the list scrolls to keep the chosen row in view, and Enter
inserts it. A field that already holds text keeps Down for itself.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Expressions: Preferences caps the Down-arrow parameter list (#9246)
Down arrow in an empty field lists the parameters up to "Parameters
listed by Down arrow" in Preferences > Design (default 50, 5 to 200),
favourites and the design's own parameters first, so a large design
does not build a list of hundreds of rows.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Expressions: a label that fits for the parameter list cap (#9246)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Expressions: the parameter list cap in every complete catalog (#9246)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
After a lost device the next start began from on_exit, while eframe was
still writing app.ron, and the old process then hung in
vkDestroySwapchainKHR on NVIDIA. Start it from Drop for App instead, after
eframe has joined the settings save and before it drops the window's GPU
surface, and end the process there after a loss. The update restart moves
there too. The loss reason is passed to the next start and shown in its
note.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The test asserted the readout was still empty right after the first poll, but the measuring thread can finish first, so it failed now and then in merge groups and on unrelated pull requests. It now asserts only that the measurement arrives.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Everything en.json gained since the last catch-up except the render.*
messages: Preferences, navigation bar, assistant settings, materials,
hole presets, joint move, edit in place, point clouds, view angles,
tool help texts and error hints. Added as one pending locale file.
* Sketch: the coordinate readout starts right of the pointer arrow (#9480)
The readout of a sketch tool's point (2D and 3D sketches) started 10
points below and right of the snapped point, under the system arrow,
which hangs about 12 points wide below the hot spot and covered the
first value. It now starts 18 points right and 10 below the pointer
(the point snaps away from it), from the snapped point only when there
is no pointer over the view.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch: the readout clears the next larger pointer too (#9480)
The reporter uses the system's pointer size 2, whose arrow is about
18 × 28 points: start the readout 26 points right of the hot spot
instead of 18, and test against that arrow.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The clamp that crashed #9492 (f32::clamp between a view's edges, which
panics once the view comes out inside out) also sits in the room tool
windows get beside the docked Assistant, the Render selection bar's
anchor and a left-anchored panel's anchor. Use shell::within there,
the clamp that gives the lower edge for an empty range (#9271).
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #9081
Co-authored-by: Cursor <cursoragent@cursor.com>
* Tell same-named recent designs apart (#9081)
Show the file name with extension, ellipsize long paths in the middle so the end stays visible, and label imports by kind (STEP, STL, …) instead of always Design.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Start: Recent paths take their ink from the theme, and the middle-ellipsis helper is exported for tests only
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #9170
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019FAWPuFoh9qxn2uS2j53QZ
* Model cache: a design with mesh bodies keeps its saved model (#9170)
One mesh body (an inserted STL or 3MF, or a STEP part shown as a mesh of
its faces) left the model cache out for the whole design, so opening it
evaluated every feature again. The doc records refused mesh bodies, and
the native B-rep codec the io cache stores bodies with refuses them too.
Kernel work is paused, so the kernel is left alone: the io cache stores a
mesh body as its own slice kind (SCMESH01, a JSON header with the counts,
colour and paint, then the vertices as f64 and triangles as u32), read
back through mesh_body, with_color and with_paint. A mesh body is kept
only when its slice reads back as the same vertices bit for bit, the same
triangles, colour and paint. The decoder checks the counts against the
bytes before allocating. The picture shown while opening draws a mesh
body with its own triangles (native_preview_mesh refuses mesh bodies).
Cache version 4 -> 5; docs/design-container.md describes the mesh slice.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019FAWPuFoh9qxn2uS2j53QZ
* Model cache: decode mesh slices with as_chunks, as clippy asks
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
* claim: #9417
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Assistant: drop earlier thinking blocks when history is trimmed (#9417)
Claude signs each thinking block against the conversation before it. Once
drop_old_views swaps an older view for a note, or trim_history drops the
oldest messages, every later block stops matching and the API refuses the
request with HTTP 400 "Invalid signature in thinking block". For the
Anthropic protocol, a trim now also removes the thinking and
redacted_thinking blocks from earlier replies, which the API allows. A
reply that was only thinking keeps a short note. Requests where nothing
was trimmed are sent unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #9324
* Sketch: Project takes a construction axis, not only X, Y and Z (#9324)
sketch.project with {"axis": "Axis1"} upper-cased the name, and the axis link knew only the
origin axes, so a construction axis failed with "unknown axis `AXIS1`". X, Y and Z (any case)
are still the origin axes; any other name must be a construction axis, kept as its feature names
it. The link finds the axis where the timeline before the sketch puts it (construction_axis_in,
as Revolve does) and projects it as an origin axis: its line in the sketch plane, or a point
when the axis stands square to it. Renaming the axis renames the link, as for a construction
plane's projection.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* claim: #8398
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Closing: stop the Home picture builds instead of waiting for them (#8398)
Dropping SolveApp waited up to 10 s for the Home picture threads to end
(#6063). The thumbnails thread imports and rebuilds every cold recent
file (STEP and mesh imports, designs without a stored picture) one after
another, and its channel only closes once all are done, so closing the
window froze until every cold recent was built or the 10 s ran out.
The picture threads now build under a shared progress watch that
finish_pictures cancels first: the design being built stops at its next
feature (a STEP import at its next step), the remaining cold files are
skipped, and a build stopped part way draws and caches no picture.
Pictures already on the GPU are still waited for, so the driver crash
#6063 fixed stays fixed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Quick measurement: the selection's length, area or distance in the view's corner (#7000)
With edges, faces, sketch curves or points selected, the view's bottom right corner above History
shows what they measure, as the Measure tool lists it: an edge's length (Ø and R for a round
one), a face's area, a point's position; two items' distance and angle; several edges' or faces'
count and total. It is computed off the window thread from a copy of the session whenever the
selection or the design changes, and a click copies it. Preferences > Interface turns it off. The
Measure dialog's rows come from the same function.
* Quick measurement: off by default, turned on under Preferences > Interface (#7000)
The quick_measure preference now defaults to false, so nothing changes until it is turned on.
Its tests turn it on, and a new test checks that nothing shows until it is.
* claim: #8315
* fix(control-server): release the watch seat deterministically (#8315)
The seat a subscribed connection holds on the DesignWatch lived in the
events thread alone, so it left the watch only when that thread ended:
on the first write a closed client refuses, which needs the design to
move, or when the thread noticed the end of the stream, which waits for
the connection's own thread to come out of its blocking read. A closed
connection could therefore keep counting on the watch for a second or
more, and the test that asks for the count to be gone could lose its
race with the ten seconds it waits.
The seat is now shared: it leaves the watch once, whichever thread sees
the connection's end first, the connection's own thread at the end of
the stream or the events thread at the first refused write. The count
of subscribed connections is right as soon as the connection is gone,
and the events a subscribed connection is told of do not change.
* claim: #6262
* Inspect Sketch: the ribbon button opens a Sketch report window (#6262)
sketch.inspect gets a place on the Sketch tab (Inspect panel). ui.start opens a
read-only Sketch report: solver status, degrees of freedom left, counts of
curves, points, constraints and profiles, the curves and points that are not
fully constrained, and Copy JSON. engine.execute still only returns the JSON.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Inspect Sketch: a Sketch Report button on the Sketch tab while a sketch is open (#6262)
SKETCH/INSPECT placed it in the Inspect tab's Surface Quality drop-down, which
is hidden while a sketch is edited. It now has its own Inspect panel on the
Sketch tab, labelled Sketch Report; the test clicks the ribbon button.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch Report: strings in a pending locale file, help for sketch.inspect (#6262)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch Report: the Sketch tab fits at 1366 px; the width test closes the report (#6262)
The panels left of the Finish group keep the gap before it when they are fitted,
so they collapse before they reach it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Frank <plexi32@gmail.com>
Opening a DWG stopped with "design file is not UTF-8 JSON". It is told
apart by its signature, whatever the file is called, and the error points
to STEP or DXF.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #9001
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch: a projected face follows a resize that moves its corners far (#9001)
A sketch on the face of an angle kept its projected face boundary (and
reported "projected face boundary identity is ambiguous") when the wall
thickness changed: the inner corner moved by more than a tenth of the face,
past the nearest-point match, and the same-order fallback refused faces.
Accept the same-order layout when, for every point, the point read in that
order is also clearly the nearest one on the same loop with lines the same
way. Rims and holes, and rotated readings of a rectangle, stay refused.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #9341
* Insert Part: seat a fastener in several holes at once (#9341)
parts.insert / parts.fastener take `at` as one pick or a list of picks.
The part is built once as one component; each hole gets its own
placement and rigid seat joint. All placements are made before any is
seated, since joint.create takes the part's seating point where the
placement is. The insert now runs on a scratch session like
parts.resize, so it is one undo step, and a pick off every body fails
it before anything is made.
The Insert Part dialog's hole input is multi-select ("Holes
(optional)") and OK passes every pick.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Insert Part: picks on one hole's rim seat one part (#9341)
A hole's rim is drawn as two half-circle edges, and one click on it
picks both, so four clicks gave eight picks and two screws per hole.
Picks whose wall point lies level on the same hole axis, on the same
body placement, are one hole.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
A comma separates arguments only directly in a function call, so plain
parentheses take a decimal comma like the rest of an expression:
`-(width-0,5)/2`. The UI no longer rewrites commas before wrapping a value
in the design unit's parentheses.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #9297
* Assistant on Windows: find Node in its install folder when the launcher's PATH lacks it (#9297)
An npm-installed agent starts on Windows through its .cmd shim, which runs `node`, with the PATH
search_dirs makes: SolidCraft's own PATH, then npm's and other installers' folders. Node's own
install folder was not among them, so started from a launcher that leaves it off the PATH (the
ArtCraft launcher), the agent stopped with "'node' is not recognized". The search now also takes
%ProgramFiles%\nodejs, %ProgramFiles(x86)%\nodejs and nvm-windows' %NVM_SYMLINK%, after
everything it took before. search_dirs reads its variables through search_dirs_in, so a test can
give it a launcher's environment.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* Point clouds: colour and filter by any property of a PLY file, on range tracks (#5151)
A PLY file's other vertex properties are kept as named numbers on the
points (whole-numbered for integer types; a comment `property: 0 name,
1 name` names a label's values). A cloud is coloured by any of them, a
ramp or a palette for whole numbers, and filtered by up to eight, each a
range or a set of named values. The GPU colours and filters the points
itself, so editing a range uploads nothing.
In the Edit Point Cloud card each range is a track, as in an audio
editor: a histogram of the property over what the other filters keep,
stacked by colour, under an overview strip. Ranges are drawn, moved,
snapped to colour changes, typed (Start, End, Length, relative values)
and played (Space or Play). The automatic colour range is exactly the
values the property takes over what the filters keep.
pointcloud.insert and pointcloud.edit take color_by: <property>,
color_range and filters; pointcloud.list reports them with the
properties. docs/point-clouds.md describes it all.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Point clouds: note the saved properties, colouring and filters as a pending format change (#8816)
A design now saves a cloud's per-point columns and their value names, colouring by a property and
filters; an older release must refuse it instead of dropping them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #7971
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Toasts: a close (×) with a countdown ring instead of a Close button (#7971)
Every toast the user may close (the status, the 3D mouse offer and the
stand-up note) has a close (×) at its top right, handle toast:<name>:close,
in place of a Close button; it does what the button did, is a kit-drawn
button with an accessible name and hover text, reachable by Tab, and dead
behind an issue report. Toasts that go by themselves draw a thin ring
around it that shrinks over their time; pointing at the offer or the note
starts their time again, as before, so their ring is full while pointed at.
The 3D mouse offer keeps three buttons, Calibrate…, Use Direct Driver (the
detail in its tooltip) and Never show again, on one row: the corner
toasts are 400 points wide, as the row's estimated widths need 356.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Toasts: lay buttons out at their drawn widths; corner toasts 360 wide (#7971)
The toast's button rows used an estimate of each button's width that ran
about 40 points over the kit button as drawn, which wrapped the 3D mouse
offer's three buttons and left the main action short of the right edge.
Each button's drawn width is now remembered and the rows laid out with it
(the estimate stands in for the one frame before a button was first
drawn), so the main action is flush right; the corner width is the
smallest round one that fits the offer's three buttons in a row: 360.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Toasts: keep the analysis status test whole after rebasing onto main
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>
On Macs patched with OpenCore Legacy Patcher the OS reports a newer
macOS version than the GPU's Metal compiler supports, so wgpu picks a
shader standard the compiler refuses (`invalid value 'metal3.2'`) and
the window never draws.
The variable has to be set before the process starts, and Rust 2024
makes `set_var` unsafe (this crate denies unsafe code), so main()
now re-launches the binary with SYSTEM_VERSION_COMPAT=1 when the OCLP
NVRAM key (OCLP-Version) is present, and exits with the child's status.
A process that already has the variable set never re-launches, so
there is no loop and a user's own value is respected. If the check or
the spawn fails, startup continues normally.
Tested on an OCLP Mac: one re-launch message, graphics render
correctly; with SYSTEM_VERSION_COMPAT=1 set beforehand it does not
re-launch.
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
* Help: add manual update checks and status window
* Test manual update checks and localized results
* Add update check to the native Help menu
* Localize Help update checks in all app languages
* Document manual update checks from Help
* Document ui.help updates control action
* claim: #7535
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Vendor accesskit_macos 0.26.3 as published (#7535)
The published package, unchanged, so the next commit shows the fix as a diff.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* macOS: quitting on a Mac with a Touch Bar no longer crashes (#7535)
eframe's accessibility adapter (accesskit_macos 0.26.3) swaps the window's
view to a subclass of its own. When AppKit's Touch Bar observes the first
responder (nextResponder), key-value observing subclasses the view again.
Dropping the adapter on quit reset the view's class, which threw away KVO's
subclass, so AppKit's removal of its observer raised NSRangeException ("not
registered as an observer") and the app ended with EXC_BREAKPOINT.
accesskit_macos 0.27.2 fixes this (AccessKit/accesskit#835), but egui 0.36
is on accesskit 0.24, so vendor 0.26.3 with 0.27.2's src/subclass.rs and its
tests: the adapter leaves the class alone when KVO subclassed the view.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #8894
Co-authored-by: Cursor <cursoragent@cursor.com>
* Web: load Simplified/Traditional Chinese fonts for zh and zh-TW (#8894)
BIZ UDPGothic only covers Japanese, so Chinese UI showed tofu. Ship locale-glyph Craft UI SC/TC subsets and fetch the matching face on demand.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
* claim: #8980
Hide constraint glyphs when zoomed out (claim).
Co-authored-by: ReDead <ReDead@users.noreply.github.com>
* Sketch: hide constraint glyphs when zoomed out (#8980)
Fixed-size constraint glyphs stayed visible at every zoom and cluttered the
view when far out. Hide them below a sketch-plane scale threshold, with the
old always-on behaviour behind hide_constraint_glyphs_zoomed_out.
Co-authored-by: ReDead <ReDead@users.noreply.github.com>
* i18n: complete-catalog strings for zoom-hide preference (#8980)
Add ar/he/vi translations for prefs.hide_constraint_glyphs_zoomed_out
(and its note) so cargo xtask i18n --check no longer reports 6 gaps in
complete catalogs.
Co-authored-by: ReDead <ReDead@users.noreply.github.com>
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: ReDead <ReDead@users.noreply.github.com>
Native compilers inline every call, and `main` inlined 33 BVH walks: three
sample_path calls, and a shadow walk per light source family. NVIDIA's
compiler (driver 610.88) crashed in nvgpucomp64.dll or ran out of memory.
- main settles the adaptive budget first, then samples at one call site.
- direct_light takes over direct_studio, visible and the fixed-light loop:
one loop over the sources with one shadow-ray call site, drawing random
numbers in the same order.
- next_vertex (a select) replaces (a+1u)%3u: after a BVH walk that index
crashed NVIDIA's compiler within seconds.
main now inlines 5 BVH walks; main_inlines_few_bvh_walks counts them with
naga, so the limit holds without an NVIDIA GPU.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Problem: every state of a saved model wrote the evaluation key of each sketch before it, so a
design of many sketches and many features after them repeated the same map over and over. In
the SELENE rover (5,484 features, 2,116 bodies) that was 27 of the 33.5 MB manifest: the
manifest hit its 32 MiB limit, the save thinned the cache (#3220) to 2,140 of 5,484 states, and
every open evaluated the 3,344 left-out features again. Reading the kept states one after
another took a few seconds more.
Solution: as for sheet bodies (#6152), the records keep a list of sketch-key sets, each distinct
set written once, as the keys it sets and drops relative to the set written before it; a state
names its set by place. A thinned model keeps the whole list and counts it once against its
budget. The reader counts every set's keys, and each state's, towards MAX_HELD before it copies
them (a short record can stand for a large set), then builds the states on threads
(kernel::map_on_threads) once the shared records are read. The cache format is now /5: an older
cache is a miss and the design is evaluated.
SELENE's manifest is 15.9 MB with every state kept; opening it evaluates nothing. Parse and
evaluate on opening, release build, median of 5 on a shared machine: 7.36 s -> 1.14 s. The
evaluated bodies are identical (inspect.measure of all 2,116 bodies).
Claude-Session: https://claude.ai/code/session_013WUFq6QFBqmZjxwaKy2J1M
Co-authored-by: Markus Kaltenbrunner <kaltenbrunnermarkus@users.noreply.github.com>
* claim: #5574
* Pick hidden geometry near long clicks in a five-row list (#5574)
* fix: make Select Other configurable and fit its list to the window
---------
Co-authored-by: Kirill Frolov <k.a.frolov@vk.team>
* claim: #7168
* Saved model cache: keep fast opening after an update (#7168)
The model cache was read only by the exact build that wrote it (same app
version and commit), so every update evaluated every design again on its
first open. It is now keyed by the evaluation source instead: a digest of
the committed source the model is computed from (doc and the workspace
crates it is built from, the patched truck crates, locked external
dependencies, workspace build settings without the version, and the
cache reader), with the platform. An update that changed none of that
opens designs from their cache; one that changed any of it evaluates
them again, as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #7944
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Theme: System follows the desktop's dark/light setting on Linux (#7944)
On GNOME (Wayland, or X11 through XWayland) the window toolkit never reports
the desktop's dark or light preference, nor its changes, so Theme: System kept
its colours. The Linux app now reads org.freedesktop.appearance color-scheme
through the XDG Desktop Portal's Settings interface (then GNOME's color-scheme
and gtk-theme when it has no preference) on a thread of its own, follows the
portal's SettingChanged signal, and falls back to gsettings or a slow poll.
The window reads the answer through Services::desktop_dark; the decision from
the raw values is prefs::desktop_prefers_dark. Windows and macOS keep the
window toolkit's theme.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #8429
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Print face: Send to Slicer lays a body on its marked face (#8429)
A body can have one flat print face (Document::print_faces, held by the
certified target a sketch on a face holds). body.print_face {body, point?}
marks or clears it. Send to Slicer, Save Mesh with a slicer and EasyPrint
turn and move the exported copy so the face lies on the bed at Z = 0 with
the centre of mass over the origin; a face that can't be found is a
warning and that body is exported as it lies. Move/Copy and Mirror give a
copy its source's print face. One pending format file; the command
has its own icon (print_face).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Print face: Ctrl+B, the face menu and the Send to Slicer row (#8429)
Ctrl+B and the face context menu mark the selected flat face as its
body's print face, or clear it. Send to Slicer shows the print face of
the one body being sent in a Print face input, takes a flat face picked
in the viewport and clears it when emptied.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Print face: a light tint in the viewport and its strength in Preferences (#8429)
Each visible body's print face is tinted towards the theme's section
colour and outlined, at the strength set under Preferences > General >
Slicer (print_face_highlight, 0 to 1, 20 % by default; 0 hides it). The
user guide describes the print face, and the known limitation after a
shell of a shelled body is listed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #9118
* Agent chat window: an off-screen chat window no longer stalls the main window (#9118)
The chat popped out into its own window is an immediate viewport: eframe draws
and presents it inside the main window's frame. Both surfaces waited for vsync,
and a Wayland compositor sends no frame callbacks to a window it isn't showing,
so the main window stalled behind the chat window's present.
A surface other than the main window's is now configured with Mailbox, else
Immediate, when the configured mode waits for vsync and the surface offers one
(viewport_present_mode in the vendored egui-wgpu). The main window keeps vsync
and still paces both.
* claim: #8718
* Browser: the eye sits in a column at each row's right end (#8718)
Tree rows draw the visibility eye right-aligned at the row's end, clear of
the fold arrow and the radio and of a floating scroll bar; names stop short
of it. Preferences > Interface > Eye next to the fold arrow
(browser_eye_left) keeps the old position. Rows report their control
positions so placement handles and inline rename follow the layout.
* Docs: the browser's eye column and browser_eye_left (#8718)
* Browser: the eye's tip shows by the eye at the row's end (#8718)
* Browser: a gap keeps the fold arrow and the radio apart; the eye clears a floating scroll bar (#8718)
The eye's old slot leaves a 10 pt gap instead of 4 pt, so the radio's hit
area is 7.5 pt from the fold arrow's and History pattern rows step in from
their sketch as far as browser children do. The eye's inset follows the
scroll style: only a floating bar laid over the rows takes room. Rows
publish indent:<label> where their depth puts them.
* Tests: browser depth checks run on the default layout, by indent handles (#8718)
* claim: #8993
* Reverse zoom direction no longer flips 3D mouse zoom (#8993)
The checkbox sits under Wheel and trackpad and now reverses the wheel
(and the navigation bridge's wheel-style steps) only. The 3D mouse cap
zooms by its own per-axis invert switch alone: device_camera no longer
takes a reverse_zoom argument, nor does the test model in Preferences.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #8390
* Sketch modify: pick a whole profile or chain for the shared Entities input (#8390)
- Click inside a closed profile selects its whole boundary (and its holes);
double-click a curve selects its whole chain, for Mirror, Move/Copy, Scale
and the patterns. Single curves and points keep working as before.
- The mirror line may itself be among the entities: the command leaves the
line in place and mirrors the rest, instead of rejecting it.
* Mirror: the shared line case is valid, not an error (#8390)
Update the half-filled-window test: a mirror line that is also among the
entities is now accepted (the command leaves it in place and mirrors the
rest), matching the engine.
---------
Co-authored-by: Dane Doubell <2853036+danedoubell@users.noreply.github.com>
* claim: #8908
Co-authored-by: Cursor <cursoragent@cursor.com>
* Update Cancel clears the UI and stops before checksum downloads.
Drop the install job as soon as Cancel is pressed so Downloading ends
immediately, and check the cancel flag around feed refresh and
checksum GETs so a stall before the package body no longer ignores it.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
* Assistant composer: the input box fills its area, and the hint stays on one line
The composer footer inset the box by its own margin on top of the box's text
padding. The footer now has no margin, so the box spans it on all four sides and
only the text padding remains. The hint is painted on a single row, cut with an
ellipsis when the panel is narrow.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Assistant composer: the hint takes its colour from the theme, not a literal
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Ribbon drop-downs as a grid of large icons, as an option (#4739)
Preferences > Interface gets two settings, both off by default:
- Menus: List | Large icons. With Large icons a panel's drop-down shows its
commands as rows of up to five icons at 32 px (twice the list's), keeping
the list's group headings and order. The name, shortcut and help are in
the tooltip, as on a ribbon button. Arrow keys move across and between
rows; a click starts the command; right-click and drag pin it as before.
Resting on an icon for two seconds arms a pin in its corner; a quicker
click anywhere starts the command, so nothing is pinned by accident. A
pinned command shows its pin.
- "Leave pinned commands out of the drop-down menus": a drop-down (list or
grid) leaves out the commands the ribbon shows as buttons; a panel too
narrow for its buttons lists them again, and a menu is never left empty.
No icon artwork or colours change. Sketch > Create keeps its family list.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch > Create in the large-icon grid: every variant under its family (#4739)
With Menus: Large icons, Sketch > Create showed its family sub-menus as
plain text rows (Line, Rectangle, Circle, ...), unlike every other menu. It
now shows every variant as a large icon under its family's heading, then the
other tools under "More". The list keeps its family sub-menus.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Large-icon menus: groups side by side, no footer (#4739)
A menu of two or more headed groups (Sketch > Create's families, Model >
Modify's Edges, Faces, Bodies, Specialise) puts the groups side by side, each
heading over a column of its icons, at most two across. Menus without groups,
or one too wide for the window, keep rows of up to five. The pinning note
under the large-icon menus goes: the tooltips and right-click menus say it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Large-icon menus: a separator line between groups side by side (#4739)
As between the ribbon's sections: a 1 px line in the theme's border colour.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Large-icon menus: at most three rows, growing to the right (#4739)
A menu fills five columns, more when that would take over three rows (Model >
Create: 7 x 3); in grouped menus a group too big for three rows gets more
columns. Only a window too narrow for that wraps into more rows. A test checks
every panel's menu of every tab.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Large-icon menus: group headings centred over their icons (#4739)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Drop-down group headings: smaller than the drop-down's header, not in capitals (#4739)
Edges, Faces, Bodies (and Sketch > Create's families) were drawn in capitals
at font_small, which reads larger than the drop-down's own header
(font_caption). They now use font_micro, written as named, in the list and the
large-icon grid. Review feedback on #5012.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Large-icon menus: start a command from the grid at its icon, as main's rows do (#4739)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Ribbon icon grid strings: a pending locale file, with ar, he and vi (#4739)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Large-icon menus: commands in no group stand apart, so a left-out group doesn't move them (#4739)
With Convert to Sheet Metal pinned and left out of Model > Modify, its
Specialise group went and Delete, in no group, moved under Bodies.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #9332
* Expressions: hyperbolic functions sinh, cosh, tanh and their inverses (#9332)
`sinh`, `cosh`, `tanh` and `asinh` take a unit-less number and give one,
as `exp` does; `acosh` refuses x < 1 and `atanh` refuses |x| >= 1, worded
as `asin` and `ln` refuse what lies outside their domain. They are in the
expression help (`FUNCTIONS`, the parameter autocomplete), the
hostile-expression fuzz tokens, the parameters and equation-curve guides,
and the checking skill's list of functions. Horn flares (Salmon's hypex
`cosh h + T sinh h`), catenaries and the tractrix can now be written as
they are stated instead of expanded into `exp`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #6896
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013WUFq6QFBqmZjxwaKy2J1M
* Sketch: find profiles island by island and say when the search is cut short (#6896)
Every live drag frame ran the profile search over the whole sketch: every
spline piece of a large imported logo was sampled, checked for crossings
and traced again, about 100 ms per frame at 6,000 degrees of freedom.
Profile curves are now split into islands whose enlarged piece boxes do
not touch (the margin exceeds every tolerance the search uses), each
island's loops are found on their own and cached by a fingerprint of its
geometry, and the loops are nested into profiles over the whole sketch in
the order a single search finds them, so profiles, ids, holes and
whole-boundary certificates stay the same. The end-near-crossing lookup
covers its whole reach, independent of the grid's cell size.
The 50,000-piece limit now holds per island, and a sketch that still
exceeds it gets a warning instead of silently missing profiles.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013WUFq6QFBqmZjxwaKy2J1M
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
`schema::issues(id, params)` looks up the command's typed contract and
lists what is wrong with the parameters (none without a contract or for
an unknown id). The batch preflight, the MCP bridge's pre-send check and
the app's check of a design-switching command each did that lookup
themselves; they now call it. No behaviour change.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
sketch_consumed knew Extrude, Revolve and the sheet metal flanges but not
a Hole drilled at a sketch's points, so that sketch stayed drawn inside the
holes. A Multiple hole now counts as using its sketch; Edit Feature shows
its points while the dialog is open. Single holes keep their sketch shown,
since a hidden sketch offers no points to snap to.
* claim: #9299
* Recover unsaved design?: Discard all, with a confirmation (#9299)
A Discard all button at the left of the window's footer, away from Later.
It asks first ("Discard 7 autosaves? This cannot be undone.", Discard all /
Cancel), then discards every listed autosave the way its own Discard button
does (doc.recovery_discard on each id). Cancel goes back to the list
unchanged. The button publishes the handle recovery:discard_all.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #9265
Co-Authored-By: Claude Code <noreply@anthropic.com>
* Preferences: a setting in a row with no label is found by the words it shows (#9265)
A search hid a row by its label before its controls were drawn. Seven checkbox rows and two segmented rows on the Interface page have no label, so "status bar", "search results" and "reveal on hover" found nothing. A hidden row with no label now keeps the words of its controls, as a dropdown keeps its value, and is drawn when they match. Each such row has a key of its own, so its neighbours are not drawn with it.
Co-Authored-By: Claude Code <noreply@anthropic.com>
---------
Co-authored-by: Claude Code <noreply@anthropic.com>
* claim: #9195
* Viewport: an opened design is framed at the home view, in the middle of the view (#9195)
A fit the viewport cannot frame yet is kept and asked again on the next frame instead
of being consumed and lost, so a design opened from the Home screen or by launching on
a file no longer comes up at the default camera, wherever its own coordinates put it.
Framing then centres the model on the view rather than on the free rectangle between
the floating controls: `Layout` carries `middle`, the view without the panels, computed
before the compass, the navigation bar and the OK/Cancel corner join the obstacles. The
model is still sized to `drawable`, so it stays clear of every control, but only a panel
moves it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* Viewport: keep the centred fit clear of the controls, and frame --sample (#9195)
The fit framed the model into a rectangle the size of `drawable` centred on `middle`, which is
not inside `drawable`, so the model could sit under the compass, the navigation bar or the
render product frame. The model is now sized to `drawable` as before, and only the room left
over in it moves the model towards the middle of the view.
`s27_move_triad` turns the Z ring on its far side: the model now sits further right, and the
Move dialog that docks on the right afterwards covered the ring's old grab point.
A launch with `--sample` builds the design straight into the session and never asked for a
fit; it now comes up at the home view, framed, like an opened design.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Viewport tests: the cursor orbit's pivot keeps the pixel it was picked on (#9195)
With the model now centred on the view, one of the test's perspective drag points lands within
the pick tolerance of the box's top edge, and the pick snaps the pivot onto that edge, a few
pixels from the cursor. The orbit keeps that point still, as it should; the test compared it
with the cursor instead of with where it started.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Both commands need a choice, but start_with_variant_memory only special-
cased the parameterless display toggles, so a click on the ribbon (or
command search, a shortcut, ui.start) ran them with {} and showed only
the engine's "style must be ..." / "tool must be ..." error.
They now open a short menu, the way Units does: under the ribbon row
that started it (a second click closes it), or in the middle of the view.
Visual style lists the four styles with the current one checked; the
Navigation tool lists Orbit, Pan and Zoom, and picking the latched one
releases it, as its navigation-bar button does. The four styles come
from one list shared with the navigation bar's menu.
A sketch that failed (a dimension set negative by a parameter) left every
feature built on it with "unknown sketch 1 (it must come earlier in the
timeline)", which points at the timeline order. The evaluation now notes
the features that failed earlier in the pass, and a feature whose input
sketch is one of them fails with "sketch `Sketch1` failed: <its error>".
That result is never cached, since the cache key holds neither the
sketch's name nor its error: a second failing value or a rename shows
up at once.
A page window (Preferences) filled its frame with
dialog_body.gamma_multiply(0.97), which also scales the alpha, so the
ribbon's labels showed through its title bar, search field and heading.
It now uses the opaque dialog_body, as every other window does; the
navigation bar keeps its own translucent resting fill.
* i18n(uk): add pending translations for missing strings
* i18n(uk): add pending translations for new strings from upstream
* i18n(uk): improve pending translations
* i18n(uk): professional translation of all pending strings
* i18n(uk): finish the pending translations for 29 October
Every value in uk-sync-29oct.json is now Ukrainian except the seven symbols
and formulas that must stay unchanged: the modifier keys, HDR and the three
inertia tensors.
Terminology follows uk.json: spot light is "прожектор", directional light is
"спрямоване", body is "тіло", render is "рендеринг". Camera orientation keeps
тангаж, рискання and крен.
Removes history-origin-bodies.json, which was empty after its content moved
into this file.
- worst_status searched the history and the results for each feature of a folded folder, the square of the history's length every frame
- A folder of 16 features or more now finds places and results in tables built once, first of an id winning as feature_index and Model::result do
- Frame on a 4,880-body design: 20.4 ms -> 7.9 ms (M4 Max, release, headless)
- Test a_folders_mark_is_the_worst_of_its_features_own
Co-authored-by: P'tite Tête <asso@ptitetete.com>
A new user guide page, Equation curves, under Sketching: the first curve step by step, Explicit and
Parametric explained with tables of rules to type (a line, a parabola, a wave, a circle, a spiral),
radians, parameters with the involute of a base circle, building a part (a closed flower curve
extruded), editing, and common mistakes. The technical details stay in a short section for scripts
and agents.
Seven pictures, rendered by `cargo xtask book` from new scripts in docs/book/scripts/equation-*.json
(gitignored output, attributed in attribution/pending/docs-equation-curves.md). The Sketching page
keeps a short summary and links to the new page.
* claim: #9184
* Control channel: a request's own param_check level, applied and confirmed by the app (#9184)
`engine.execute`, `engine.batch` and `engine.script` take an optional
`param_check: "off"|"warn"|"strict"` in their params. The app runs the
request at that level through the same `run_checked` path and batch
preflight a headless session uses, puts its own level back, and every
reply says the level it ran at as `param_check` beside `ok`. A command
that switches designs keeps the app's level; any other word is refused.
Readings and exports answered on a copy off the window thread keep the
level too.
`control.capabilities`, answered at once on the control-server thread,
lists what the channel does beyond the methods every version has:
`{"param_check": ["off", "warn", "strict"]}`. An older app answers
"unknown method".
The MCP bridge asks it once per connection. When the app lists the
level, every call and batch goes with it and the app checks against its
own commands; otherwise the bridge checks before sending, as #9073 does.
At strict, a reply that doesn't say the app checked the call is an error,
and the next calls are checked by the bridge. Also: `add_param_warnings`
takes the reply by `&mut`, and `with_param_check` sits beside
`Remote::new`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Control channel: a design-switching command is checked at the request's level
Review fixes for #9184. `component.edit_in_place`, `component.finish_edit`,
`view.next_tab` and `view.previous_tab` ran at the app's own level and
echoed `off`, so a misspelt call went through at strict. They now have
their parameters checked at the request's level before they run (and
still run at the app's own level, as each design keeps its own), and the
reply echoes the requested level. The MCP bridge at strict now requires
the echo to match its level, not just to be there. `param_check` is read
only by `engine.execute`, `engine.batch` and `engine.script`; other
methods ignore it. The off-thread test now shows the design change
computing off the window thread before it is refused.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch corners: a round that would use up a whole arc is refused
A quarter arc of radius 5 running into a line along it, rounded with
radius 5, put the round's tangent point at the arc's other end and left
an arc of no length. round_corner now skips a round whose tangent point
falls on either curve's other end; with none left it says that radius
does not fit, as for two lines (found by a local trim fuzzer).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch corners: a round where the curves meet smoothly is refused
A line running on from an arc's end along its tangent (off only by rounding)
has no corner, but the round's tangent points landed within 1e-7 mm of it and
the round came out as an arc of no length (found by a local trim fuzzer). The
corner test's line met its arc this way too and was checking that output; it
now meets the arc at a corner.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #9179
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019FAWPuFoh9qxn2uS2j53QZ
* sketch.inspect: each profile loop names a curve once, not once per segment (#9179)
A profile records one curve id per piece its loop walks, and a conic or
spline is walked as many short pieces, so sketch.inspect listed a single
Bezier curve 50 times in a profile's `curves` and as often in a hole loop.
The reply now names each curve of a loop once, in the order the loop walks
them (solidcraft_sketch::distinct_curves, as delete and profile references
already do). The solved profiles keep their per-piece lists.
The lists are now a profile pick as they stand: a whole_boundaries pick
refuses a curve named twice, and the regression test extrudes the wall
from the reply's lists.
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>
An SVG import leaves a sketch with regions and no body. The marquee (box select) took only the bodies, so the imported geometry was unreachable with the selection tools: a marquee over it selected nothing, and a click on a region did not take it either.
The marquee now takes the regions of the shown sketches when the design has no body. A design that already has a body keeps the old exclusive behaviour: the marquee takes the bodies alone, not the regions of the sketches that are shown. A dialog that asks for edges or faces (the Patch dialog) never takes the regions: they are neither.
* claim: #2829 (construct.* contracts)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Typed contracts for the construction axes, points and planes
The 19 construct.* commands get input contracts and FeatureResult as
their output, with shared types for references, planes, a plane or
face base, an axis line and a construction plane's name, each calling
the reader its handler uses (split out of geo, plane_param, the face
capture and named_axis_line).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #9188
* Preference: keep the view when a sketch opens, Look At faces it on request (#9188)
Preferences > Sketch > "Turn to face a sketch when it opens" (on by default,
so nothing changes unless it is turned off). Off: creating or editing a
sketch leaves the view as it is, a sketch started on a face no longer
re-centres on the picked point, and Look At (the palette button, Ctrl+8)
still turns to face the sketch. The control channel sets it as
`ui.prefs {"sketch_open_look_at": false}`.
Changes a default: no
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #9087
* fix(#9087): ui.home refuses a sample index that names no sample
The control channel passed the sample index straight to open_sample, which drops an index that names no sample and says nothing. The request then replied ok, so an agent could not tell a typo from a sample that had opened.
The index is checked before it reaches open_sample: an index that names no sample is refused with the valid range in the error. A sample that exists still opens, as before.
* claim: #9086 (edge lengths)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Measure: an exact circle's or arc's length agrees with its circumference (#9086)
Measure gave a circle's or an arc's length_mm as the length of the
polyline drawn for it, which runs inside the curve, while the same reply
gave its circumference or arc_length from the exact circle. When the edge
is known exactly (the kernel's circle, or a sketch's own circle or arc),
length_mm is now its radius times its angle. A circle fitted to a mesh
body's polyline keeps the polyline's length.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* claim: #9029
Co-authored-by: Cursor <cursoragent@cursor.com>
* Square sketch axes when looking at a turned plane (#9029)
Look At only yaw-aligned sketches facing world up/down, so a sketch on a rotated component stayed tilted on screen. Roll (or yaw at the poles) so the sketch's x runs right and y is upright.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
* claim: #9108
Co-authored-by: Cursor <cursoragent@cursor.com>
* Canvas calibrate: pick Point A and B with viewport clicks (#9108).
The Calibrate card only had typed coordinates, so users could not mark the image. Clicks on the canvas now fill A then B, with on-image markers and a short hint.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Calibrate canvas: ja/zh/ar/he/vi strings for the click hints
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Persistent naming: a proposal from a second implementation
A design proposal for the persistent-naming failures still open (#4341,
#5198, #5308, #5857, #5858, #5860, #5861), from building persistent naming
in Form Studio: fall back by lineage, never by geometry alone; a suppressed
maker is not a lost reference; names saved as structured one-level values;
ordinals from the feature, not the kernel's face order; split picks resolve
to all pieces; body identity apart from the display name. Documentation
only.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Persistent naming proposal: body identity follows the #5308 plan
Change 6 now agrees with bluecmd's plan on #5308: an id minted from
next_id and stored per feature slot, better than the derived identity
the first version proposed. It adds that a stored id is only as stable
as the slot assignment, which today follows each lump's least face
name, and gives the lineage signature from the #5308 discussion.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #4515
Co-authored-by: Cursor <cursoragent@cursor.com>
* Inspect: keep analysis legends clear of a left History (#4515).
Draft, Accessibility and Curvature legends were painted at the viewport's left edge under a left History sidebar. Place them in the free workspace content instead.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
offscreen_device() asked wgpu for its default adapter and ignored the
SOLIDCRAFT_GPU policy that headless pictures follow, so with
SOLIDCRAFT_GPU=software the ui-egui offscreen tests still opened the
hardware GPU. Under hardware or software the adapter now comes from
solidcraft_gpu::select (new pick_among, which pick uses too); under cpu
there is none; unset (auto) keeps wgpu's default adapter as before.
Part of #9040.
Claude-Session: https://claude.ai/code/session_013WUFq6QFBqmZjxwaKy2J1M
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #9147
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch: the Sketch Grid box in the Sketch panel turns the sketch's grid off (#9147)
The box sets the open sketch's hide_grid, but the viewport drew the grid
whenever a sketch was open, so hide_grid only reached the ground grid and
the box did nothing. An open sketch now shows its grid whatever the Grid
switch says, unless its own Sketch Grid option is off; the window guides
that renders and the control channel use follow the same rule.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* Release 0.27.1
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Render job test: wait for the cancelled job's reservation to be released
The worker drops a job's reservation just after the job turns terminal, so
checking admitted bytes right after wait_timeout raced (10 of 40 local runs
failed). Poll with a deadline, as dielectric_tests does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #7521
* fix: headless pictures return their draw counts with the picture
`the_renderer_counts_what_it_draws` drew with `Capturer::rgba` and then
read `Capturer::last_stats()`. The capturer is shared with the binary's
other tests, which draw on it from parallel threads, so another picture
could land between the two calls and the test read its counts (e.g. 10224
triangles for the empty design).
`Capturer::rgba_with_stats` returns the picture and its counts, read while
the call still holds the device. `last_stats()` is removed; this test was
its only caller (the performance overlay reads the viewport renderer's own
stats handle).
New test `pictures_drawn_at_once_each_count_their_own` draws empty and box
designs from eight threads on the shared capturer. Against the old pair it
failed 25 of 40 full runs of the `capture` binary; it passes with the new
call.
* fix: a headless picture waits for its read-back's answer
A picture maps its read-back buffer, waits for its submission with
`device.poll(PollType::Wait)`, and then checked once with `try_recv`
whether the map callback had run. wgpu calls a finished mapping's
callback from whichever thread next maintains the device, in a poll or
in any `queue.submit`, after it lets go of the device. When another
thread got there first, the wait returned with the mapping done but its
callback not yet called, and the picture failed with "not supported yet:
reading a picture back without waiting for the browser", the error meant
for a browser.
On the shared capturer, the other thread is another picture's device
check (`probe_lost` submits nothing before it takes the capturer's lock),
so the `capture` tests failed now and then: `bad_picture_sizes_are_errors`
(its last 8 x 8 picture), `pictures_show_a_plain_cut_without_the_hatch`,
`pictures_blend_a_line_that_is_not_opaque`. In the app, the window's
frames are submitted on the device its thumbnails and `ui.render`
pictures are drawn on.
A native picture now waits for the answer, up to the picture timeout; a
browser still answers later, on its own.
New test `pictures_read_back_while_another_thread_uses_the_device`: 2,000
small pictures while another thread polls their device. Without the fix
it failed 19 of 20 runs (1 to 7 pictures each); with it, 150 of 150 runs
pass. `bad_picture_sizes_are_errors` now prints the error it gets.
* Sketch: snap where two recent centre and midpoint guides meet (#8694)
The alignment guide remembered one reference and did not move the cursor. Keep the previous centre or midpoint as well, and snap a drawing point to where their guides cross.
* Sketch: keep the recent-guide crossing snap off until asked (#8694)
The crossing snap was on whenever alignment guides were. Give it its own Sketch preference, off by default, so the guides stay visual until someone turns the snap on.
* Sketch recent snap: ar/he/vi strings and clippy borrow fixes
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: #9072
* MCP: --param-check checks calls to the running app (#9072)
`mcp --connect` (and `mcp` once it bridges to an app) took
`--param-check` and forwarded every call unchecked, while the app's own
session is always off. `Remote` now checks each `engine.execute` and
`engine.batch` against this server's command contracts before sending:
`strict` refuses a call, or a batch with such a step, without sending
anything, and `warn` adds `param_warnings` to the app's reply (each
step's result in a batch). A command with no contract here goes to the
app unchecked. `Auto` carries the headless session's level over when it
bridges to a late app. `batch_preflight::contracts` is the contract half
of the existing preflight, for a runner in another process.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Fold the pending design format changes (sketch-equation-curves, timeline-joint-folders) into format 44.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
theme::apply always sets egui's theme to light or dark, and egui's own window
sync then told the native window to be light or dark. A macOS window told so
keeps that appearance and no longer reports the system appearance changing,
so with Theme set to System the app stayed light after macOS turned dark. The
app now syncs the window itself: the system's appearance while the theme
follows it, the theme's own otherwise (for the title bar).
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #9025
Co-Authored-By: Claude Code <noreply@anthropic.com>
* Sketch: an old edge-on projection keeps its line when it is re-resolved (#9025)
A face seen edge-on was saved as two lines on the same two points, one each way, and is one line now. Re-resolving the link dropped both lines and built a new one under a new id, so an offset that names the first line as its source failed with "an offset source vanished or changed shape". Keep the first line and drop only the second.
Co-Authored-By: Claude Code <noreply@anthropic.com>
---------
Co-authored-by: Claude Code <noreply@anthropic.com>
The check for a constraint that shrinks a curve to a point took under 1e-6
for a point. One solve could squeeze a 49 mm side to about 1.2e-6 mm and
the next take it the rest of the way, so neither solve tripped it (two
rectangles' corners made coincident, found by a local sketch fuzzer). Under
1e-5 now counts: in 4000 random sketches the constraints that still
collapsed a line went from 9 to 0, and every refusal rate stayed the same.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #9015
Co-authored-by: Cursor <cursoragent@cursor.com>
* Evaluate bare length offsets in the design's units (#9015)
Feature expressions like baseLength - 1 were subtracting millimetres because val() parsed with mm defaults and only applied design units to a final unit-less result.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
* claim: #6921
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* CLI: run refuses unknown options and takes --param-check (#6921)
`solidcraft-cli run` now checks its options through `known_args`, so a
mistyped option fails instead of being ignored, and takes
`--param-check off|warn|strict` with the MCP server's words and default
`off`. `warn` prints each unknown key with a suggestion on stderr and
runs the step; `strict` stops at that step and writes nothing.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* CLI: run's option check takes --param-check; warn on timed runs as steps complete
`run` keeps #6919's (#6918) unknown-option check, with `--param-check`
among the options that take a value. `Session::run_script_timed` now
takes the same per-step callback as `run_script_each`, so `run --timings`
or `--profile` at the warn level reports a step's warnings even when a
later step fails, instead of after the script (and not at all on a
failure).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #8644
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Web: open tabs share settings changes, so one tab no longer undoes another's (#8644)
Every open tab of the web app saved its whole copy of the settings to the
same browser storage, every 30 s and on focus loss, so a tab still holding
old settings put them back over a toolbar change made in another tab.
A tab now writes its settings in the frame they change, and the other open
tabs load them from the browser's storage event, so their own saves keep
them. Settings loaded that way aren't written back.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* claim: #9016
Co-authored-by: Cursor <cursoragent@cursor.com>
* UI: keep Distance entry under Preferences and Report a bug (#9016)
Canvas value boxes used Foreground order, so they covered system windows. Draw them as Background chrome instead.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
* Sketch Trim: a cut at a curve's own end leaves no piece of no length
A curve that ends where another one ends crosses it there only within the
intersection's tolerance (1e-6), but bracket dropped cuts at the curve's
ends only within 1e-9 of its parameter range. A line ending on an arc's end,
trimmed from its other end, was cut a hair inside its own end and kept a
piece about 1e-7 mm long (found by a local trim fuzzer: 78 such trims in
4000 sketches, and 8 breaks). bracket now also drops a cut within that
tolerance of the curve's ends, as add_cut merges cuts that close together.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch Extend: a touch at an arc's growing end is that end, not a stop
An arc whose end is where a line ends that touches the arc's circle there
found the touch a hair beyond the end (within the intersection's tolerance),
stopped on it, and added an arc from that point to itself. A hit within that
tolerance of the growing end (1e-6, as for the far end) is now the end.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
A constraint whose only solution squeezes curves to a point (a rectangle's
corner made the midpoint of its opposite side, or two opposite corners made
coincident) collapsed the whole rectangle while the sketch said solved. A
solve that shrinks a line or circle from over 1e-3 to under 1e-6 now counts
as one that fails, as one that throws points far away does: a strict edit is
refused, another keeps the sketch from before its solve, and the free solve
after the held ones in apply_in_order refuses the constraint.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- Skip drawing and interaction for history chips outside the strip's visible part, after their slot is recorded
- Keep a dragged or focused chip, and every chip while a popup is open
- Frame on a 4,880-body design: 20.3 ms -> 14.6 ms (M4 Max, release, headless)
- Test a_long_timeline_paints_only_the_chips_in_view
Co-authored-by: P'tite Tête <asso@ptitetete.com>
On a Windows account whose %TEMP% is a short (8.3) path, such as
C:\Users\MATHEU~1\AppData\Local\Temp, three tests failed because they built
their folders from std::env::temp_dir(), and the agent file scope refuses short
names ("not allowed for this agent: short (8.3) names such as `PROGRA~1`"):
- engine: cmd::render::settings_tests::embedded_hdr_capture_rejects_parameters_scope_and_cpu_primaries_without_mutation
- ui-egui: agent_chat::tests::file_actions_wait_for_the_user
- ui-egui: agent_chat::tests::permission_modes_decide_what_asks
They now use vfs::temp_dir(), which spells the folder with long names, as
libraries_tests and motion_tests already do (#1385, #1211).
Checked on Windows 11 x64 with the 8.3 TEMP: the agent_chat and
render::settings tests (113) pass, and clippy -D warnings is clean for both
crates.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #7287
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Command search: show a command's place in the ribbon, also inside section menus (#7287)
Hovering a command result in Search commands switches the ribbon to the command's
tab and marks its button with its tooltip; a command that has no button there
opens its section's drop-down on its row instead. Leaving the results (or closing
the box) goes back to the tab the user was on, so pressing S, typing and Enter
works as before.
Starting a command from the box makes its button or section label blink twice,
and a command started from a section drop-down marks that section's label while
it runs, like a pressed button.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Command search: the ribbon reveal is a preference, off by default (#7287)
Preferences › Interface › Show search results in the ribbon (`ribbon_reveal`) turns on the hover
preview, the blink after a start and the running mark on a drop-down's panel label. Off, the
ribbon behaves as on main.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Pending locale: Arabic, Hebrew, Japanese, Vietnamese and Chinese for the ribbon reveal setting
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>
* claim: #7912
* Sketch: dimension a sketch entity against a model edge
A dimension took sketch entities only, so measuring a sheet-metal body's edge
was refused with "cannot dimension that combination of entities". The
entities now take a model reference too, an {edge: [x,y,z], body?} or a
{vertex: [x,y,z], body?}, which is projected into the sketch first, as the
constraint commands' operands are since #6348, and measured there.
The Dimension tool picks a body's edge or vertex when the sketch has nothing
under the cursor, and reads the pick back into the reference the command
takes.
* style: rustfmt the sketch dimension command
* Cap the entities one call may project
A dimension projects the model references among its entities into the
sketch, one link and one resolve each, and the loop had no cap: a single
request could make the engine resolve thousands of model edges before
the dimension refused the list. The count is now refused before anything
is projected, and the projection loop stops at the same limit the
Project command uses.
* Sketch dimension on a model edge: use the sketch store after the store refactor
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>
The cursor correction tries the constraints' free directions a whole step at
a time and took a step as soon as the cursor's distance fell, by any amount.
Along freedom that can't reach the cursor (the far end of a line held only
by its direction), retracting onto the constraints still moves the cursor by
rounding, so such steps were taken: the end walked away a step at a time, and
a line measured by an angle could shrink to nothing, as in #7635. A step now
has to move the cursor itself by more than the solve's tolerance.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #6237
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Settings profiles: save, switch, export and import input and interface settings (#6237)
A profile keeps the Interface, Navigation and Sketch preferences (mouse mode,
radial menu and gestures with every studio's lists, theme, scale, font, ...),
the language, the keyboard shortcuts, the toolbar, the S box favourites and
the imported theme it uses under a name. Machine-specific settings stay out
(3D mouse, graphics, units, AI, recent files).
- Preferences > General > Settings profiles: choose the active profile to
apply it, Save, Rename, Delete, Export... and Import...; a note shows when
the settings differ from the saved profile; Cancel takes a switch back.
- settings.profile.{list,save,apply,rename,delete,export,import} commands for
the control channel, palette and scripts; the engine checks the parameters,
the app does the rest.
- One `solidcraft-profile/1` file; import checks the format and name, drops
unknown keys and unreadable values, caps sizes, never switches, and never
panics. Each preference is applied through prefs::set, so a value this
computer refuses keeps its current one and is reported.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Glossary: settings profile, and "profile" among the words with more than one meaning (#6237)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Settings profiles: new strings in a pending locale file, not the catalogs (#6237)
New UI strings now go to assets/locales/pending/ (CONTRIBUTING.md), so
this pull request no longer edits the shared catalogs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Preferences snapshot: compare the toolbar, favourites and profile too
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Settings profiles: keep a command's several keys (#6237)
Since a command can have several keys (#6288), the keymap saves a list where it has more than
one. A profile's checks only took a single key, so saving or importing a profile dropped such a
command's shortcuts. They now read the keys as the keymap does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Settings profiles: Arabic, Hebrew and Vietnamese strings (complete catalogs) (#6237)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Tripper <tripper@localhost>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
* claim: #8927
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Dialogs: a pre-selected sketch line lying on a body edge goes in as that edge (#8927)
With a finished sketch shown, a click on a body edge that one of its lines
lies on picks the sketch line. Fillet and Chamfer take edges and no sketch
curves, so the pre-selection found no input to fill: the tool opened with
Edges empty and the selection was dropped, and only a second click with the
tool open (which picks the edge) made it work.
When nothing in the dialog takes a pre-selected sketch curve but an input
takes edges, look for the body edge the curve lies on from end to end
(display polylines, with room for a curved edge's chord sag) and take that
edge instead.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Preferences > Graphics > Viewport gets a Field of view row under Perspective view: the UI kit's
slider in degrees, 15 to 90, dragged or typed and kept to whole degrees, enabled only while
Perspective view is on. The preference is perspective_fov, in radians (default 0.6, about 34°,
so the default view draws exactly as before), and Restore Defaults puts it back.
One place decides the angle the view is drawn with: SolveApp::view_fov(), the preference with
Perspective view on and 0 with it off or while a sketch is open. A saved value outside 15°-90°
reads as the default. projection and fit_bounds read it (fit_bounds' second hard-coded copy of
the 0.6, tan(0.3), now goes through Camera::half_height), and so does the headless fit.
perspective_view() stays, as view_fov() > 0, so #4291 needs no change.
When the angle changes from one perspective angle to another, the camera's distance scales by
tan(old/2) / tan(new/2), with a running view animation's ends, so the target keeps its size and
the slider changes the depth, not the zoom. prefs::follow_view_fov does it once per frame and
after ui.prefs, Cancel and Restore Defaults; a parallel view keeps the last perspective angle,
and while Render is open the camera is Render's own, so the change waits for the design view.
The Render workspace's Perspective check box turns perspective on at the preference's angle,
not π/4; a saved render setup keeps its own. ui.prefs takes perspective_fov and refuses values
outside the range; ui.camera reports the angle the window draws with, and checks a replayed
camera against it, so its reply still replays. Field of view is in en, ar, de, he, ja, ko, vi and zh;
the ui.prefs settings list in docs/control-protocol.md and the guide's tour describe it.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #8865
* parameters.add: a parameter with no unit takes the design's units (#8865)
A new parameter given no unit took "mm" for any length or bare number, so
in an inch design `parameters.add {name: v, expression: "40/2"}` made a
20 mm parameter where the Parameters dialog makes 20 in. The unit a new
parameter infers is now a length in the design's units (mm designs are
unchanged); parameters.change on a new name and parameters.import share
the same inference. Angles and unit-less values keep their units.
A definition typed into a value of no fixed kind (a coupling's pitch,
a joint's value) still reads its bare number as millimetres, as that
field does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #8912
Co-authored-by: Cursor <cursoragent@cursor.com>
* View cube: rapid clicks add full turns from the in-flight target.
Match arrow-key retargeting so a second cube click turns from the glide destination instead of the mid-animation camera (#8912).
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Problem: a design saved before body names were pinned (#6403) is pinned by its first command
that changes it, as part of that command. Undoing that command went back to the unpinned design,
whose fingerprints no evaluated result matched any more, so the first undo after opening
evaluated the whole timeline again: 63-72 s in the SELENE rover (5,484 features) when opened
from the command line or headless (`Session::opened_at`), 0.08 s with this change. `doc.open`
pinned at the end of the command instead, which kept undo fast but left the design with unsaved
changes on opening.
Solution: `Session::opened_at` pins the names as it opens the design, with the names the file's
bodies have (no result changes, so nothing is evaluated), and takes the pinned design as the
saved one: opening is no change.
Claude-Session: https://claude.ai/code/session_013WUFq6QFBqmZjxwaKy2J1M
Co-authored-by: Markus Kaltenbrunner <kaltenbrunnermarkus@users.noreply.github.com>
Favorites, User and Model sections stay lined up when only some lists hold a long name (follow-up to #8551).
Co-authored-by: Cursor <cursoragent@cursor.com>
home.settings and ai.connected_toast follow the English change from Settings to Preferences (التفضيلات, as ui.preferences). Hover is no longer rendered as scrolling in the browser and navigation bar options, the Project options dialog label is fully in Arabic, and the trackpad zoom key help says to hold the key.
* claim: #8875
* Text fields: a right-click menu with Cut, Copy, Paste and Select All (#8875)
Every kit text field (dialog and sketch value fields, the sketch dimension
box, canvas value boxes, names, text areas, passwords, read-only text) now
calls one shared ui_kit::text_menu::menu after drawing its editor. Rows are
disabled when nothing is selected, the text is read-only or a password, or
the clipboard holds no text. The edit marks the response changed and gives
the focus back, so owners that commit on lost focus keep the field; a right
press inside the selection keeps it; Esc closes only the menu; expression
suggestions wait while it is open. New message ui.select_all in a pending
locale file; Cut, Copy and Paste reuse ui.cut, ui.copy and ui.paste.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #8860
* Timeline: delete after the marker counts and deletes the shown features (#8860)
With the bottom timeline filtered to one component, "Delete N Features After
History Marker" took the marker as a position among the shown features but
sliced the design's feature list with it. N counted features of other
components, and confirming deleted them too, including features before the
marker.
The entry now gets the feature's design position, the design's marker and
the shown positions, and deletes the shown features after the marker; the
label counts the same set. The history beside the browser shows every
feature and passes no filter, so it behaves as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Timeline: look up shown positions by binary search, test the entry before the marker (#8860)
The shown positions are ascending, so a binary search finds each one without
walking the list for every feature after the marker. The test now also checks
that a shown feature before the marker does not offer the entry.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
A segmented control measured its options translated but painted them as
the caller passed them, so the many callers that pass English labels
(the Coil's Circular | Square and Inside | Center | Outside, Revolve's
One Side | Two Sides | Symmetric, Shell's direction, ...) showed English
in every language. Only where the row was too narrow and the control
fell back to its dropdown, which translates, did they read right.
Segments now paint, size, describe and tip their options translated;
the handles keep the option as passed.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #7435
* Viewport: the 3D mouse, the bridge and drag-zoom stop at the model, like the wheel (#7435)
The wheel and pinch hold the model point under the cursor, so the view
comes up to the surface and stops there. Every other zoom path passed no
anchor, so the 3D mouse cap, the navigation bridge and drag-zoom zoomed
straight through the surface into the body.
A device has no pointer, so the cap and the bridge now hold the model
point in the middle of the view; drag-zoom holds the point under the
pointer, like the wheel. With "Zoom to the cursor" off the old
zoom-through-the-model behaviour stays, as it does for the wheel.
The orbit half of the issue is a design decision and is untouched.
* Viewport: drag-zoom holds the point where the drag started (#7435)
The zoom anchor followed the moving pointer, so the zoom slid away from
what was pressed on as the drag went on. It now holds the press point,
the way the wheel holds the point under the cursor.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* 3D mouse: turn around the model in the middle of the view (#7435)
The cap and the navigation bridge turned the view around the view's
centre, which after a pan or from far away sits behind the face, so the
part swung away while it turned. They now turn around the model point in
the middle of the view (the middle of the bodies in view over empty
space), and keep it still on screen. Preferences > Navigation > "Turn
around the model in the middle of the view" switches back to the old
behaviour.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* 3D mouse: keep the turning point until the cap is back at rest (#7435)
The point the cap turns around was picked again every frame, so it slid
to whatever came into the middle as the view moved. It's now chosen when
the cap leaves rest and kept until the cap comes back, the way a mouse
orbit keeps the point it started on.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* 3D mouse: show the point the view turns around (#7435)
While the cap turns the view, a faint ball in the accent colour marks the
model point it turns around, so it's clear what the turn holds still. The
point is now picked when the cap starts turning, not when it only pans or
zooms, so a pan shows no ball.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* 3D mouse: orbit is a choice of Traced or Classic (#7435)
The checkbox becomes a two-way choice on the 3D mouse section's form:
Traced turns around the model in the middle of the view when the turn
begins and marks it with a faint ball; Classic turns around the view's
centre as before. Traced stays the default.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Docs: the 3D mouse's Traced and Classic orbit (#7435)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Make model zoom and traced orbit opt-in (#7435)
---------
Co-authored-by: nomadsgalaxy <16734355+nomadsgalaxy@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #8899
Co-authored-by: ReDead <ReDead@users.noreply.github.com>
* Sketch: keep Diameter box when placing a second concentric circle (#8899)
A centre-point circle's press starts drag-to-draw; releasing on the same
centre (or close to the press) must keep the stage-1 Diameter field instead
of resetting it and risking a second on-click that finishes the shape.
Co-authored-by: ReDead <ReDead@users.noreply.github.com>
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: ReDead <ReDead@users.noreply.github.com>
* claim: #8352
* Sketch: equation-driven curve, a fit point spline from x(t) and y(t) formulas (#8352)
sketch.equation_curve draws a curve from formulas in the expression
language and the design's parameters: x(t) and y(t) over a range of t, or
y = f(x) over a range of x. The curve is a cubic fit spline through fixed
points sampled from the formulas, as many as keep it within a tolerance
(0.01 mm by default, at most 500 points); a curve that ends where it starts
closes smoothly. The sketch keeps the formulas (one new design-format
file), places the points again before every solve so they follow the
parameters, and sketch.equation_curve.edit changes them, keeping the joined
end points. Both commands have input and output schemas.
* Sketch: Equation Curve window, its Edit menu item, labels and guide (#8352)
The ribbon's Equation Curve opens a window for the form (parametric or
explicit), the formulas, the range and the tolerance; OK runs
sketch.equation_curve. Right-clicking an equation curve offers Edit
Equation Curve, which reopens the window on its formulas and runs
sketch.equation_curve.edit. New labels and help in a pending locale file
(with ar, de, he, ja, ko, vi and zh), the icon, the glossary term and the
sketching guide.
* Equation curve: one if for closing the sampled points (clippy collapsible_if)
A held solve applying a constraint, or the edit's own solve, could find a far
solution of the constraints and move points millions of millimetres while
the sketch said solved. Such a result now counts as a failed one: the next
held solve is tried, or the edit is refused.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #7828
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch: hold horizontal/vertical and line up with other points at once while drawing (#7828)
While drawing, the pointer lines up horizontally with the nearest sketch
point level with it and vertically with the nearest one plumb with it, at
the same time, as a dragged point already does; holding H/V with the
previous point still lines up across with another point. Dotted guides run
from the points lined up with (H, V or H+V), and the click adds a
Horizontal or Vertical constraint with each, in the shape's undo step,
unless the sketch already has it.
A click takes the tool out of the app while it runs, so refine_snap
treated every tool as the Line tool then: a rectangle's click could take a
line-extension inference its hover never showed. The tool's points and
kind are kept from the last hover, as the previous point was.
Preferences > Sketch > "Line up with other sketch points while drawing"
(sketch_align_points) turns it off for the old behaviour.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch: lining up with other points is off unless turned on
Review on #8474: sketch_align_points defaults to off, so nothing changes for anyone who
doesn't turn it on. With it off, a click also treats the tool as before (the Line tool's
inferences), instead of keeping the tool's kind from the hover.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #8570
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Insert Design: pick one of the other open designs, or browse for a file (#8570)
With other designs open in tabs, Insert Design first lists them under the
button: one click inserts a saved one as a linked component; one with
unsaved changes inserts its saved file and the status line says so; one
never saved is greyed out, as there is no file to link. Browse... opens
the file picker, as the button did before, and it still goes straight
there with no other design open.
Preferences > Design > Linked designs > "Insert Design lists the designs
open in other tabs first" (insert_lists_open_designs) turns the list off.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Insert Design list: sit with the other menus' items, so run_item keeps its doc comment
Review on #8571: insert_design_items had landed between run_item and its comment. The
rebased menu-walk list keeps main's Target::HistoryControls beside Target::InsertDesign.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #8100
* History: say why an edited feature behind the marker changes nothing (#8100)
Editing a feature that sits after the history marker (double-clicking its
greyed-out item) rolls back to it, previews the change, and on OK puts the
marker back where it was: the feature stays rolled back, so nothing in the
view changes and nothing says why. That is the silent "pattern of a cut
does nothing" in #8100 (the reporter's design has its pattern behind the
marker; on main the pattern itself builds fine once rolled forward).
- timeline.redefine replies `rolled_back` and, when true, a `note`; it also
carries the feature's `warning` (for a rolled-back feature, the one its
constructor met building it at its place).
- The window shows that note in the status line after such an edit.
- A rolled-back item's tooltip in both History layouts says it is after
the history marker and not built.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Pending locale: Arabic, Hebrew, Japanese, Vietnamese and Chinese for the rolled-back notes
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>
* claim: #8639
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch profiles: close the joints near-coincident curves leave open (#8639)
Two free-form curves that lie on each other along a stretch (a DXF that
carries an outline twice, with the same straight sides and different
corners) are cut into sampled pieces whose ends the graph joins into one
node within 1e-4 mm, but the pieces keep their own ends, a few millionths
apart. Only analytic edges were closed at those joints, so the sliver
loops between the two curves stayed open by up to 1e-5 mm, more than the
1e-6 a solid needs. The region around them took those loops as holes:
extruding it failed with "profile loop is not closed" and it drew no
fill when picked, so it looked unselectable.
Close a line's joint in each finished loop: line to line at their middle,
a line onto any other segment's end. On the reporter's DXF the band
between the outer two rings now extrudes (110.5 mm2 by 2 mm = 220.9 mm3),
and the zero-area slivers no longer form profiles (221 -> 216).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #8863
* Sketch: the status line after a dimension is in the interface language (#8863)
The Dimension tool built its "Dimension applied" status sentences (new
parameter, renamed parameter, plain value) and the driven-dimension note
with English format! strings, so they stayed English under a translated UI.
They now come from the catalog (one pending locale file: en, ar, de, he,
ja, ko, pl, vi, zh), with the typed parameter name passed in as is.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #8861
* History: Delete on a selected feature deletes the selection in both timelines (#8861)
With several features selected, Delete in a selected feature's menu deleted
the whole selection in the bottom timeline and only that feature in the
history beside the browser, which offered the selection as a separate
Delete Selected Features… entry.
Both menus now use one Delete entry: the selected features when the
right-clicked one is among them, labelled "Delete N Selected Features",
else that feature alone. The history beside the browser no longer adds its
own Delete Selected Features… to a selected feature's menu, since the entry
above covers it; folder menus keep it. The single-feature delete helper had
no callers left and is removed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* History: a feature's menu leaves out Delete Selected Features… by a flag (#8861)
A right-click on a feature outside the selection selects that feature
first, so in a feature's menu the clicked feature is always selected. A
flag that says the menu is a feature's says the same without the lookup.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #8006
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* Parts library: button head cap screws, ISO 7380-1 (#8006)
Insert Part offered the socket head cap screw but no button head, so a
design using button heads had to stand in a cap screw and accept the
wrong head. A button_head_cap_screw family now sits beside it, taking
the same parameters from the ISO 7380-1 table over M3 to M16.
The head is a new Head::Dome: the shank and the head revolve as one
outline, with the rim at dk, a short cylindrical flank, the arc of the
crown, and a flat top around the socket, so the hex socket stays a plain
cut on a plane. The standard draws the flank and the flat but dimensions
neither; they are 0.15 k and 0.7 s, and the comment on those constants
records what that costs against the standard's crown radius r1 and why
neither other fractions nor r1 taken as given does better. dk, k, s and
t are exact, each checked against three reproductions of the table.
Insert Part, Change Part Size and the bill of materials need no change:
they read the families and their table rows from parts.library.
The wider fastener request is issue 7692; this is only the one family
from it, and leaves the rest of that list open.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* Parts: format after the rebase
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Pending locale: the Button head cap screw family name
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
* Components: copy and paste bodies and components between designs and components (#3390)
Squashed for the rebase onto main.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #3511
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Paste New: copy from a design other than the one pasted into
`component_clone::paste` read the design it copies from through the session it pastes into.
`build` now takes the source (document, evaluated model, unapplied moves) apart from the
target session; Paste New within one design passes the session as both, as before. Names are
numbered against the target's names.
From another design (`Source::copied`, a `ForeignCopy` snapshot taken at copy time):
- names are kept where the target doesn't have them, else numbered;
- the user parameters the copy uses come along, `w_2` where the target has a `w`, with the
copied expressions following;
- a copy whose features use what isn't in it (bodies, sketches, faces, parameters outside it)
is refused with a list of what; `FeatureKind::input_bodies` lists the bodies a feature uses;
- configurations stay with their design, and the target's timeline marker must be at the end;
- a body moved in from outside comes as an imported base named `BodyImport1`.
Body transfers are pointed at the copy's components before they are added: `add_feature`
checks that the components they name exist, which another design's don't.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Engine: paste bodies and components copied in another design
`component.paste_from_design` pastes what `Session::foreign_copy` holds, at the same world
coordinates as in its design, into the active component or `component` (placed by
`component_path`; a component placed more than once gets it in every instance), as one step
to undo:
- bodies as imported bodies, without their history, in one Import feature (named as any
import is, "Import1"); a body seen in another placement ("Cube (B:2)") pastes as "Cube";
names the target has are numbered;
- components as independent copies (Paste New from the snapshot): with their history,
appearances, materials, joints and motion inside them; joints to the outside stay behind.
Tests: bodies (position, naming, target component, undo; a body of another placement) and
components (history, appearances, a part placed twice, editing independently, save and reopen,
undo; user parameters renamed; refused when using a body outside it; bodies moved in).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Clipboard: copy in one design, paste in another
Copying or cutting bodies or components also keeps a snapshot of them (`ForeignCopy`); another
tab pastes it with `component.paste_from_design`: bodies as imported bodies, components as
independent copies (Paste and Paste New alike), folded and selected. Cut pastes as a copy
there, the original staying; Esc on the cut leaves nothing to paste anywhere.
The last copy in any design is what pastes: each copy or cut is numbered, and a tab whose own
copy is older empties its clipboard when it is switched to (`drop_stale`). A failed copy
anywhere leaves nothing to paste in any tab.
Tests: bodies and components pasted in another tab, the last copy winning across tabs, cut
as a copy. `copied_bodies_stay_with_their_design` gave way to the cross-design test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Clipboard: what is selected wins over a design row picked earlier
Copy copied the whole design as a linked component whenever the design's
browser row was still picked, even after a component, body or feature had
been selected in the view or another tab, and failed in an unsaved design.
The design row now gives way to anything selected, and switching tabs
drops the picked components of the design left.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Put the Paste from Another Design label in the pending locale file
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Tests: unground the placement before moving it (a design's first part is grounded)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Drop locale keys main already folded into en.json
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Pending locale: Paste from Another Design in the complete catalogs
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>
* claim: #7409
* Keyboard Shortcuts: the read-only list says so and leads to the editable page (#7409)
Home › Keyboard shortcuts and Help › Keyboard Shortcuts open the
Shortcuts page read-only, in the Preferences window with its sidebar and
Cancel/Apply/OK, so it looked exactly like Settings › Shortcuts with
Import…, Reset All and recording missing. The read-only page now says
"Read-only list: shortcuts can't be changed here." and has an "Edit
shortcuts" button that turns the same page into the editable one.
Strings in assets/locales/pending/shortcuts-read-only-note.json (en, ja,
zh). Harness test opens it both ways, checks the note, clicks Edit
shortcuts, and finds Import…, Reset All and a recordable binding.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Pending locale: Arabic, Hebrew and Vietnamese for the read-only shortcuts note
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>
* claim: #8864
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Viewport: with Orbit, Pan or Zoom lit, a press held still keeps the highlight under it (#8864)
A mode lit in the navigation bar, or a held Space, owns drags, not a still
press (#8438), but the hover pick counted any left press under such a mode
as navigation and dropped the highlight, so the line under a Select Other
hold went dark until the list opened. Only a preset's left-button binding
or Ctrl+left orbit navigates from the press on now; a lit mode's drag
still drops the highlight once it starts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* Sketch Intersect: add Faces/Bodies and Projection Link options (#1110)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Sketch Project: add Geometry/Bodies and Projection Link options (#1110)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Sketch Project/Intersect: keep exact lines and arcs of whole bodies, no noisy rebuild warning (#1110)
Whole-body silhouettes and sections fit lines and arcs tightly and snap arcs
tangent to their neighbours. A rebuilt projection warns only when it removed
the user's own constraints or curves. Pending locales cover ar, he and vi, and
the preselection tests use the direct-pick workflow.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Select Other: label curves from Intersect as intersected (#1110)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* claim: #8692
* Browser: remember the folded card across runs, optional reveal on hover (#8692)
The browser card's fold (the chevron in its header) is now a preference,
saved with the other layout state, so the next run starts the way the user
left it. Preferences > Interface > Layout adds Shown / Reveal on hover: the
browser then stays out of sight until the pointer comes over its place or
the thin tab at its left edge, fading in like the navigation bar's Hide
until hovered, and stays while the pointer is inside or a menu, drag or text
edit started from it is open. It never folds in that mode.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Browser: move the reveal-on-hover messages to a pending locale file (#8692)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #8306
* Timeline: joints in feature folders (#8306)
timeline.group and timeline.move_to_group take joints as well as features, so
hundreds of joints fold into one folder tile. A closed folder shows one tile
whatever it holds; opening it shows its features and joints in both the bottom
and left History. Suppress, delete and ungroup on a folder reach its joints too
(one undo step), deleting a joint removes it from its folder, and an unknown
joint id or name is an error. Saved with the design; designs without joint
folders read and write as before.
* claim: #8862
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Expressions: Enter after `pi mm` keeps the unit, not a longer name (#8862)
A unit after a name or constant multiplies it, as after a number (`pi mm`
is pi millimetres), so it is a complete word there: the longer names are
listed unarmed and Enter submits what was typed. A unit at the start, or
after an operator or bracket, stays a prefix as #3664 chose.
Whether Enter completes is now worked out again every frame, unless a key
armed the list. It was kept from when the list first appeared for a word
and caret position, so it went stale when the text before the word was
replaced, or when the box took the focus before the names were gathered.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* claim: #7023
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Status bar: switch the mouse mapping from a menu (#7023)
The status bar gets a mouse-mapping button beside the units. It shows the
current mapping, and a click opens a menu of the mappings above the bar.
Picking one sets what Preferences > Navigation sets for it (bindings, wheel
direction, middle double-click, right drag orbits) through
choose_nav_preset. Nothing changes while the status bar is off (the
default).
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: #4176
* Preferences: mouse-wheel zoom speed (#4176)
A 0.25x to 4x multiplier for each wheel zoom step, applied in
navigation::handle so every canvas uses it. The 3D mouse keeps its own
speed. Values outside the range are refused when set and reset to 1x when
loaded.
* Control protocol: wheel_zoom_speed in the sorted ui.prefs list (#4176)
* Wheel zoom speed: its label in the complete catalogs (#4176)
* Arabic: fix the Resize Part hint (tanween style, part list, command name)
* Arabic: navigation bar Edge reads as position, tanween spelling in the geometry budget help
* claim: #8040
* wip: 3D line/spline/point tools (#8040)
* Take sketch.move3d off the toolbar help backlog now that it is agent-only (#8040)
---------
Co-authored-by: Dahak Builds <dahakbuilds@users.noreply.github.com>
* claim: #8794
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019FAWPuFoh9qxn2uS2j53QZ
* Control token: a closing window removes only its own token and discovery files (#8794)
Under Flatpak each instance has its own loopback, so a second window
binds the same port number as the first and writes the same
<port>.token, <port>.pid and current.port in the shared data folder.
At exit the first window deleted them all, unconditionally, leaving the
second window running with no token file a client could read.
remove_token_file now takes this launch's token and removes the token
and process files only while the file still holds it; the process id
can't tell instances apart, as every Flatpak instance has the same one.
remove_current_port also takes the token and keeps the discovery file
while another launch's token file holds that port, and removes
current.pid with current.port (it was left behind).
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>
* claim: #8835
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch: a selected text moves when dragged from anywhere inside its frame (#8835)
A drag had to start on one of the text's letters to move it. From the
space between letters, or inside a letter's outline, it found no geometry
and drew a selection box instead.
Off the sketch's own geometry, a drag from inside the selected text's
frame now grabs the text, as one from a letter does. Handles, dimensions
and geometry under the pointer still come first, and outside the frame a
drag from empty space still draws a selection box.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* Trackpad: zoom with two-finger scroll while holding a key, chosen in Preferences (#8322)
Holding the zoom key turns a vertical two-finger scroll (or a wheel) into zoom at
the pointer, with the wheel zoom's speed, direction and anchor, so a laptop user
zooms without pinching. Preferences > Navigation > "Zoom with two-finger scroll
while holding": Cmd on macOS and Ctrl elsewhere (default), Option/Alt, Ctrl, or
Off. That key with the scroll did nothing before, so no default changes; Off
keeps it that way. Shift keeps its orbit.
* Trackpad zoom modifier: off by default (#8322)
Scroll keeps its usual meaning until a key is chosen in Navigation.
* Trackpad zoom modifier tests: Off is the default; tests that zoom choose Cmd (#8322)
* Trackpad zoom modifier: zoom once alongside the Ctrl+wheel pinch from #8539 (#8322)
A wheel the chosen key claims is no longer also read as a pinch, and a key that is not
chosen keeps main's pinch handling unchanged.
Align gets an Object Type row (Bodies | Components). In Components mode it runs the new
occurrence.align command, which moves only the chosen occurrence placements (their
transforms) instead of adding an Align feature to the shared component, so other instances
stay where they are.
- occurrence.align: from/to points, snaps, planar faces and planes in world coordinates,
flip, capture/pending, grounded and rigid-group refusals as occurrence.move.
- A viewport or browser-body pick takes the placement directly under the active component.
- A placement inside a subassembly placed more than once still moves (as occurrence.move
does) but the dialog warns, and the reply lists it under shared.
corner_free looked only in a fixed column at the view's right edge and took
the largest free rect there by area. Next to a tall dialog docked at the
right edge, or the Assistant in the Floating layout, the narrow strip beside
the pane won: the toasts were squeezed into it, their text broke inside
words, and with a wider pane they were not shown at all.
It now tries that column first and then columns ending at each obstacle's
left edge, nearest the corner first, and takes the first free rect that is
TOAST_WIDTH wide and tall enough for a toast, as the frame-rate readout
moves beside a right pane (#6755); failing that, a rect tall enough beats a
wider one too short to show any toast. Without a pane the stack stays
exactly where it was.
* claim: #7688
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Graphics: a background colour of your choice for the 3D view (#7688)
Preferences › Interface › Theme gets a Background row: the theme's
gradient (the default, unchanged) or one colour for the whole application,
filling the view in either theme. The window keeps its own colours, a
picture of the view (`ui.render`) takes the colour too, and `ui.prefs`
sets it as `viewport_background` ([r, g, b] or null).
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>
* feat: add joint-aware assembly Move handles and live motion readouts
* test: improve render job failure diagnostics
* fix: preserve assembly Move compatibility and viewport theme colors
* ci: skip default label sync for fork PRs
* fix: keep Windows CI checks portable after main rebase
* test: cover RTL assembly strings and stable refraction job
* i18n: cover assembly Move in Vietnamese
* i18n: cover assembly Move in French
* fix: complete CGA palette for assembly motion handles
* fix: complete Assembly Move palettes and Turkish strings
* fix: retain assembly move cache answers and toolbar help
* style: format merged assembly Move navigation guard
* fix: align Assembly Move preferences and tests with main
* Fix offline update fixture synchronization and clipped prism intersections
Wait for all v5 manifest requests before deploying v6. Intersect hole-free prism sections over their common height, retaining the established B-rep path for newly clipped perforated sections. Preserve the failing property seed and add closed-form and cap-boundary regressions.
* claim: #8592
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* History: error and warning marks in the browser and on folded folders (#8592)
In the History "In browser" layout, a feature whose build failed gets the
error icon at the end of its row, one that built with a warning the
warning icon, with the message in the tooltip: the marks and text of the
History on the left. A folded History or Features folder carries the
worst mark of what it holds, and so does a folded folder in the History
on the left. Suppressed and rolled-back features are not built and get
none.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- active_body: take every body's owner in one pass (model_body_components) instead of searching the timeline body by body
- Timeline strip: find each chip's result in a table built once per frame (result_index) instead of a linear search per chip
- 4,880-body design: UI frame 31-32 ms -> 7 ms of CPU; tests pin the active body and the result lookup
Co-authored-by: P'tite Tête <asso@ptitetete.com>
* claim: #8095
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Properties: moment of inertia, principal axes and first moments of a part or assembly (#8095)
component.properties and occurrence.properties report each closed body's
inertia tensor about its centre of mass, and in total the tensor about the
total centre of mass (world axes, g·mm²), its principal moments and axes,
the first moments, and with `about: [x, y, z]` the tensor about any point.
The tensor comes from each body's welded export mesh, tessellated once per
body, scaled to the measured mass and turned by each placement; bodies add
by parallel axes. The maths is a new crates/geom inertia module.
The Properties window shows the first moments, the tensor, the principal
moments and axes, and the view marks the centre of mass with the principal
axes while it is open. Inspect → Measure → Properties now opens that window
(it ran the command and showed nothing).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Properties: Copy matrix, and the whole design's card titled just Properties (#8095)
Copy matrix, under the tensor, puts it on the clipboard as three lines of
tab-separated values at full precision, in the units shown. The card for
the whole design is titled Properties; one opened on a component,
placement or bodies keeps its name.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Properties: measure beside the window, so a large assembly doesn't freeze it (#8095)
Properties ran on the window thread: 18 s on a 144-body design, with the
window answering nothing meanwhile (#5203). In the desktop app the card now
measures on a copy of the session on a worker, the way #4614 made agents'
measures work, says "Measuring…" until the reply is in, and Close drops it.
A quick measure still shows at once; headless, tests and the web measure in
place.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The wasm compile ran only by hand, so #8227 (a native-only DroppedFile::bytes() call in
crates/ui-egui) passed PR CI and the merge queue and broke the web build on main (#8534).
A merge group that touches the crates below the apps, the web app, the vendored crates, the
manifests, the toolchain or this workflow now runs `cargo xtask wasm`, and `ci` requires it.
Dependency advisories ran only nightly. A pull request or merge group that changes Cargo.lock
or a Cargo.toml now runs `cargo deny --locked check --warn unmaintained advisories`, and `ci`
requires it then. The nightly keeps the full advisories, bans and sources scan.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #4127
* Render preview: geometry budget from the device, readable refusals (#4127)
The Render preview refused every scene over a fixed 128 MiB of geometry. On
desktop the budget now follows the device's buffer limit (eight of its
largest buffers, at most 2 GiB; 2 GiB with the usual 256 MiB buffers). The
web keeps 128 MiB.
The product upload runs under its own GPU error scopes: an allocation the
device refuses is reported in the preview instead of reaching the device's
uncaptured-error handler, and the half-uploaded batches are dropped. Buffer
uploads map the buffer themselves so a refused buffer fails the upload
rather than panicking in create_buffer_init.
Refusals name the geometry size and the limit and say how to get under it,
on a theme panel card that stays readable on any preview background.
* Render preview: keep the previous geometry budget behind a preference (#4127)
* Render preview: adapt the device budget to main's GPU upload errors (#4127)
* claim: #5717
* ui(feat): let users choose visible toolbar tabs
Keep Sheet Metal and Weldments visible by default while adding persisted tab visibility controls and restoring specialty tabs for designs that use them.
Signed-off-by: Karl Von Richter<karl@embeint.com>
* ui(fix): respect hidden toolbar tabs in every design
Remove feature-based visibility overrides for Sheet Metal and Weldments. Keep all tabs visible by default and preserve the user choice across feature creation, opening designs, selection and undo.
Signed-off-by: Karl Von Richter<karl@embeint.com>
---------
Signed-off-by: Karl Von Richter<karl@embeint.com>
The nightly failed every day from 2026-10-16 to 2026-10-28 without anyone noticing. A new
nightly-alert job (scheduled runs only, the only job with issues: write) opens or comments on
one issue, "Nightly CI is failing" (label ci), when a nightly job fails, and closes it when a
nightly passes.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #7724
* Update now installs the newest release, checked when pressed (#7724)
* Update: close the unmanaged-install test after the rebase (#7724)
* Update: the refresh messages in Arabic, Hebrew and Vietnamese too (#7724)
* claim: #8533
Co-authored-by: Cursor <cursoragent@cursor.com>
* Parameters: show full names in the table and formula popup.
Hover tips carry the full identifier when Name cells or completion rows clip, and the completion list widens for long suggestions (#8533).
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
A new arch job repackages each release tarball as
solidcraft-<version>-linux-<arch>.pkg.tar.zst with makepkg, through the Craft apps' shared
action (storytold/craft-common-actions/actions/arch-package, pinned by commit). The package
is io.tangencylabs.solidcraft, named after the tarball's .desktop file, provides solidcraft
and conflicts with solidcraft-bin/-git. Each package also gets a version-less copy, and
docs/downloads.md links them.
* claim: #8183
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Navigation bar: can sit at the top of the view (#8183)
Preferences > Navigation bar gets an Edge choice, Bottom (default) or Top.
On top the bar sits under the ribbon, its menus open downwards and its
tooltips show below it; the OK/Cancel pair and the axis view move down
below it when they would overlap.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Navigation bar: the edge's labels in the complete catalogs (#8183)
ar, he, ja, vi and zh carry every English message; the Edge row's three
labels were missing from them, so `xtask i18n --check` and
`every_complete_catalog_carries_every_english_message` failed.
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>
body_appearance_key hashed only the name of a body's assigned material, so
after material.edit changed that material's colour (or an undo or redo
changed it back) the key stayed the same and the viewport kept the cached
old look. The key now also hashes the material's resolved look; with an
unchanged material the cache is reused as before.
* Keyboard shortcuts: a command can have several keys
Click a key on the Keyboard Shortcuts page to replace it, or the + after it
to add another; every key runs the command and menus show the first. One
key is still saved as a string, several as a list. Taking a key over from
another command leaves that command its other keys. ui.shortcut takes a
slot and reports every key.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Keyboard shortcuts: follow main's search layout; fix prefs test for key lists
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>
Chamfer at a corner of two lines lying on each other cut both back and
added a line of no length between their ends; lines in line with each
other have no corner either. chamfer now refuses a corner angle of 0 or
180 degrees with Fillet's message, "the lines are parallel" (found by a
local trim fuzzer).
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Resize Part: say what it works on instead of a parameter error; Parts opens the library (#8735)
Resize Part (parts.resize) started with no standard part selected, as the
ribbon starts it, ran the command with no parameters and showed
"`component` must be an id or a name". It now says to select an inserted
standard part, and that Scale resizes a body. Parts (parts.library) only
listed the library, so its button did nothing: it opens the Insert Part
dialog. The German and Dutch button labels said "scale" ("Teil skalieren",
"Onderdeel schalen"), like the Scale command: they now say change size.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Resize Part: the German label matches the command, and the test checks that nothing ran (#8735)
The German ribbon label reads "Bauteilgröße ändern", the name the command,
its dialog and its messages already use. The hint for a missing standard
part is now one function shared by the ribbon start and the Browser's
Change Part Size. The regression test also checks that the design and its
history stay as they were and no message carries the command's own error,
that a scripted parts.library still only lists the library, and that with a
standard part picked Change Part Size opens on it.
---------
Co-authored-by: semmelrinde <124386289+semmelrinde@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
A placement row's menu starts from its component's menu, which takes
Turn Up Axis's enabled state from the component's first placement.
Move/Copy was already set again for the placement right-clicked; Turn Up
Axis and its two turns now are too, so a free second placement can turn
while the first is grounded, and a grounded one can't.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
A trimmed circle ended on a line exactly where an arc started on that
line, held there by a point-on-curve tie. Trimming the line away dropped
the tie and left the two ends on one spot, untied. remove_curves now ties
each point held on a removed curve at one of its ends to that end before
the curve goes (found by a local trim fuzzer, 15 -> 12 cases in 2000
seeds).
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #8480
* Pinch to zoom on touchpad: native Ctrl+wheel pinch zooms the view (#8480)
The viewport's pinch path read only `egui::Event::Zoom`; `immediate_wheel` leaves
wheel events carrying the zoom modifier to egui. Native Linux and Windows
touchpads report a pinch as Ctrl+wheel in points, so the gesture reached neither
path and the view did not move. `pinch_factor` now reads those wheels too, in
every wheel unit and scaled as egui scales them, and `ui.scroll` can send the
same gesture (`ctrl: true`) so scripts and scenarios can replay a pinch.
* fix: finish Ctrl+wheel pinch handling and replay validation (#8480)
* claim: #8561
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Dialogs: disable Apply until something changed (#8561)
The dialog footer gets a disabled state for Apply (Footer::nothing_to_apply,
Window::apply_enabled): drawn with the theme's dim ink, not clickable, not
reached by Tab and reported disabled to accessibility. Preferences enables
Apply only while something differs from its snapshot (what Cancel would put
back), so it is disabled when the dialog opens and again after Apply.
ui.prefs replies with apply_enabled.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #8746
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Move/Copy: changing Move Type keeps the move made so far, and picks follow the moved body (#8746)
Switching the Move Type reset the values, so the move made under the
earlier type was lost. The dialog now keeps it (`chain`) and the new type
moves the bodies on from there; params() composes the two into one
solid.move (one turn about the origin and a translation, as before).
While the new type's picks are missing, the kept move alone is made.
The new type measures from where the bodies now stand (Free Move's pivot,
Rotate's ring, round-edge centres and axes on a moved body). The picker,
the edge/face highlights, the centre marker and Rotate's axis line follow
the moving body, mid-drag too; a pick on it is taken back by the current
type's own move so it doesn't move with the move it sets. While the
triad is dragged the moved bodies' highlights are hidden and rebuilt once
on release.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #8382
* Browser: Show/Hide context menu on origin axis rows (#8382)
Right-clicking an origin axis row (X, Y, Z) now opens its own menu with
Show/Hide (V), through ui.originPart and browser.visibility like the
origin planes' entry. An axis takes no sketch, so Create Sketch is not
offered.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The Display menu's "Perspective while orbiting, ortho on faces" ran past
the menu's edge: a menu row painted its label at full length. It is now
cut before the row's detail and check, and shown whole on hover, as a
long name in a list is.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix(ui): keep selected sketch points highlighted
Add persistent point selection rings while retaining constraint-state markers, with regressions for pointer selection, dialog inputs, aliases and visibility. Fixes#1069.
* Move the sketch point selection tests into the viewport overlay tests (#1069)
Run them through a frame that clears its texture uploads, as the current egui
requires, next to the other overlay tests.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Victor Santos Oliveira <56417388+Vittuu@users.noreply.github.com>
Co-authored-by: Colin Faulkingham <colinfaulkingham@Colins-Mac-mini.local>
Co-authored-by: Cursor <cursoragent@cursor.com>
* Render lights: uniform light list, solo and mute, and the Light editor
* Render lights: Lighting and Light editors to the approved design
* Render lights: the scale-test frame carries the product light list
* Restore the stable Render light mute target on the shared eye control
* fix: validate the viewport BRDF probe with current shader dependencies
* fix: compile legacy lighting controls only for tests
* fix: scope the legacy lighting preset import to tests
* fix: reuse the merged Add Light label and translations
* test: enable the implemented Add Light radial action
* fix: restrict the legacy CPU vector editor to tests
* test: reveal Output before checking Lights browser focus
* test: use the published Browser card rectangle for Output scrolling
* test: reveal Output children before exercising editor navigation
* claim: #8267
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch Create menu: family flyouts open beside their row, on hover (#8267)
A family row (Line, Rectangle, Arc, ...) opened its variants as a dropdown
below the row, covering the rows under it. The flyout now opens to the
right of its row, level with it, and on the left only when the right has
no room. Hovering a row opens it, as the context menu's Appearance
flyout does; resting on another row closes it after the shared delay.
The context menu and the Create menu share the flyout timing
(SUBMENU_CLOSE_DELAY, flyout_hover, flyout_leaving) and placement
(flyout_at) in ui_kit::menu.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
A vertex pick found its named edges and then picked each again by a point
on it, among the edges of every body. At a corner where another body's
edge runs past (a wall against a side wall), all three became that edge,
and the vertex its end 3 mm away. Take each named edge's ends from the
body that has it.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
menu_tests::linked_component_opens_its_part_in_a_tab_and_reports_a_missing_source
gave the background open of the linked part 600 frames. Headless frames
are quick, so on a loaded machine (the whole ui-egui suite at once) the
read can take longer, and the test then found the assembly still shown.
It now waits until the part is open, up to 120 s as Harness::settle and
the edit-in-place tests wait for a load.
Co-authored-by: Claude <noreply@anthropic.com>
* claim: #4160
* Sketch Fillet: 3D sketch lines are not offered, and say they can't be filleted (#4160)
The Fillet tool offered 3D sketch lines as picks, and sketch.fillet then
refused them with "`a` must be a line". The tool no longer offers them, and
the command refuses them with "not supported yet: filleting 3D sketch lines".
* Sketch Fillet and Chamfer: refuse all spatial sketch curves (#4160)
* Sketch corner test: give the picked curve its point (#4160)
main added a point to sketch-curve hits after this branch was written.
* Render shot editor: camera and frame controls, aspect presets, guides and the frame overlay
* Render shot: Shot editor and Scene overview to the approved design
* Distinguish Render stage variants from native window frames
* fix: restore library inspection after clearing shot selection
* test: follow the merged Output Check navigation
* test: inspect Check without requiring a preflight result
* test: cancel Shot removal through its shared dialog action
* fix: remove obsolete Inspector stage form after editor integration
* fix: preserve Appearance Advanced when clearing Wear focus
* test: expect the dedicated Check editor from Scene overview
Saving gave no sign that it worked, while an export names the file it wrote.
After Save or Save As, the status line now reads "Saved <path>".
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The dense solve updated each matrix element through bounds-checked get/add;
on slow scripted drags it was most of the time. The same operations in the
same order on row slices give bit-identical solutions, about twice as fast.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #4359
* Canvas and Decal say where to click after the image is chosen (#4359)
After picking an image the placement tool started silently, so it looked
as if nothing had happened. Show the tool's placement hint as a toast while
it waits for the click (translated), and end it once the image is placed.
* Canvas: include placement hint in viewport obstacles (#4359)
* claim: #8662
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Preferences: typing in a number field edits it instead of searching (#8662)
Type-to-search takes printable keys unless the focused field notes the key
sink. number_field (Autosave every, the Largest file to open limits) never
did, so its digits went into the search.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #4193
* Sketch Mirror: an origin axis can be the mirror line (#4193)
Mirror accepted only a straight curve of the sketch. It now also takes an
origin axis that lies in the sketch plane ({axis: X|Y|Z}), projected into
the sketch as a fixed linked line, so the copies stay symmetric to it and
follow the source when it moves.
* Sketch Mirror: use current sketch store (#4193)
In Preferences, letters typed in a drop-down list's own Search box (the
Font list, a 3D mouse's button assignments) went to the Preferences search
instead, which then filtered the page, usually to "No matching
preferences", and closed the list. The Preferences search takes typing only
when no text field held the keyboard on the previous frame, and the
drop-down's Search box never said it did. On the full Interface page the
number fields' focus check covered for it; once a search hid them, as when
the Font row is found by searching for "font", nothing did.
The Search box in searchable_dropdown_with now reports that it has the
keyboard, as the Preferences search box does.
Co-authored-by: Claude <noreply@anthropic.com>
* Render output: Effects and Check sections, Size presets panel and the Check tool chip
* Render output: Sampling, Effects and Check editors as child rows under Output
* fix: restrict legacy Output controls to test builds
* fix: cover status controls and reveal Effects test targets
* fix: include shared quality reviews in copied check reports
* fix: remove the obsolete Effects GPU guard after Wear integration
* Render appearance: click, drag and paint to assign, Pick Up, copy and paste, and Remove
* Exclude the workspace Shots strip from dialog width checks
* Exclude the collapsed Shots control from dialog width checks
* Retain assembly cache listings and measure actual dialog bounds
* fix: handle toast button rectangles in assignment status
* fix: restrict gallery note measurement helper to tests
* test: measure assignment help at the catalogue footer
* fix: share the command revision snapshot after restacking
* claim: #7445
* Color picker: add shared Hex input mode (#7445)
Accept strict RGB/RGBA Hex input while preserving exact opacity,
opaque/PBR restrictions, invalid drafts and existing U8/F behavior.
Keep explicit English fallback entries for the three new Hex messages
in Arabic and Hebrew, which final integration main now requires as
complete catalogs. No Arabic or Hebrew translations are claimed.
The user authorized this feature-only commit after full cargo xtask ci
stopped at the pre-existing geometry nonminimal_bool lint. The same
failure was reproduced without this patch at the published claim and
main. No unrelated lint fix or suppression is included.
* i18n: cover complete catalogs for Hex picker (#7445)
Add the three Hex message keys to Vietnamese in the feature's pending
locale file. Keep the unchanged English source values as explicit
fallbacks; no Vietnamese translations are claimed.
Audit the complete-catalog set (ar, he, ja, vi, zh) rather than only
the first catalog reported by CI. Preserve the existing completeness
test and all picker/Appearance code.
The completeness regression changes from red to green. Full local CI
and wasm checks remain blocked by failures independently reproduced
in clean upstream main; no unrelated fixes or suppressions are added.
* Appearance: use the shared opacity-aware color picker in Render (#7445)
Route the merged Render Appearance editor through the UI-kit picker,
passing exact opacity only for non-PBR looks. Keep a non-editable mixed
label and treat explicitly accepting the first color as a mixed edit.
Remove the external Hex field and duplicate parser, replace its gallery
entry with the shared mixed-aware picker, and update the control contract.
Preserve existing regression assertions and add actual Render UI tests
for alpha edits, precision, invalid drafts, PBR rejection and mixed edits.
Full local CI and wasm were run before this commit and remain blocked
by the separately reproduced unchanged upstream geometry lints and
prefs.bytes wasm error. No unrelated fixes or suppressions are included.
---------
Co-authored-by: mynoc <298906611+mynoc-96@users.noreply.github.com>
* claim: #8086
* Configuration table: a filter in the Add Column menu (#8086)
The + menu listed every parameter, feature suppression and body
material/appearance with no way to narrow it. A filter field now sits at
the top, takes focus when the menu opens, narrows the entries and the Face
Appearance submenu as typed, picks a lone match on Enter and clears when
the menu closes. A click into the field keeps the menu open (plus_header
closes on outside clicks; entries close it themselves), and the list keeps
its full height after the filter has emptied it once.
New kit element ui_kit::menu_filter (glyph, frameless editor, rule) so the
field does not read as an entry; gallery, i18n guard and docs/ui-kit.md
updated. Regression test in manage_dialogs_tests.
* claim: #6756
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Windows: never load a dxcompiler.dll found on PATH; FXC unless DXC ships beside the app (#6756)
wgpu's Dx12Compiler::Auto loads dxcompiler.dll by name, so Windows takes the
first one on PATH. SolidCraft ships none, and DXC 1.8.2505 from other programs'
folders (Obsidian, NVIDIA SDK Manager) crashed every start with
EXCEPTION_ILLEGAL_INSTRUCTION during the viewport shader compile.
solidcraft_gpu::dx12_compiler() now chooses: WGPU_DX12_COMPILER as wgpu reads
it when it names a compiler; otherwise DXC from the dxcompiler.dll beside the
program, by its full path, else FXC. The window, the headless/CLI instance and
the test helpers start with it, and dx12_compiles_with_fxc follows it.
WGPU_DX12_COMPILER=dxc keeps the old lookup.
Fixes#6756
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch: points tied only through a removed curve's end stay tied
Removing a curve removed its orphaned end points with their constraints, so
two points made coincident with such an end lost their tie although they
still met: two rectangles' corners on one arc end came apart when the arc
was trimmed. They are now tied to each other before the point goes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch: keep the tie test with the other trim tests
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Naming a face swept from a sketch flattened every curve of the sketch to
a polyline and measured its distance to each, for every face. On a sketch
with thousands of curves (a sheet with many cut-outs) that grew with
curves times faces. The curves are now flattened once per feature, each
with its box, and a curve whose box is farther than the best distance so
far is not measured.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Spanish (es-ES): catch up with en.json
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Navigation preferences: name the Shift key through the catalog
Spanish now translates the key name, so the page painted it in English.
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: #7356
* Orbit about the cursor: on an edge or sketch line, turn about the point under the cursor (#7356)
With Orbit around: point under the cursor, a drag that starts on a body
edge turned about the edge's middle, which on a long edge is far from
the cursor, and one that starts on a sketch curve turned about the
centre of the bodies in view. Edge and sketch curve hits now carry the
point under the cursor, which the pick already measured, and
pivot_under uses it. An edge hit's `mid` is unchanged, so selections
still record the edge's middle.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #8139
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019FAWPuFoh9qxn2uS2j53QZ
* 3MF export: a component's bodies as one object with parts (#8139)
file.export and file.save_mesh wrote every body as its own 3MF object and
build item, so a slicer loaded a multi-body part as separate objects and
could arrange them apart on the bed. With objects: "component", each placed
component that holds two or more bodies is written as one object whose
<components> are its bodies' meshes, built once, so PrusaSlicer, OrcaSlicer,
Bambu Studio and Cura load it as one object with a part per body, each of
which can take its own filament. The design's own bodies, and a component
with a single body, stay objects of their own. The default (objects: "body")
writes the same file as before.
- io: model_xml_parts and write_3mf_parts write the parts; export_with_parts
maps body names to them. model_xml_with and export_with_quality give the
same output as before.
- engine: mesh_parts reads `objects` (component is refused for formats
other than 3MF); component_parts groups the placed bodies by placement,
each part named after its occurrence (Bracket:1, Bracket:2).
- file.save_mesh passes the parts on when it also sends the file to a slicer;
file.send_to_slicer's own folder export is unchanged.
Fixes#8139.
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>
* Render wear: a Wear row and editor for the aging layers
* test: read Wear header text from accessibility label values
* test: restore body picks for Wear header recovery assertions
* Render environment editor: rotation, intensity, backdrop colour and ground through render.environment.set
* Render environment editor: rotation ring in the view with one undo step per drag
* Render environment: Source, orientation, backdrop, ground and atmosphere sections
* Keep segmented controls inside their allocated rows
* fix: compile legacy environment controls only for tests
Where several curves cut at one place, trim pinned the new end onto the
first of them. At the touch point of two circles that was a rectangle side
passing through, and once a later trim removed the side the arc ends were
loose: a trimmed dog bone kept two untied ends.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch Coincident: refuse a line's two ends
Making a line's two ends coincident shrinks it to a point; a Parallel or
Perpendicular on it afterwards has no direction to hold and a later solve
could throw points far away while reporting solved, as with a line's own end
as its midpoint (#8562).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch Coincident: keep the own-ends test with the other Coincident tests
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #3693
* Fix timeline playback to show each rebuilt feature
* Move History playback into the Inspect ribbon
* Refuse Play History up front when playback couldn't run
Starting Play and the per-frame check that stops it were looking at
different app state, so Play could be accepted during an apply, a delete
check or queued history work and then stop silently on the next frame.
Both now use one playback_blocked check.
The UI also reads the engine's PlaybackAction as the typed enum instead of
matching strings, so a bad reply is an error rather than a quiet toggle.
* Put Play History in the History controls
History now shows five controls in transport order: start, previous,
play, next, end. Play runs timeline.play, so it turns into Stop while
playback runs, same as the ribbon button and the shortcut.
It's on by default (#3688, #3740, #4216). Anyone who wants the old four
controls back can right-click them and pick Hide Play Button, or clear
history_play_button under Preferences > Interface. In a named vertical
History too narrow for five buttons beside its grip, Play is left to the
ribbon rather than overlapping the grip.
* Draw the History Play button as a plain play/pause glyph
The Play button in the History controls now uses the same drawn glyphs as
its neighbours: a filled triangle, and two bars while History plays. The
triangle is bigger than the step arrows so it doesn't read as Forward.
The ribbon command keeps its timeline_play and timeline_stop icons.
* Keep the playback changes clear of PRs queued ahead
Several PRs in the merge queue touch the same lines, so this moves ours
aside without changing behaviour:
- history_play_button and its reset-test row sit with the other History
preferences, not beside view_confirm_corner (#7248 adds status_bar there).
- History keeps main's history_transport_width() for four controls (#6430
and #7459 call it); the Play button's count uses history_controls_width.
- A new or opened document stops playback after dim_view::cancel(), clear
of the line #7819 adds after reset_chat.
- The Play/Stop label swap moves from interactive_command_info, which #7906
rewrites, into the ribbon's command_info.
* Stop playback after the command completes, clear of #6433#6433 rewrites the session.execute line in execute_command, and our playback check sat right under it, so the queue couldn't merge the two. Checking the revision after command_completed does the same job a couple of lines later.
---------
Co-authored-by: cfaulkingham <colin.faulkingham@gmail.com>
* claim: #7834
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Viewport: vertex markers where edges are drawn (#7834)
Each body's vertices (the ends of its pickable edges, once each) show as
small screen-space squares in the edge colour in the styles that draw
edges: shaded with edges, wireframe, and shaded with hidden edges, where
hidden vertices show faintly like hidden edges. Plain shaded draws none.
A marker is a lifted line from the vertex to itself in the body's block,
so it is instanced with the edges, cached with them, and depth-tested
like a highlight band. The existing vertex hover ring stays on top.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Viewport tests: count edge segments apart from vertex markers (#7834)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Vertex markers: an option, off by default (#7834)
Adds the Vertex markers preference (Preferences > Graphics, off by default) and draws the dots only when it is on.
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>
Open refused a saved model whose text records (a feature's warning, a body or
sketch name) held more than 65,536 bytes, although Save writes them, so such a
design was evaluated again on every open.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Viewport: cut dashed lines to the view, so a huge distance can't fill the memory
epaint makes one shape per dash and steps along a line in f32. The
viewport's dashed lines (the Extrude taper handle, Measure's legs, the
fillet radius guide, the inference and snap guides, a sketch text's
frame) were dashed over their whole projected length. With an Extrude
distance of 1e9 mm the taper line was billions of points long: the
frame never finished and filled the memory within a second.
`viewport::dashed_line` dashes a line that lies in view exactly as
before, and cuts one that reaches far off screen to the view (grown by
two dash periods) first, keeping its dashes in phase with its start.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Viewport: compute a cut dashed line's ends in f64
A line a trillion points long lost its cut ends in f32: they collapsed to one point, and the part in view drew nothing. The new test caught it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Two new commands, view.next_tab and view.previous_tab, with Ctrl+Tab and
Ctrl+Shift+Tab as their default keys: they appear on the Keyboard Shortcuts
page and can be rebound or cleared like any other. The window steps along the
tab strip and goes round at either end; one design alone stays as it is, and
a "save changes?" prompt keeps its tab shown. The engine only echoes the
step (no design edit, no undo step); engine.batch refuses them like the
other steps that go to another design.
Co-authored-by: Phanoob <phanoob@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #5066
* Pattern counts: name a length-unit parameter, refuse fractional counts (#5066)
A count parameter created with a length or angle unit now gets an error
that names it and says to set its Unit to none. A count that evaluates to
a fraction is refused instead of being rounded silently.
A mesh body's row (an imported STL, 3MF, OBJ, ...) now shows a new
mesh_body icon, a triangulated cube in the mesh accent, instead of the
solid body's grey cube. Hovering a body row says Mesh body, Surface body
or Solid body. Body::is_mesh() decides; nothing new is saved.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #7744
* Vendor winit 0.30.13 with Wayland touchpad pinch and rotation gestures
The 0.30 Wayland backend never binds zwp_pointer_gestures_v1, and
WindowEvent::PinchGesture / WindowEvent::RotationGesture only come from the
macOS and iOS backends, so a trackpad pinch produces no input at all on
Linux and Wayland (two-finger scroll still works, since X11/XWayland
reports it as a wheel). egui-winit already maps the events to
egui::Event::Zoom and navigation::handle already zooms the camera from
that, so the Wayland backend was the only missing piece.
Upstream the gesture support is part of winit 0.31 (rust-windowing/winit#4338),
which eframe 0.36 cannot use while it still requires winit 0.30, and the
0.30 backport rust-windowing/winit#4778 was closed unmerged. So ship the
same fix locally, upstream-style-applied and stripped of example and test
targets, with its unit tests for the gesture state machine kept under
cargo test -p winit --lib -- pointer_gesture:
- src/platform_impl/linux/wayland/seat/pointer/pointer_gesture.rs: bind
the global (versions 1..=3), one pinch gesture object per pointer,
destroyed before the pointer is released; begin/update/end become the
two events for the window the gesture began over. Two fingers only;
the protocol's scale is relative to the gesture's beginning, so the
event carries the change since the previous update; rotation is
negated to winit's counterclockwise degrees; a non-finite scale is
dropped, and so are updates of a gesture whose begin we never saw.
- seat state wiring: bind the global, track and drop the gesture object.
- wayland-protocols' unstable feature, platform notes on both events,
changelog, patch-section entry in Cargo.toml, vendor/README, and
Linux/Wayland notes in the troubleshooting guide and the UI tour.
Retire this vendoring once an eframe release consumes winit 0.31 or
later; the cleaned branch is kept as
https://github.com/purp0w/winit/tree/wayland-pinch-gestures.
* ci: retrigger after flaky occurrence-cache test
---------
Co-authored-by: Francisco das Chagas Danta Filho <dev@dantas-filho.me>
Parallel, collinear, equal or concentric of a curve with itself was added
as a constraint that always holds; perpendicular or tangent to itself was
refused as geometry that can't move. These six now refuse one curve
picked twice, as Coincident already does for a point, and leave the
sketch as it was. Constraints already in a design are untouched.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #7919
* Pick by the body's own size, not the camera's distance (#7919)
The occlusion test that decides whether an edge or vertex is hidden by the
face in front allowed a slack of `scene_radius(app) * 0.02`, and
`scene_radius` ends with `.max(app.cam.half_height() * 2.0)`: in an
orthographic view that is `0.016 x camera distance`. Zooming out grew the
slack without bound until a whole body fitted inside it and every edge behind
the front face counted as visible, and edges and vertices are offered before
faces, so they won the pick. Cutting the slack from the scene also gave a
small part in a large assembly a tolerance many times its own size.
`pick_slack` now takes a size and returns the mesh's own chord tolerance with
room to spare, `DisplayQuality::Coarse.chord(size) * 4.0`: a facet sits at
most a chord tolerance inside its surface, and that tolerance is already a
fraction of the body's size, so the slack no longer moves when the camera
does. Each body is tested with its own bounding box, which the loop already
holds. `view.pick` does the same, so an agent's pick still matches a click on
the same pixel. Sketch profiles and origin planes belong to no body and keep
a scene-level value, `scene_pick_slack`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
A line's own end held at its middle shrinks the line to a point. The
solve accepted that, and a later angle, tangent or parallel on the line
had no direction to hold: its points went millions of millimetres away
while the sketch still reported solved. MidPoint now refuses the pick,
saying why, and leaves the sketch as it was. Constraints already in a
design are untouched (only the command refuses).
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #4750
Co-Authored-By: Claude <noreply@anthropic.com>
* Extend: pick two curves to extend both to where they meet (#4750)
`sketch.extend` gains a second form: `curves: [a, b]` names two lines or
arcs, with an optional `at: [[x,y],[x,y]]` naming the end of each to grow
(default: the end nearest the meeting point). Both grow along their own
line or circle to where they cross and the two ends are joined with one
Coincident, in one undo step. `moved` is the joined end's id.
- Where two crossings are possible (line and arc, two arcs) the one with
the least total growth is used; a tie is refused.
- A curve that already reaches the point does not grow. An end held by
other geometry or a constraint stays and gets a new line or arc.
- Parallel or collinear lines, curves that never meet, a point behind the
chosen end, the same curve twice, splines, ellipses, infinite lines,
offset geometry and malformed `curves`/`at` are refused with no change.
- The single-curve form is unchanged; its code is shared through
`grow_line` / `grow_arc`.
Canvas: in the Extend tool, Shift-click two curves. Shift goes through
the tool modifier registry as the new `extend_pair` action, and Extend
turns off Shift's midpoint snap. Escape drops a pending first pick. The
sketching guide describes the gesture.
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* claim: #7893
* Toasts: one stack in the view's bottom-right corner, one width, buttons as in dialogs (#7893)
* Control protocol: toast_place in the sorted ui.prefs list (#7893)
* Toasts: a sketch test clears the status notice before clicking the view's corner (#7893)
The test's line end lands in the bottom-right corner, where notices now stack
and take clicks as they did at the bottom centre; the test is about the circle.
* Toasts: the pattern scenario's circle sits clear of the view's corner (#7893)
After the refit, a circle at y 0 landed in the bottom-right corner, where the
tool's prompt now stacks and takes clicks. The circle moves 40 mm up; the
pattern it checks is the same.
* claim: #8358
* Components: a component named like the design is reachable by name (#8358)
Document::find_component resolved the design's own name to the root component (id 0) before
searching the component list, so a component that shares the design's name was unreachable by
name: appearance.assign reported `no component `CurtainRailPlug`` (the same lookup serves every
command that takes a component by name).
An exact component name (or component id) match now wins over the design-name fallback; "0" and
"root" still always mean the design.
Regression test: a_component_named_like_the_design_takes_its_appearance_by_name (fails on main
with the reported error, passes with the fix).
* claim: #8521
* feat: give History feature folders their own symbol
A folder of features in the History on the left drew the same folder icon
as the Browser's folders. It now draws feature_group, a pile of history
chips with the front one in blue, and so do Add to New Folder and Remove
from Folder in the History's menu. Other uses of folder keep it.
* claim: sheet.* typed parameter schemas
* Sheet metal feature commands: typed parameter contracts
sheet.flange, sheet.perforate, sheet.hem, sheet.fold, sheet.unfold, sheet.refold and
sheet.convert read their parameters by hand, so a misspelt key or a value of the wrong
shape was ignored or failed late, and describe_command had only prose for them.
Each now has a contract in schema/sheet.rs, checked in front of the unchanged handler,
and returns FeatureResult. The values no shared type fits (sheet-edge points, an edge
flange's edges, a fold's line and fixed point, the flange type, the positions, the
relief shape and the perforation pattern) get types that call the handlers' own
readers, split out of cmd/sheet.rs, and give their messages. The rule's values
(thickness, K-factor, bend radius, reliefs) stay out of the contracts: the handlers
refuse them, and strict refuses them as unknown keys.
A test passed a nested `base` object whose `sketch` the flange never reads; it now
names the sketch at the top level.
Part of #2829 (#6581).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sheet metal contracts: a further bend's angle stays as lenient as before
The flange handler reads a `then` bend's `angle` that is no expression
(`true`, a list) as left out and bends it 90 degrees. The rebased
contract had moved that check into the shared reader, so the handler
refused such a call even with the check off.
The reader is back to the handler's behaviour, and `ThenSteps` is the
stricter one, as the schema guide asks: warn reports such an angle and
strict refuses it. Tests cover the reader's leniency, the type's
refusal and the three check levels on one flange.
Part of #2829 (#6581).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Browser: the history in the browser, as a History layout (compact mode)
Settings > Interface > History gains "In browser" (view.history_layout dock: browser):
neither timeline is shown, and each component's History folder lists its sketches and
features in history order with the marker. Dragging the marker rolls the history back
through timeline.roll_to; Find in Timeline reveals the feature in the browser.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Browser history: sketches listed under their feature as in the vertical History
With Sketches: Under their feature, the history in the browser (compact mode) lists each
sketch under the first feature built from it, through the shared history_rows (#6494)
without reference rows: same rule, same fold key, so switching the History layout keeps
what is open. A drop above a row rolls back its listed sketches with it. Selecting a feature
lights the rows of its sketches, and selecting a sketch lights the features built from it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Browser: with the history in the browser, the Sketches folder starts closed
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Browser history: sketch rows have the eye of their Sketches folder row
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* History in the browser: Arabic, Hebrew and Vietnamese for the layout's name
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Dialogs: scroll a long refusal into view with its Create Issue (#8454)
A dialog's refusal comes last in its fields. When the fields fill the
dialog, the refusal sits under the fold: an Extrude Cut that the kernel
refused showed only its first line, "boolean Cut: the operation failed: no",
and no Create Issue. The dialog now scrolls a new refusal into view once:
a refusal on OK at once, a preview's once no field is being typed in, so a
field never moves away while it is typed in.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Dialogs: decide the reveal in one function and test it
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Lists many PRs add to: no written lengths, one file per theme (#7747)
* xtask lists: the fixed-table allowlist in order, each with its own reason
* Lists: the wide-character blocks and the drawing scale presets as slices too (#7747)
* Lists: the drawing units test reads UNITS as a slice (#7747)
A copied body transfer was pasted like any feature and given the target
component as its owner. A transfer belongs to the component it moves bodies
to, so the saved design was refused on open ("body transfer owner must match
its destination"), and a pasted transfer had nothing to move anyway: the
copies go into the component they are pasted into. Paste now leaves body
transfers out and says so in its notices; a clipboard of only transfers is
refused.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #8410
* Preferences: reverse trackpad orbit direction (#8410)
Add "Reverse trackpad orbit direction" under the pan settings in
Preferences > Navigation. orbit_reverse defaults off, so older settings
files load unchanged, and turns the Shift + two-finger orbit the other
way on both axes, as natural scrolling flips both. Pan, zoom and mouse
orbiting are untouched. Labels in en, ar, de, he, ja, ko, vi and zh as
a pending locale file; orbit_reverse in the control protocol's sorted
settings list.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #8542
* CI: report missing translations for complete catalogs before review (#8542)
`cargo xtask i18n --check` already reads the pending locale files and fails
when a complete catalog lacks an English message, but no workflow ran it, so
the gap surfaced only in the full test run after approved-for-ci. It now runs
in Format and repository checks, and CONTRIBUTING names the complete catalogs.
Describe the problem and resulting behavior. Link the issue when applicable.
Start with what this is, in plain words someone new to the project would understand: what it does and who it's for. Don't assume anyone knows the name of your tool, project or feature. Then describe the problem and resulting behavior. Link the issue when applicable.
Changes a default: no
@@ -14,6 +14,8 @@ What did you run, and what were the results? Include expected and measured value
## Screenshots of the behavior (when observable)
Put the picture or video here in the description. A file in the diff doesn't count: a maintainer has to be able to see what this is without opening the changed files.
For observable CAD, UI or model behavior, include actual descriptive screenshots or a short recording from a small reproduction. This includes kernel, geometry, assembly and backend effects that can be shown in the app. Coding agents should run the app and capture the evidence themselves, rather than asking a human for evidence they can produce. Show the actual failure/result, preferably before and after, with brief steps, expected/actual behavior and annotations where helpful.
Flowcharts and Mermaid diagrams cannot substitute for evidence of visible behavior. Use diagrams for architectural reasoning when actual screenshots cannot illustrate the point. Changes with no useful observable behavior may omit screenshot/video evidence. Exclude private designs and sensitive details; attach media or link to an accessible recording rather than committing generated review artifacts.
# Cargo.lock or a Cargo.toml changed on a pull request or in a merge group.
if [ "$DEPS_RUN" = true ]; then
test "$ADVISORIES" = success
fi
# A merge group that reaches the wasm build.
if [ "$WASM_RUN" = true ]; then
test "$WASM" = success
fi
- name:Note a carried result
if:needs.changes.outputs.carried != ''
env:
@@ -1033,9 +1090,13 @@ jobs:
run:|
echo "This push only merges main into a change that already passed; the heavy checks were not repeated. Passing run: $CARRIED" >> "$GITHUB_STEP_SUMMARY"
# Every crate below the apps and the web app must build for the browser (`cargo xtask wasm`, a
# cargo check, no tests). In the merge queue for a group that reaches the wasm build (Select
# checks' `wasm`), where `ci` requires it; by hand with cross_compile.
@@ -84,6 +84,8 @@ Editable drawing code, native projects and shipped exports are collected under `
| docs/book/src/generated/tool-preview.png | SolidCraft contributors | rendered by `cargo xtask book` (`solidcraft-cli snapshot docs/book/scripts/tool-preview.json`; gitignored) | MIT OR Apache-2.0 |
| assets/fonts/liberation-sans/LiberationSans-Regular.ttf | Red Hat, Inc. (Liberation Fonts 2.x; digitized data Google) | https://github.com/liberationfonts (Debian fonts-liberation), unmodified; licence text in assets/fonts/liberation-sans/OFL.txt; used for sketch Text outlines | SIL OFL 1.1 |
| assets/fonts/biz-udp-gothic/BIZUDPGothic-Regular.ttf | The BIZ UDGothic Project Authors | https://github.com/googlefonts/morisawa-biz-ud-gothic (Google Fonts), unmodified; licence text in assets/fonts/biz-udp-gothic/OFL.txt; used for web UI Japanese font | SIL OFL 1.1 |
| assets/fonts/craft-ui-sc/CraftUI-SC.otf | Adobe / Google Inc. (Noto Sans SC); SolidCraft subset | https://github.com/notofonts/noto-cjk Sans/SubsetOTF/SC Regular, glyph-subset for assets/locales/zh.json and renamed Craft UI SC (OFL reserved-name); licence text in assets/fonts/craft-ui-sc/OFL.txt; web UI Simplified Chinese | SIL OFL 1.1 |
| assets/fonts/craft-ui-tc/CraftUI-TC.otf | Adobe / Google Inc. (Noto Sans TC); SolidCraft subset | https://github.com/notofonts/noto-cjk Sans/SubsetOTF/TC Regular, glyph-subset for assets/locales/zh-TW.json and renamed Craft UI TC (OFL reserved-name); licence text in assets/fonts/craft-ui-tc/OFL.txt; web UI Traditional Chinese | SIL OFL 1.1 |
| tests/step-conformance/cone_point.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/cone_point_vertex.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/cylinder.step | step-conformance contributors | generated from formulas by https://github.com/dan335/step-conformance (corpus/generated, 96f30bb); import regression fixture | CC0-1.0 |
@@ -125,6 +127,8 @@ Editable drawing code, native projects and shipped exports are collected under `
| assets/appearances/library.rs | SolidCraft contributors | Original SolidCraft resource data or drawing code, collected without redesign | MIT OR Apache-2.0 |
| assets/appearances/opaque_finishes.rs | SolidCraft contributors | original illustrative opaque finish values; no external dataset, textures or measured engineering data | MIT OR Apache-2.0 |
| assets/fonts/biz-udp-gothic/OFL.txt | The BIZ UDGothic Project Authors | Unmodified accompanying font licence | SIL OFL 1.1 |
| assets/fonts/craft-ui-sc/OFL.txt | Adobe / Google Inc. (Noto CJK); SolidCraft header | Accompanying OFL for CraftUI-SC.otf | SIL OFL 1.1 |
| assets/fonts/craft-ui-tc/OFL.txt | Adobe / Google Inc. (Noto CJK); SolidCraft header | Accompanying OFL for CraftUI-TC.otf | SIL OFL 1.1 |
| 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 |
@@ -185,11 +189,7 @@ Editable drawing code, native projects and shipped exports are collected under `
| assets/shaders/viewport.wgsl | SolidCraft contributors | Original SolidCraft resource data or drawing code, collected without redesign | MIT OR Apache-2.0 |
| assets/styles/ui/palettes.rs | SolidCraft contributors | Original SolidCraft resource data or drawing code, collected without redesign | MIT OR Apache-2.0 |
| assets/styles/ui/themes/gruvbox-dark.json | SolidCraft contributors | original SolidCraft theme file mapping SolidCraft's UI colours to the Gruvbox palette's colour values (Gruvbox by Pavel Pertsev, https://github.com/morhetz/gruvbox, MIT) | MIT OR Apache-2.0 |
| assets/styles/ui/themes/gruvbox-dark-hard.json | SolidCraft contributors | original SolidCraft theme file mapping SolidCraft's UI colours to the Gruvbox palette's colour values (Gruvbox by Pavel Pertsev, https://github.com/morhetz/gruvbox, MIT) | MIT OR Apache-2.0 |
| assets/styles/ui/themes/gruvbox-dark-soft.json | SolidCraft contributors | original SolidCraft theme file mapping SolidCraft's UI colours to the Gruvbox palette's colour values (Gruvbox by Pavel Pertsev, https://github.com/morhetz/gruvbox, MIT) | MIT OR Apache-2.0 |
| assets/styles/ui/themes/gruvbox-light.json | SolidCraft contributors | original SolidCraft theme file mapping SolidCraft's UI colours to the Gruvbox palette's colour values (Gruvbox by Pavel Pertsev, https://github.com/morhetz/gruvbox, MIT) | MIT OR Apache-2.0 |
| assets/styles/ui/themes/gruvbox-light-hard.json | SolidCraft contributors | original SolidCraft theme file mapping SolidCraft's UI colours to the Gruvbox palette's colour values (Gruvbox by Pavel Pertsev, https://github.com/morhetz/gruvbox, MIT) | MIT OR Apache-2.0 |
| assets/styles/ui/themes/gruvbox-light-soft.json | SolidCraft contributors | original SolidCraft theme file mapping SolidCraft's UI colours to the Gruvbox palette's colour values (Gruvbox by Pavel Pertsev, https://github.com/morhetz/gruvbox, MIT) | MIT OR Apache-2.0 |
| assets/styles/ui/themes/solidcraft-95.json | SolidCraft contributors | original SolidCraft theme file | MIT OR Apache-2.0 |
| assets/styles/ui/themes/platinum-9.json | SolidCraft contributors | original SolidCraft theme file | MIT OR Apache-2.0 |
| assets/styles/ui/themes/cupertino.json | SolidCraft contributors | original SolidCraft theme file | MIT OR Apache-2.0 |
@@ -603,6 +603,158 @@ Original SolidCraft drawings, Open for owner review; no runtime assignments.
| 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 |
| assets/locales/pending/point-clouds.json | SolidCraft contributors | Original point-cloud UI strings and translations | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/feature_group.svg | SolidCraft contributors | two-tone modelling icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/mesh_body.svg | SolidCraft contributors | two-tone modelling icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/timeline_play.svg | SolidCraft contributors | Original history playback icon; canonical grey_cube base and original action cue | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/timeline_stop.svg | SolidCraft contributors | Original history playback icon; canonical grey_cube base and original action cue | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/status_pin.svg | SolidCraft contributors | Original analytic pin icon for persistent viewport results | MIT OR Apache-2.0 |
| assets/locales/pending/render-wear.json | SolidCraft contributors | Original UI labels and translations | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/equation_curve.svg | SolidCraft contributors | two-tone modelling icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
| assets/locales/pending/sketch-recent-snap.json | SolidCraft contributors | Original preference messages for the recent centre and midpoint snap | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/construction_line.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/construction_line_horizontal.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/construction_line_vertical.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/construction_line_angle.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/construction_line_parallel.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/construction_line_perpendicular.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/construction_line_tangent_external.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/construction_line_tangent_at.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/construction_line_tangent_two.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/construction_line_bisector.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| docs/book/src/generated/equation-circle.png | SolidCraft contributors | rendered by `cargo xtask book` (`solidcraft-cli snapshot docs/book/scripts/equation-circle.json`; gitignored) | MIT OR Apache-2.0 |
| docs/book/src/generated/equation-flower.png | SolidCraft contributors | rendered by `cargo xtask book` (`solidcraft-cli snapshot docs/book/scripts/equation-flower.json`; gitignored) | MIT OR Apache-2.0 |
| docs/book/src/generated/equation-involute.png | SolidCraft contributors | rendered by `cargo xtask book` (`solidcraft-cli snapshot docs/book/scripts/equation-involute.json`; gitignored) | MIT OR Apache-2.0 |
| docs/book/src/generated/equation-line.png | SolidCraft contributors | rendered by `cargo xtask book` (`solidcraft-cli snapshot docs/book/scripts/equation-line.json`; gitignored) | MIT OR Apache-2.0 |
| docs/book/src/generated/equation-parabola.png | SolidCraft contributors | rendered by `cargo xtask book` (`solidcraft-cli snapshot docs/book/scripts/equation-parabola.json`; gitignored) | MIT OR Apache-2.0 |
| docs/book/src/generated/equation-spiral.png | SolidCraft contributors | rendered by `cargo xtask book` (`solidcraft-cli snapshot docs/book/scripts/equation-spiral.json`; gitignored) | MIT OR Apache-2.0 |
| docs/book/src/generated/equation-wave.png | SolidCraft contributors | rendered by `cargo xtask book` (`solidcraft-cli snapshot docs/book/scripts/equation-wave.json`; gitignored) | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/print_face.svg | SolidCraft contributors | two-tone modelling icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
| assets/locales/fi.json | SolidCraft contributors | Original Finnish translation of SolidCraft's English catalog, AI-assisted (Claude Code) clean-room from the English and not yet reviewed line by line by a native speaker; no commercial CAD text used | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/plane_axis_point.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| docs/design/preferences/figures/aa-2x.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/aa-2x.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/aa-4x.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/aa-4x.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/aa-8x.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/aa-8x.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/aa-off.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/aa-off.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/agent-pointer-speed.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/agent-pointer-speed.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/ao-on.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/ao-on.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/assembly-move.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/assembly-move.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/browser-card.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/browser-card.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/browser-column.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/browser-column.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/colour-vision-axes.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/colour-vision-axes.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/colour-vision-bends.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/colour-vision-bends.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/colour-vision-draft.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/colour-vision-draft.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/colour-vision-failed.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/colour-vision-failed.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/colour-vision-fixed.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/colour-vision-fixed.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/colour-vision-projected.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/colour-vision-projected.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/direct-interaction.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/direct-interaction.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/frame-rate.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/frame-rate.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/history-bottom.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/history-bottom.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/history-in-browser.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/history-in-browser.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/history-left.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/history-left.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/history-under-browser.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/history-under-browser.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/layout-schematic.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/layout-schematic.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/measure-axis-distances.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/measure-axis-distances.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/mouse-mapping.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/mouse-mapping.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/navigator-axes.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/navigator-axes.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/navigator-cube.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/navigator-cube.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/orbit-centre.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/orbit-centre.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/orbit-cursor.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/orbit-cursor.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/orbit-origin.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/orbit-origin.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/orientation-y-up.dark.svg | SolidCraft contributors | original SolidCraft artwork: the Orientation figure copied from the app and extracted from the Preferences redesign board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/orientation-y-up.light.svg | SolidCraft contributors | original SolidCraft artwork: the Orientation figure copied from the app and extracted from the Preferences redesign board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/orientation-z-up.dark.svg | SolidCraft contributors | original SolidCraft artwork: the Orientation figure copied from the app and extracted from the Preferences redesign board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/orientation-z-up.light.svg | SolidCraft contributors | original SolidCraft artwork: the Orientation figure copied from the app and extracted from the Preferences redesign board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/quick-measure.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/quick-measure.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/select-bodies.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/select-bodies.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/select-faces-edges.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/select-faces-edges.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/sketch-conflict.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/sketch-conflict.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/sketch-exit-keep.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/sketch-exit-keep.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/sketch-exit-previous.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/sketch-exit-previous.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/sketch-exit-unless-turned.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/sketch-exit-unless-turned.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/sketch-guides.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/sketch-guides.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/sketch-hide-glyphs.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/sketch-hide-glyphs.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/sketch-state-icons.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/sketch-state-icons.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/style-shaded-edges.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/style-shaded-edges.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/tess-coarse.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/tess-coarse.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/tess-fine.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/tess-fine.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/tess-normal.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/tess-normal.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/tess-ultra.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/tess-ultra.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/theme-dark.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/theme-dark.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/theme-gruvbox.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/theme-gruvbox.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/theme-imported.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/theme-imported.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/theme-light.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/theme-light.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/theme-system-dark-half.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/theme-system-dark-half.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/theme-system-light-half.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/theme-system-light-half.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/tool-cursor.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/tool-cursor.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/tool-extrude.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/tool-extrude.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/tool-hole.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/tool-hole.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/tool-placement.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/tool-placement.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/vertex-markers.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/vertex-markers.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/view-angles.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/view-angles.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/view-grid.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/view-grid.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/view-perspective.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/view-perspective.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/view-shadow.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| docs/design/preferences/figures/view-shadow.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
| `assets/locales/pending/sketch-offset-instances.json` | SolidCraft contributors | Original offset instance-count label and translations | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/settings_profile.svg | SolidCraft contributors | single-colour application/navigation icon master (64 grid), original analytic head and shoulder geometry for settings profiles | MIT OR Apache-2.0 |
## Code adapted from sibling projects
@@ -662,6 +814,7 @@ copied into SolidCraft.
| Directory | Source | Licence | Changes |
|---|---|---|---|
| vendor/accesskit_macos | accesskit_macos 0.26.3, https://github.com/AccessKit/accesskit (The AccessKit contributors) | MIT OR Apache-2.0 | the subclassing adapter and its tests from 0.27.2: dropping the adapter keeps AppKit's key-value observers (vendor/README.md) |
| 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) |

@@ -98,7 +98,7 @@ More commands, the Windows cross-build and the browser build are in [docs/buildi
runs its own design session, or with `--connect` it drives the running app so you can watch.
Claude Code: `claude mcp add solidcraft -- solidcraft-cli mcp`. If a terminal doesn't find
`solidcraft-cli` (older Windows installers don't put it on `PATH`), give its full path (by default
`"C:\Program Files\SolidCraft\solidcraft-cli.exe"`). Tools, access rules and an example session:
`"C:\\Program Files\\SolidCraft\\solidcraft-cli.exe"`). Tools, access rules and an example session:
[docs/mcp.md](docs/mcp.md).
## Scripts
@@ -150,3 +150,4 @@ Tinkercad are trademarks of their respective owners, named only to describe comp
Except as otherwise noted, this project is licensed under the following
@@ -13570,6 +13571,32 @@ The above copyright notice and this permission notice shall be included in all c
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
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";
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 [--param-check off|warn|strict]\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 (--param-check checks each step against its command's contract: warn reports on stderr, strict stops the run)\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.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";
"The graphics device was reset (the graphics driver stopped responding), so SolidCraft closed and started again. Unsaved changes were kept for recovery.",
);
ifsoftware{
note.push_str(" This start uses software rendering, which doesn't depend on the graphics driver.");
}
ifletSome(reason)=self::reason(reason){
note.push_str(&format!(" The graphics driver reported: {reason}"));
}
note
}
/// Start SolidCraft again with the same arguments, the loss stamped at `lost_at`, on the software
/// renderer when `software`.
pubfnrestart(software: bool,lost_at: u64){
letOk(exe)=std::env::current_exe()else{return};
/// The command that starts `exe` again with `args`, the loss stamped at `lost_at` and its
/// `reason`, on the software renderer when `software`.
"A Single hole snaps to the middle of its face and to the centres of its round edges":"A Single hole snaps to the middle of its face and to the centres of its round edges",
"Base Center":"Base Centre",
"Center X":"Centre X",
"Center Y":"Centre Y",
"Center of mass":"Centre of mass",
"Centre to centre":"Centre to centre",
"Circle center":"Circle centre",
"Colour":"Colour",
"Copy all licences":"Copy all licences",
"Customize Toolbar":"Customise Toolbar",
"Customize…":"Customise…",
"Face center":"Face centre",
"Favorites":"Favourites",
"Favourite":"Favourite",
"Mass Center":"Mass Centre",
"Modeled":"Modelled",
"Select the centre the pattern turns around first":"Select the centre the pattern turns around first",
"Snap to face and edge centres":"Snap to face and edge centres",
"cmd.sketch_slot_arc_center":"Centre Point Arc Slot",
"cmd.sketch_slot_center_point":"Centre Point Slot",
"cmd.sketch_slot_center_to_center":"Centre to Centre Slot",
"color.copy":"Click to copy colour values",
"color.edit":"Click to edit colour",
"color.gamma":"Showing colour values in 0-255 gamma space",
"color.linear":"Showing colour values in 0-1 linear space",
"color.recent":"Recent colours",
"color.recent_help":"Colours you pick in any colour field appear here",
"color.saturation":"Saturation (per cent)",
"color.selected":"Selected colour",
"color.step_down":"Step down",
"color.step_up":"Step up",
"color.value":"Value, the brightness (per cent)",
"dxf.cancelled":"DXF export cancelled",
"dxf.cancelling":"Cancelling DXF export",
"help.apache":"Apache licence 2.0",
"help.appearance.assign":"Gives the selected bodies, faces or components a colour or a material look, without changing their weight. Use it to tell parts apart or to preview a finish.",
"help.face.delete":"Removes the selected faces and lets the neighbouring faces close the gap, or leaves the gap open as a surface. Use it to take off a rounded edge, a hole or a boss that is in the way.",
"help.font_details":"The interface uses egui's built-in fonts (Ubuntu Light and Hack, under the Ubuntu Font Licence and the MIT/Bitstream Vera licence; Noto Emoji and the emoji icon font, under the OFL and MIT). Sketch text uses Liberation Sans (SIL OFL 1.1). The Crafting Apps' optional font pack, craft-fonts (BIZ UDPGothic, Shippori Mincho, BIZ UDMincho, Noto Sans CJK SC, Noto Sans Arabic; SIL OFL 1.1), is not bundled in SolidCraft builds.",
"help.licences":"Licences",
"help.material.assign":"Sets what the selected bodies are made of, such as steel, aluminium or ABS. Use it so that the mass and weight match the real part.",
"help.mit":"MIT licence",
"help.solid.draft":"Tilts the selected faces by the angle you set, measured from a neutral plane. Use it on moulded or cast parts so that they slide out of the mould.",
"help.solid.revolve":"Spins a sketch profile around an axis to make a round solid. Use it for shafts, knobs, bowls and any part that is round about a centre line.",
"help.solid.split_body":"Cuts a body into separate bodies with a plane, the face of another body or a sketch line. Use it to make parts that are printed, machined or coloured on their own.",
"help.solid.split_face":"Divides the selected faces along a plane, the face of another body or sketch curves, without cutting the body. Use it to give part of a face its own draft, colour or offset.",
"help.third_party":"Third-party licences",
"home.new_mm_hint":"Millimetres · opens in Design",
"navbar.description.separator":"A thin line that groups neighbouring slots.",
"pattern.alignment_hint":"Counts include the original. Centre balances copies; Flip chooses the extra side for even totals.",
"prefs.bottom_centre":"Bottom centre",
"prefs.logo_theme":"Theme's colours",
"prefs.max_file_size":"Largest file to open",
"prefs.max_file_size_design":"Design files",
"prefs.max_file_size_flag":"This run started with --max-file-size {size}, which applies until SolidCraft closes. These settings apply when it starts without it.",
"prefs.max_file_size_note":"Defaults: 512 MB and 256 MB; at most 16 GB. Opening a file takes several times its size in memory. A design that keeps a large import may need a higher design file limit to save and reopen.",
"prefs.max_file_size_step":"STEP and import files",
"prefs.max_file_size_web":"The web app opens STEP and import files up to 512 MB and designs up to 256 MB: a browser tab can't hold larger files. The desktop app can raise these limits.",
"prefs.megabytes":"MB",
"prefs.network_locations_note":"For organisations that keep designs on file servers. Off: only servers you allow.",
"prefs.sketch_conflict_colour":"Conflicting geometry in the conflict colour",
"prefs.sketch_conflict_colour_note":"While a sketch's constraints conflict, the curves and points a failing constraint holds are drawn in the conflict colour too, not only its glyph or dimension. Off: they keep their usual colours.",
"prefs.sketch_construction_lines":"Extended construction line tools",
"prefs.sketch_recent_snap":"Auto snap to recent centres and midpoints",
"prefs.sketch_recent_snap_note":"While drawing, snap the cursor where the guides of the two most recent circle centres or straight-edge midpoints cross. Off: those guides stay visual and the cursor does not move.",
"prefs.sketch_state_icons":"Sketch icons in the colour of their state",
"prefs.sketch_state_icons_note":"In History and the browser, a sketch's icon takes the colour of its state (under constrained, fully constrained or conflicting) and its tooltip names the state. Off: every sketch has the same icon.",
"prefs.sketch_tracking_guides":"Centre and midpoint alignment guides",
"prefs.sketch_tracking_guides_note":"Hover over a circle centre or straight-edge midpoint, then move horizontally or vertically to show a temporary dotted guide.",
"prefs.status_bar_note":"A thin bar along the window's bottom edge shows what is being edited, the open sketch's state in its colour and the design's units; a click on the units changes them. The sketch's state then no longer shows in the view's corner.",
"prefs.structure.appearance_note":"Colours, text and accessibility.",
"render.aging_airborne_help":"Airborne dust is a separate finite homogeneous scattering volume. It changes transport and raw EXR, not the surface-aging stack. Existing volume/glass/denoising restrictions remain explicit; no atmosphere is added automatically.",
@@ -62,6 +133,9 @@
"render.aging_target_help":"A named target must still match its captured index and name. Global masks are world-anchored; they do not follow moving parts. This is neither cavity detection nor true edge wear. Filter the list to reach additional bodies.",
"render.background_hdr_separate":"The current HDR resources illuminate and reflect on surfaces; they are not automatically displayed as a panorama behind the subject. Changing a gradient does not rotate or recolour the lighting.",
"render.background_lighting_section":"HDR illumination · separate from camera background",
"render.background_no_alpha_override":"A camera background is not added to transparent output silently. Raw-master regrading uses captured coverage and does not reapply denoising; the source EXR remains unchanged.",
"render.background_not_light":"Background colour is presentation, not an additional lighting source.",
"render.background_regrade":"Apply to retained raw master",
"render.background_stage_occlusion":"This shot includes physical stage geometry. A large floor or wall can fill the frame and hide a camera-only background. Clear or resize the stage deliberately, or change its physical reflectance. Regrading cannot remove captured geometry.",
"render.background_workflow":"Choose the background you intend: a camera-only colour/gradient, a physical stage that receives light, or HDR illumination. They are independent. Live Preview is a modelling view; render a new image or regrade a retained master to see camera finishing.",
"render.base_color":"Base colour · sRGB",
"render.bloom_help":"Creative output-only glow from positive linear highlights, spread with three normalised box passes. Radius is in output pixels. Added in linear display light before sRGB; not a lens, volumetric effect or convergence improvement. The raw EXR is unchanged.",
"render.cpu_area_editor_help":"Full side vectors in millimeters; U cross V points towards the emitting side. This differs from GPU half-edge vectors.",
"render.cpu_light_help":"Up to eight directional lights. An empty custom list turns them off; selecting a preset restores its lights. Colours are linear RGB and intensity is relative, not watts.",
"render.effects_gpu_only":"These effects require GPU path tracing. Changing the page never switches the renderer automatically.",
"render.effects_help":"Combine ordered colour looks and image-space lens effects. Rendering and raw-master regrading use the same saved settings. Raw EXR stays unchanged. These are original artistic controls, not calibrated lens or branded-film emulations.",
"render.effects_reset_lens":"Reset lens effects",
"render.effects_seed":"Pattern seed",
"render.env_colour":"Colour",
"render.environment.radial_only":"Centre and radius apply to Radial mode.",
"render.error_finish_gesture":"finish the modelling gesture before previewing",
"render.error_finish_operation":"finish the current modelling operation before entering Render",
"render.hdr_primaries_undeclared":"The source does not declare primaries; review the renderer colour requirements.",
"render.image_cancelled":"Render cancelled",
"render.image_cancelling":"Cancelling",
"render.image_scene_stale":"Finish the modelling gesture or refresh the scene before checking or starting an image.",
"render.inspector_colour":"Colour",
"render.layer_down":"Move down",
"render.layer_enabled":"Layer enabled",
"render.layer_remove":"Remove layer",
@@ -90,6 +182,18 @@
"render.layer_up":"Move up",
"render.lens_effects_help":"Flare uses up to four separated bright sources in the actual linear image. Ghosts, halo, streaks, chromatic offsets and radial distortion are artistic image-space approximations. Spatial effects need opaque output; outside samples extend the image edge. Grain is deterministic. No physics, EXR or geometry is altered.",
"render.library_inspect_only":"Select to inspect; nothing is applied automatically. Favourites and recent history belong to this open document session.",
"render.library_rig_help":"Fit once at an explicit centre and physical extent. Complete rigs include passive cards; calibration blockers still refuse. Conservative emitter/card clearance is checked before saving.",
"render.library_source_preview_help":"Authored base-colour source map. This is not a rendered or measured material preview.",
"render.library_unfavorite":"Remove favourite",
"render.light_behaviour":"BEHAVIOUR",
"render.light_preview_help":"Live view is unshadowed; Area lights use a centre-sample approximation. Render uses the complete light model.",
"render.material_color_replace":"Base map replaces body colour",
"render.material_map_required":"Choose a colour image or a normal image before using maps.",
"render.material_maps":"Planar colour and normal maps",
"render.material_maps_help":"PNG/JPEG up to 1024×1024; 16 MiB encoded total. U and V are orthogonal world-mm repeat vectors (maximum 128 repeats). Colour is sRGB; normal data uses OpenGL +Y. Files are captured at Check/Start.",
"render.opaque_preview_help":"Opaque PBR and simple colours; blended transparency. Refraction, HDRI and final rendering are not available yet.",
"render.page_aging":"Aging & contamination",
"render.page_background":"Backgrounds",
@@ -108,22 +217,48 @@
"render.radial_distortion":"Radial distortion",
"render.ribbon_aging":"Aging",
"render.ribbon_effects":"Effects",
"render.ribbon_studios_help":"Browse implemented studio compositions and review their replacements before applying. Concept-only scenes remain labelled.",
"render.sharpening":"Detail sharpening",
"render.soft_focus":"Soft focus",
"render.stage_panels.centre":"Centre",
"render.stage_panels.source_note":"Authored base-colour source map. Not a measured material.",
"render.studio_area_help":"Up to four one-sided rectangles. Orthogonal half-edges U and V define size; U × V faces the lit side. Coordinates are world-space millimetres.",
"render.studio_center":"Centre X, Y, Z (mm)",
"render.studio_hdr_help":"Radiance .hdr, 2:1 panorama, up to 2048 × 1024. EXR is unavailable. The colour assumption applies only when primaries are unspecified.",
"sketch_text.align_center":"Centre",
"ui.3d_mouse_kind_cap":"Return to Centre",
"ui.3d_mouse_looks_like_cap":"The reading stays put while you hold it, the way a Return to Centre device does.",
"ui.3d_mouse_orbit_tip":"Traced: the 3D mouse turns the view around the part of the model in the middle of the view when the turn begins, marked by a faint ball. Classic: around the view's centre",
"ui.3d_mouse_step_pull":"Pull the cap towards you, hold it, then let go.",
"ui.3d_mouse_switch_cap":"Calibrate as Return to Centre",
"ui.agent_chat_minimize":"Minimise to a strip",
"ui.ai_mode_edits_note":"Modelling commands run without asking; saving, opening, exporting and other file or destructive actions still ask.",
"ui.center":"Centre",
"ui.center_arc_follows_cursor":"Centre point arc follows the cursor's direction",
"ui.center_arc_follows_cursor_tip":"A centre point arc runs clockwise or counter-clockwise the way the cursor goes round its centre. Off: always counter-clockwise from the start.",
"ui.centerline":"Centreline",
"ui.colour_vision":"Colour vision",
"ui.colour_vision_note":"Changes the colours that carry a meaning: sketch states, axes, bends, analyses and icons. If you use Windows colour filters, keep this Off.",
"ui.colour_vision_off_tip":"The theme's own colours",
"cmd.sketch.construction_line.vertical":"Reta de construção vertical",
"cmd.sketch_arc_center_point":"Arco por ponto central",
"cmd.sketch_arc_tangent":"Arco tangente",
"cmd.sketch_arc_three_point":"Arco por 3 pontos",
@@ -1282,6 +1294,8 @@
"prefs.orientation":"Orientação",
"prefs.overall_speed":"Velocidade geral",
"prefs.preview_choices":"Prévia das suas escolhas",
"prefs.print_face_highlight":"Destaque da face de impressão",
"prefs.print_face_highlight_tip":"Intensidade da cor e do contorno da face de impressão de um corpo na vista. 0 % não mostra destaque.",
"prefs.quality_default":"padrão",
"prefs.quality_fastest":"mais rápido",
"prefs.quality_slowest":"mais lento",
@@ -1561,6 +1575,8 @@
"ui.3d_mouse_step_twist_counterclockwise":"Gire a tampa no sentido anti-horário, segure e solte.",
"ui.3d_mouse_switch_cap":"Calibrar como retorno ao centro",
"ui.3d_mouse_switch_trackball":"Calibrar como trackball",
"ui.3d_mouse_use_direct":"Usar driver direto",
"ui.3d_mouse_use_direct_tip":"Ler o mouse 3D diretamente (hidraw) em vez de pelo spacenavd.",
"ui.3d_mouse_view_axis":"Eixo da vista",
"ui.3d_mouse_view_off":"Desligado",
"ui.3d_mouse_view_off_tip":"O perfil do driver do mouse 3D desliga este eixo, então o movimento dele não move a vista. Escolher aqui o movimento de outro eixo troca os dois, e esse movimento deixa de mover a vista.",
"ui.constraints_conflict":"Restrições em conflito",
"ui.construction":"Construção",
"ui.construction.ribbon_label":"Linhas aux.",
"ui.context_menu":"Menu de contexto",
"ui.continuity_g0":"G0 vinco",
"ui.continuity_g1":"G1 tangente",
@@ -2074,6 +2092,7 @@
"ui.infinite":"Infinita",
"ui.insert":"Inserir",
"ui.inspect":"Inspecionar",
"ui.instances":"Instâncias",
"ui.intersect":"Interseção",
"ui.isolate":"Isolar",
"ui.join":"Unir",
@@ -2161,6 +2180,7 @@
"ui.pan_zoom_orbit":"Deslocar, zoom, orbitar",
"ui.parallel":"Paralelo",
"ui.parameters":"Parâmetros",
"ui.part_size_needs_a_part":"Selecione uma peça padrão inserida (um parafuso, uma porca, uma arruela ou um pino) para alterar o tamanho. Para aumentar ou diminuir um corpo, use Escala.",
"ui.part_size_shared":"Reconstrói a peça, substituindo quaisquer recursos adicionados. Todas as ocorrências deste componente são atualizadas.",
"ui.paste":"Colar",
"ui.path":"Caminho",
@@ -2186,6 +2206,11 @@
"ui.polygon":"Polígono",
"ui.preferences":"Preferências",
"ui.press_a_key":"pressione uma tecla…",
"ui.print_face_body_not_sent":"O corpo dessa face não está sendo enviado: adicione-o primeiro a Corpos",
"ui.print_face_cleared":"{body}: face de impressão removida",
"ui.print_face_input":"Face de impressão",
"ui.print_face_pick":"Selecione uma face plana de um corpo sólido para torná-la a face de impressão",
"ui.print_face_set":"{body}: face de impressão definida; Enviar ao fatiador apoia o corpo sobre ela",
"update.check_unavailable":"A verificação de atualizações não está disponível para esta instalação.",
"update.checking":"Verificando…",
"update.copy":"Copiar",
"update.dmg_opened":"A imagem de disco está aberta e verificada: arraste o SolidCraft para Aplicativos, substituindo a cópia antiga, e reinicie o SolidCraft.",
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.