Text and icons the newer UI paints on a selected, highlighted or accent fill now
fall back to the theme's selected or body ink where the preferred ink does not
read there (theme::readable, Tokens::accent_on, Tokens::on_selected), so
SolidCraft 95's navy-on-navy selections, 1-bit and CGA's same-colour pairs and
the light-accent title bar pill are readable. Themes that predate the
navigation bar take its hover, press, dim, field, panel and tooltip colours from
their own palette instead of their base's blue-greys. SolidCraft 95, Amber
Phosphor, One Dark, Solarized Light, Nord and Gruvbox Dark Soft get the few
colour tokens that read below 3:1 adjusted.
newer_ui_text_and_icons_stay_readable_in_every_built_in_theme walks every fill
and ink pair these surfaces paint and checks it in every built-in theme.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* 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>
* 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.
* 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>
* 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>
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>
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>
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.
* Point clouds: insert PLY, XYZ and PTS scans as a modelling reference (#5151)
A point cloud is kept in the design as a reference, drawn as depth-tested
dots and never a body.
- io: a PLY reader (ASCII, binary little/big endian; x, y, z of any numeric
type, optional colours, other elements skipped; counts checked against the
file) and XYZ/PTS text, thinned evenly past max_points. PLY meshes with
faces can also come in through file.insert_mesh.
- doc: Document.point_clouds (shared f32 points relative to an f64 origin,
colours, units, rigid placement, size and colour), stored as a binary
container part; format change in pending/point-clouds.rs.
- engine: pointcloud.insert / edit / delete / list; doc.open opens a cloud as
a new design; selection.delete takes point_clouds; view bounds include them.
- gpu: an instanced point-sprite pipeline (16 bytes a point).
- ui: Browser Point Clouds folder with eyes, menu and an Edit Point Cloud
card; drop, Open, Insert and file dialogs take .ply/.xyz/.pts; a CPU
viewport fallback; messages in en, en-GB, ja, zh, de and ko.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Point clouds: colour by the file, one colour or height, from the UI (#5151)
The Edit Point Cloud card gets Color By (File Colors, Single Color,
Height). Height is a blue-to-red ramp over design Z, between the cloud's
own two heights or, by default, a range worked out from the points (all
but the lowest and highest 2 %, from an even sample). pointcloud.insert
and pointcloud.edit take color_by and height_range; pointcloud.list
reports them. The colour of each point is worked out in one place
(PointCloud::shade) for the GPU and CPU viewports.
Also: the card's dropdowns compare the picked index (a dropdown's
response doesn't report the pick, so File Units did not change either).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Point clouds: integrate format registration and viewport caches (#5151)
* Point clouds: satisfy current UI audits and document comparisons (#5151)
Route model RGB data through the theme conversion boundary, provide a compact ribbon label, and include point clouds in the new document comparison. Gate the Unix-only CLI-link helper and test so Windows all-target checks remain buildable after the rebase.
* Point clouds: isolate translations for current main (#5151)
Move the existing 74 messages unchanged into one pending locale file and attribute it through a pending row, following the current repository workflow. Repository checks, 50 tooling tests, final workspace Clippy, wasm and generated-catalog equality checks passed after the rebase.
* Point clouds: fit the rebased code to main's GpuTarget and Frame
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Compare point clouds when reusing placement-independent results
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Point clouds: complete command help and required translations (#5151)
* Point clouds: size the save cap from the raised limit 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>
* Engine: one sketch store for the sketch commands (W38.0, #5425, no behaviour change)
The sketch commands found and kept the sketch they work on in about forty places, each its own
way. They now go through one module, cmd::sketch_store, so a drawing session (W38a/b) can put
sheet and view sketches behind the same calls without the commands changing again.
- target: the command's `sketch` param or the sketch being edited (target_sketch, moved).
- active: the sketch being edited, if any. The thirteen commands that need one keep their own
refusal text; unifying the three texts is a separate, visible change.
- read and write: the only calls from the sketch commands to Document::sketch and sketch_mut.
- update: change a sketch on a copy of the document and keep both, the edit the text and link
commands made by hand (sketch.text, sketch.edit_text, the projection, intersection and
include links, sketch.fit_curves_to_section, sketch.break_link). As before, those paths
neither solve the sketch nor run its checks.
- checked: the checks an edited sketch passes (the periodic splines), shared by edit_solved and
sketch.offset.break.
edit and edit_solved stay in sketch.rs and load and keep their sketch through read and write.
Imported text, snap points and the face projections keep their own shape (a text that can't be
linked is skipped, snap points copy the document only when one is needed) through read and
write. The reads and writes that are about the design's own sketches stay as they are, each on
a line marked `// design-only:` (Redefine Sketch Plane, a joint's curve, a motion trace's wire,
and the session's live drag, parameter edits and `created` diff).
A source scan, each_sketch_command_goes_through_the_store, fails when a command reads or writes
a sketch, or asks for the active one, anywhere but the store or a marked design-only line in the
files that work on the design's sketches. Every existing sketch test is unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Engine: the sketch store's scan also catches the active sketch taken as a value (#5425, review of #6336)
The scan only caught `active_sketch.ok_or`, so main's `let Some(id) = s.active_sketch else …`
could have come back unseen (0xlillith's review). It now also flags the value-read forms:
`) = s.active_sketch` (let-else and if let), `=> s.active_sketch` (a match arm),
`s.active_sketch.and_then(` and `s.active_sketch.map(`. State checks (`is_some`, `is_none`,
`== Some(`), assignments and snapshot copies stay allowed.
The wider scan found five commands that hand-rolled the store's `target` rule (a `sketch` param or
the sketch being edited). They now call `sketch_store::target`: sketch.inspect, sketch.glyphs,
sketch.export_dxf, inspect.curvature_comb and inspect.minimum_radius (their `curves` form).
Their refusals become target's:
- no `sketch` and none being edited: "no sketch is being edited" (was "no such sketch (and none
is active)", or "no such sketch" for sketch.glyphs);
- an unknown name or id: "no sketch `X`" (an unknown id used to reach "that sketch is not
evaluated");
- a feature that is not a sketch: "`X` is not a sketch";
- a `sketch` that is neither an id nor a name (true, a list, an object): refused,
where it used to fall back to the sketch being edited. A null `sketch` still means none given
(the scenario harness sends one): target now reads it that way for every sketch command
(target_reads_a_null_sketch_as_none_given).
No test, doc or schema text named the old wording; the params docs ("sketch?: id|name (default
active)") still hold.
The two remaining value reads, the default sketch for a feature (features.rs) and the sketch a
measurement prefers (measure_sel.rs), go through `sketch_store::active`, with no change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Engine: a number in `sketch` is an exact sketch id, never a name (#5425, review of #6336)
`sketch_store::target` turned a JSON number into text and looked it up with
`Document::find_feature`, which falls back to a name when no feature has that id. So
`{"sketch": 999999}` could pick a sketch named "999999" whose id is something else, and export
or inspect it. sketch.export_dxf, sketch.glyphs and the two curvature inspects read an exact id
before they moved onto the store; the other sketch commands (the old `target_sketch`) and
sketch.inspect read a number as text.
One rule now, for every command that takes `sketch`: a number is an exact id (`as_u64`, then
the feature with that id); text is an id written out or a name, as before. A number that isn't
a whole number of 0 or more (negative, fractional) is refused with "`sketch` must be an id or a
name"; an id no feature has gives "no sketch `N`", as before. Nothing in the tests, docs,
scenarios or examples names a sketch with digits, so nothing relied on the old fallback.
Test (sketch_store): a sketch named "999999" with another id. sketch.export_dxf, sketch.glyphs
(with another sketch being edited, for its enabled check), inspect.curvature_comb and
inspect.minimum_radius (with a real curve id) refuse `{"sketch": 999999}`, find the sketch as
`"999999"` and by its own id, and refuse -1 and 1.5.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Engine: MidPoint finds its sketch through the store too (#5425, after #5011)
#5011 added a caller of the old `target_sketch` (MidPoint's `line_stays`, which swaps a point
picked after its line) after this branch's base. It now reads the sketch through
`sketch_store::target` and `sketch_store::read`, as the other sketch commands do, so a number
there is an exact id too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Engine: standard parts fix their sketch's entities through the store (#5425, after #6376)
#6376 (a standard part's sketches are fully defined) reads the sketch being drawn straight from
the document's features, after this branch's base. `fix_all` now takes it from
`sketch_store::active` and `sketch_store::read`, as the store's scan requires, with the same
result.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Engine: Toggle Diameter and Delete on a profile find their sketch through the store (#5425)
Two sketch commands landed on main after the store: sketch.toggle_diameter (#7729) still
called the removed sketch::target_sketch, and Delete on a picked profile (#7107) read the
sketch straight from the document. Both now go through sketch_store (target and read), and
the store's source scan passes again.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch dimensioned: use the sketch store's target after the rebase
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch dimensioned: read the sketch through the store
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>
* Render output: size presets, Check list, Copy, progress pill, R and Esc, and the result viewer
* Complete Output GPU help translations and format choices
* Remove Output size labels already present in catalogs
* fix: compile the legacy finishing controls only for tests
A pattern of bodies copies them about an axis in its component's frame,
so its copies of a root body depend on where that component is placed.
placement_digest never counted a pattern's bodies, so moving the
component kept the cached copies. Read each body through placed_body,
as Combine does.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #8237
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Sketch text: right-to-left lines are ordered and shaped (#8237)
A line with Arabic or Hebrew letters is split into display-order runs by the
Unicode bidirectional algorithm (unicode-bidi) and each run is shaped with the
font's own rules (harfrust), so letters join and marks sit where the font puts
them. Lines without right-to-left characters keep the glyph-by-glyph layout.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Sketch text: a line break inside a right-to-left line keeps the text after it (#8237)
The bidirectional algorithm ends a paragraph at a line break; every
paragraph is shaped now, not only the first, so measuring such a line
counts all of it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
* claim: #7295
* Sketch Dimension: choose horizontal or vertical from the two points' box (#7295)
Two points or a slanted line got the aligned distance whenever the text
was between the perpendiculars through them, so straight above the middle
of nearly level points was aligned and horizontal needed a thin wedge.
Now the text position relative to the box the two span picks the type:
inside it or in its outer corners aligned, above or below horizontal,
beside it vertical. This is the rule midpoint pairs already used, so both
share auto_orientation().
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Sketch Dimension: aligned across the line, horizontal or vertical by the pull past an end
Review on #7355: the aligned length must stay the default, and the box
rule shrank its zone to a thin strip on a shallow line. Now `auto` is
the distance between the two points everywhere across from them
(between the perpendiculars through them, and a tenth of their distance
past), and only a clear pull past an end changes it: up or down from
that end gives the horizontal distance, sideways the vertical one. The
two meet on the diagonals through the end, so both zones stay wide on a
shallow or steep line. Midpoint pairs keep main's box rule.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
DroppedFile::bytes exists only on native; the web build stopped at
take_theme_drops. Native keeps reading the file there. On the web, the
shell already reads every drop asynchronously before the app sees it: a
drop over Preferences' Import tile now arrives as a theme and goes to
prefs::import_theme_bytes, instead of being opened as a design.
Ctrl+W (Cmd+W) closes the active design, asking first when it is
unsaved; closing the last one shows the start page. In the web app a
capture-phase keydown listener keeps the browser from closing the tab
while a design is shown; on the start page the browser closes the tab.
Ctrl+W joins the keys the app reserves, and the tab menu's Close shows it.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #4071
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Define a named parameter inline from any value input (#4071)
Any value input, typed or sent as a command's expression parameter, takes
`name = expression` (or `#name = expression`): the design gets the user
parameter `name` with that expression, and the input stores `name`, in the
command's one undo step.
- doc: the lexer skips a definition's `name =`, so until the design stores
it the text evaluates as its expression and commands read and check it
like any value. `Document::define_inline` then turns every stored
definition (feature inputs and placements, dimension parameters, sketch
pattern and text values, joint and coupling values, sheet metal and
plastic rules, canvas planes) into a user parameter, the unit following
how the input reads a bare number. A taken, reserved or empty name, two
different values for one name, and a definition in a parameter's own
expression or a configuration cell are refused; all or nothing.
- engine: `Session::execute` runs it after any command whose parameters
hold definition-shaped text; the reply has `defined` (the first name,
as `sketch.dimension` already gives) and `definitions`. A refusal fails
the command, which leaves the design as it was.
- `parameters.change` with `new = expression` renames the parameter it
changes (a dimension's or feature input's own, references following)
and sets it; when the value is just `new`, it sets `new`. `changes`
refuses definitions. `expr.evaluate` reports `defines`.
- UI: lengths keep the name when the design unit is applied; sketch text
sends a definition as typed and previews only its value; the box corner
and offset Distance 2 use the value; fields whose command keeps no
expression (sketch scale, move, chamfer) refuse a definition with the
way to name it. The dimension editor says which parameter it named.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The hint "at the face" had no catalog message, so it stayed English in
every language. Add it in English and the 26 other catalogs, and check
that every plastic dialog hint has a message.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Select Other: a still hold opens the list while Orbit, Pan or Zoom is lit
With a mode lit in the navigation bar (or Space held), holding the left button still on overlapping geometry opened nothing, and the release then selected the top item as a click. The viewport treats such a mode as owning left drags: a plain click still selects, and every other check on the mode sits on a drag. The hold was the one place a press that had not moved counted as navigation.
The hold is now cancelled only by a navigation that has started (the drag past the click distance), so it behaves as it does with no mode lit. The hint and the user guide already describe the hold without that condition. A preset's modifier binding (Maya Alt, Alias Shift+Alt, HFSS Alt) and Ctrl+left orbit still cancel it at the press, as before: holding such a chord is a gesture to navigate.
Tests: holding still with each mode lit opens the list, picks from it and a click still selects; a drag under each mode still moves the view and opens nothing; Maya's Alt chord held still does not open the list (it fails without the preset check).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Select Other: the press that opened the list does not move the view
egui calls a press a drag once it has lasted 0.8 s. With Orbit, Pan or Zoom lit, the press that opened Select Other at 0.5 s then became an orbit, pan or zoom, and moving the pointer to a row turned the view under the open list. While the list is open, a primary press no longer takes the lit mode.
Test: a press held past 0.8 s that then moves keeps the list open and the view still, for each mode.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #8318
* Preferences: the File view entry and the search field carry the window's name (#8318)
The File view's entry that opens Preferences said "Settings", and the window's
search field said "Search settings" / "No matching settings", while the window
title, the File menu and the help texts around the app say "Preferences". The
strings that name the window or point to it now use "Preferences"; ids unchanged.
Translations of these ids followed the English source and keep their wording
until the language teams catch up.
Tests: home::tests::the_entry_that_opens_preferences_carries_the_windows_name
and prefs_search::tests::the_search_field_names_the_window_it_searches fail on
main ("Settings" vs "Preferences") and pass here; the prefs_search:: and home::
suites pass, and the full solidcraft-ui-egui lib suite passed in `cargo xtask ci`.
* claim: #6901
* Feature windows choose Join, New Body or Cut from what the material touches (#6901)
Extrude, Revolve, Loft, Sweep, Coil and the Box, Cylinder, Sphere and Torus choose
their operation by running the Join on a copy of the design off the window thread
and counting the pieces: Join when the material makes one body with what it
touches, New Body when it would be a separate piece, Cut when it lies inside a
body (Revolve and the four primitives; Extrude keeps its own Cut). The choice
changes only when a check is in, a pick in the Operation row (the shown option
included) stops it for good, and OK waits for a running check. Hidden bodies take
no part: with some hidden, the shown ones become the feature's targets.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Operation row: a pick in Extrude stops its own choice at once; ja/zh strings, gallery entry
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Operation check: contact test first, mesh volumes for Cut, OK takes the evaluated Join
Material touching no body is a New Body without running the Join (the contact
test costs 0-240 ms where the Join costs seconds on a large body). Inside is told
from display-mesh volumes instead of exact ones (8 s on a 400-hole plate). The
check evaluates the Join through Session::ahead, and OK of the same commands
takes it (Session::prime) rather than computing it again.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Preference: Choose the operation automatically (on by default)
Preferences > Design gets "Choose the operation automatically" (auto_operation,
on by default). Off, feature windows don't choose Join, New Body or Cut from
what their material touches: each keeps the operation it opens with (Extrude
its own Join or Cut), no check runs and OK doesn't wait for one, as before
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Arabic, Hebrew, Vietnamese and Turkish strings for the operation choice and its preference
Arabic, Hebrew and Vietnamese are complete catalogs now, and Turkish has a full
catalog, so this change's three strings get them too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Operation check: a closed window's check stops; the icon audit doesn't choose
A window's operation check kept running after the window closed, a Join on
the design for nothing, and the test harness waited for it after every step:
the icon audit, which opens every window, took ~30 min instead of ~1. The
check now runs under a Progress, and closing the window (or one that doesn't
choose) abandons it: its evaluation stops at the next feature and nothing
waits for it. The icon audit turns auto_operation off, as it only looks at
icons (75 s again).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Box placement test: the window keeps its New Body
The test #6248 added places a box that overlaps Base and expects a second body;
with #6901 the window would join or cut it. The test is about where the box
goes, so it turns auto_operation off.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Join pieces: read the target lists leniently 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: #7717
* Stand-up offer: a note only when the part seems to lie on its back (#7717)
* Stand-up note: an unclear shape still gets the note
* Stand-up note: a Don't ask again button, as the dialog has
* Control protocol: the stand-up settings in the sorted ui.prefs list (#7717)
* Stand-up note: the control import test asks as before (#7717)
The test checks the caller's `up`, so it sets the setting to Ask and every
file from another program still gets the offer.
* claim: #6645
* Sketch: Center Point Arc follows the cursor clockwise or counter-clockwise (#6645)
The centre-point arc always ran counter-clockwise from its start; a
clockwise arc needed a typed negative sweep. The tool now tracks the
signed turn the cursor makes round the centre after the start is placed,
and its sign sets the direction of the preview, the live Sweep box and
the created arc.
sketch.arc.center_point takes `clockwise` with a clicked end: the arc is
stored counter-clockwise from the end and reversed, as a negative sweep
is, so the start keeps its radius and `<id>.start`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FN5eEMrqWWXqi65FcK8GXq
* Sketch: keep the old counter-clockwise centre-point arc behind a preference (#6645)
Preferences > Sketch gets "Center point arc follows the cursor's
direction" (center_arc_follows_cursor, on). Off, the arc runs
counter-clockwise from the start whichever way the cursor went, as it
always did. Its strings are a pending locale file (en, de).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FN5eEMrqWWXqi65FcK8GXq
* Sketch: Japanese and Chinese strings for the centre-point arc preference (#6645)
ja and zh are complete catalogs, so the new preference's label and tip
need them too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FN5eEMrqWWXqi65FcK8GXq
* Sketch: Arabic, Hebrew and Vietnamese strings for the centre-point arc preference (#6645)
Arabic, Hebrew and Vietnamese became complete catalogs on main, so the new
preference needs them too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: sascha2101 <221609842+sascha2101@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Russian: translate the Render workspace's 706 messages (batch 2)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Russian: the 27 Render messages main gained since (the Appearances panel)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
A new command, sketch.spline.reverse, runs a spline the other way: its
point list is reversed in place, so its last point becomes its first and
the curve keeps its path (chord-length fits, uniform clamped knots and
closed fits read the same both ways). Points and constraints stay. A
projected spline and one an offset uses or makes are refused. The
sketch's right-click menu offers it for one selected spline.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #8284
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Sketch on a face: a round's edges that end a hair apart still close (#8284)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Sketch on a face: test both ends of a rounded loft end to end (#8284)
A square lofted to a circle with its side edges rounded, as in the
report: a sketch on either end face takes the whole face. Without the
fix the circular end fails with "face projection has a branched
boundary". Also drops a redundant term from the join tolerance.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
* claim: #8301
* Timeline: the feature menu's suppress item follows the reader's language (#8301)
Suppress Features / Unsuppress Features were raw literals. Menu::label()
translates a label only when a catalog entry carries it, and none did, so the
item stayed English in every language, the Japanese UI included.
The two labels are now the messages timeline.suppress_features and
timeline.unsuppress_features in a new assets/locales/pending file: the English
wording is unchanged, and the complete catalogs (ar, he, ja, vi, zh) plus de and
ko carry the translations.
cargo test --profile ci -p solidcraft-ui-egui --lib -- timeline::tests::the_feature_menu_suppress_item_follows_the_language
fails on the claim commit (the Japanese menu paints the English literal) and
passes with the change.
The Appearances panel's 27 messages for Dutch, Hungarian, Galician,
Malay and Norwegian Bokmål, which their merged Render batches missed,
and the shots strip's 7 newer messages for those and ten more.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Fix issue with settings window automatically scrolling to selected item, preventing scrolling by user
* Fix issue with settings window automatically scrolling to selected item, preventing scrolling by user
In the Sketch > Create drop-down a plain row such as Point was drawn
200 pt wide, the minimum width its labels fit in, while a family row such
as Slot filled the menu. The menu was as wide as the one-line pin hint in
its footer, so the plain rows' hover background and pin stopped short of
the right edge.
The Create menu now sets its width from its rows before drawing them, as
the other panel drop-downs do, and passes that width to the plain rows
instead of recomputing it over a subset. Family rows fill the same width
and the footer hint wraps inside it. The overflow menu's plain rows take
the width of all its commands in the same way. A family's submenu still
sizes its variants to their own labels.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Most browser menus show V beside Show/Hide, but the V key always ran
toggle_visibility, which acts on the selection. A right-click on a body or
sketch row also selects it, so the two agreed there; a right-click on the
Bodies, Sketches or Origin folder, a group, the component or an origin plane
does not, so V did nothing, or flipped a body selected earlier, and left the
menu open.
With a menu open, V now runs that menu's row whose shortcut is V, through
run_item as a click does, and closes the menu (context_menu::press_shortcut).
A disabled row takes the key and does nothing. With no menu open, or a menu
without such a row, V still acts on the selection (#4128, #4409: V stays
selection-based).
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #8329
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019FAWPuFoh9qxn2uS2j53QZ
* Save As offers the open file's own name (#8329)
Save As, Save As JSON and Save a Copy proposed the design's name, the one
stored inside the file. Copies of a file under other names keep the
stored name, so "Sample Plate-bug.solidcraft" was offered
"Sample Plate.sc", the same as its sibling, and one replaced the other.
Session::save_as_name() offers the stem of the open file unless the
design was renamed since it was opened or last saved, and the design's
name otherwise (a new design, or one renamed in the Browser).
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>
Each cap is tangent to both concentric sides, so its radius is already
half the gap between them and the two caps are equal. The Equal the arc
slot added between them was always redundant; it is no longer added.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
A button is 60 points tall and its label starts 32 points down, so two
lines of the label font (31 points) ran 3 points past it. Two-line
labels now take the font size plus 2 points per line and start higher
when they would still run past the button, leaving 2 points below.
One-line labels are unchanged.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #8020
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* Insert Part: the Length list opens on the default length (#8020)
The list carried an item called "Default" above the standard lengths. It
never said which length it stood for, and one of the lengths below it
always was that length, so the list offered the same length twice under
two names. Change Part Size has always listed concrete lengths.
The item is gone and the list opens on the length the part would be given
anyway. The default cannot be worked out in the dialog, because it is not
one rule: a cap screw or bolt takes the first standard length at or above
2.5 d, a set screw the first at or above d, a dowel pin the first at or
above 4 d. parts.library now reports default_length beside each size's
table values, taken from the shape the engine would build, and the dialog
selects it; the test checks that report against what an insert without a
length is actually built to, for every size of every family.
Leaving the length alone behaves as "Default" did: a new size resolves
the list again, so M6 to M12 moves the selection from 20 to 30. A length
chosen by hand stays until the size changes.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* Insert Part: the labels test skips default_length too
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: #5638
* Sphere, Torus and Cone dialogs: place them by their centre or base (#5638)
The Box and Cylinder dialogs already had typed X, Y and Z placement
fields; the Sphere, Torus and Cone dialogs now do too. Sphere and Torus
show Placement: Center and take a picked point like the Box; Cone shows
Placement: Base Center like the Cylinder. The fields take expressions,
and Edit Feature shows and keeps the stored centre or base.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
With the dialog buttons at the bottom, the spinner shown while the preview
computes or OK applies was painted on its own after the footer: at the
card's left edge and one control height up from its bottom. The footer
insets its row by 12 pt at the sides and 10 pt above and below and centres
the buttons, so the spinner sat outside that inset and about 10 pt below
the buttons' centre line.
Panel::feature now draws the spinner through the footer's left slot, so it
shares the row's inset and vertical centre, in the space the footer already
leaves for it before the status text. The rect is published as
dialog:footer:spinner, and a panel test checks it lies in the footer row,
at its left inset and on the OK button's centre line. The spinner in the
title row (buttons at the top) is unchanged.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Italian: translate the 132 messages main gained since batch 1 (batch 3)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Italian: the Appearances panel's 27 Render messages
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Dutch: translate the 199 messages main gained since batch 1 (batch 3)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Dutch: Spoed for a thread's pitch, as the catalog's Pitch (review)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Render inspector: selection-following appearance panel with the look chain, Applies to and edit controls
* Render appearance: Glass, Clear coat, Maps, Saved optics and Reflections sections
* Update Render navigation tests for dedicated editors
* Exercise Focus keyboard protection through the Library search field
* French: translate the 205 messages the catalog was missing
The French catalog lacked 205 of the English messages, so parts of
Preferences, the hole presets, the slicer messages, the Render
workspace and the Assistant showed in English. They are translated in
one pending file, with the catalog's terms (Esquisse, Fonction,
Bossage, Appui, Clip, Prise de vue) and its non-breaking space before
: ; ? and !.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Translate the 90 messages main added since (the navigation bar's)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
A variable font (one file for every weight, as most web fonts ship) has no face marked bold,
so Bold failed with "has no bold face". Without a bold face, a face whose wght axis reaches past
semibold is now set to weight 700. ttf-parser gets its variable-fonts feature for that.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #8155
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019FAWPuFoh9qxn2uS2j53QZ
* DXF import: an elliptical arc comes in as exact conic arcs (#8155)
A partial ELLIPSE (how Fusion, and SolidCraft's own DXF export, write an
elliptical arc) was read as a degree-3 spline fitted through 16 samples,
which strays from the ellipse between them: 0.84 mm on a 300-degree arc
of a 200 x 40 mm ellipse. It is now read as exact rational-quadratic
conic pieces of at most a quarter turn each, which the sketch keeps as
conics. A conic arc written by SolidCraft's DXF export now reads back as
the same conic.
The arc's sweep is taken with rem_euclid instead of a loop that added a
turn at a time, which ran for billions of turns on a start angle of 1e11.
sketch.drawing_bounds and the width/height sizing of sketch.insert_dxf
measure a conic by points along it, not by its apex, which can lie
outside the curve.
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: #8265
* Move/Copy: a triad drag ends where the button was released (#8265)
A frame carries all the input queued since the last one, so the release
that ends a drag and further pointer movement — the hand keeps moving as
the button goes up, or input is queued during a busy frame — arrive
together, and the frame's latest pointer position is already past the
release. The drag's final change was read from it, so the value, the
preview and the command both jumped to wherever the mouse had got to;
pressing Enter right after the release closed the window first, which is
why that worked around it. Rotate and occurrence moves share the triad.
The final change now comes from the release event's own position, which
the raw input carries (egui's processed pointer events drop it). The
regression test sends a release at 20 mm and a move to 30 mm in one frame:
30 mm before, 20 mm after.
Fixes#8265.
* claim: #4086
* Raise the largest file SolidCraft opens with a setting or launch flag (#4086)
* Translate the file size limit strings
* Size limits: design files are .sc now, .solidcraft still read
* Romanian: translate the Render workspace's 733 messages (batch 2)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Romanian: the Render tree's switch hint is a verb
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #6651
* test(#6651): a list parameter of the wrong type is refused
A list parameter such as a cut's `targets` or an export's `bodies` loses entries without an error: a value of the wrong type reads as an empty list, which most commands take to mean "every body". The regression test drives the engine the way a user does: two bodies, a box cut that crosses both and is meant for one of them, and a `targets` of the wrong type. The command must refuse it and leave the design as it was.
* fix(#6651): a list parameter refuses a wrong type instead of losing entries
A list parameter of the wrong type was read as an empty list, and an empty
list means "every body" to most commands: a cut meant for one body cut every
body it touched and the command reported success.
A wrong type is now an error that names the parameter, the offending entry
index and the value it got. A list longer than 10000 entries is an error
instead of being cut short. `null` stays absent, as the schema contract says,
and a plain string is still a list of one. The 80 call sites now propagate the
error instead of losing entries; the commands that take feature keys, which
may be named or numbered, read them with their own reader.
# 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.
@@ -602,6 +602,32 @@ Original SolidCraft drawings, Open for owner review; no runtime assignments.
| assets/locales/sr.json | Tarik Sijercic (tsijercic1) | Original Serbian (ekavian, Cyrillic) translation of SolidCraft's English catalog; no commercial CAD text used | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/finish_edit.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| `assets/locales/pending/sketch-tracking-guides.json` | SolidCraft contributors | Original alignment-guide preference labels and help text | MIT OR Apache-2.0 |
| 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 |

@@ -58,9 +58,9 @@
- **Web:** [try it in your browser][web-app], no install.
ARM64 and 32-bit builds, portable zip, .deb, .rpm, Flatpak and the other packages are on the
[downloads page](docs/downloads.md). The downloads aren't signed yet: on Windows choose
**More info**, then **Run anyway** in SmartScreen; on macOS right-click SolidCraft and choose **Open**
the first time. On SolidCraft 0.11 to 0.14, download once by hand; see
[downloads page](docs/downloads.md). The macOS app is signed with a Developer ID and notarized by
Apple. The Windows downloads aren't signed yet: choose **More info**, then **Run anyway** in
SmartScreen. On SolidCraft 0.11 to 0.14, download once by hand; see
Status as of **2026-10-14**. SolidCraft supports parametric modelling (sketch →
features → parametric timeline) as an open, pure-Rust, agent-drivable application.
## At a glance
| Question | Answer |
|---|---|
| Where are we? | **M0 and M13 done; M1, M2, M5–M7, M9, M10, M14 in progress.** Sketch with solver and the full SKETCH create/modify set; extrude, revolve (two sides, symmetric, to object), fillet/chamfer (variable radius, chord length, a radius per edge, corner patches), holes, threads (cosmetic and modelled), patterns, mirror, shell (inside/outside/both), draft, loft, sweep, coil, split body and split face, delete face, construction planes, axes and points; persistent naming of faces and edges; parameters and configurations; components, joints, motion studies, exploded views; sheet metal and a plastic enclosure subset; STEP/IGES/3MF/STL/OBJ/DXF; FreeCAD/Rhino import; autosave and crash recovery; desktop and browser (wasm) app, CLI, MCP server. |
| Features | Every registered command by area — [docs/features.md](docs/features.md) (`cargo xtask features`); registry breadth does not establish workflow or geometric depth |
| Reference tests | [Current case count and results](docs/oracle.md): public cases in `tests/oracle/` with analytic or FreeCAD-computed expected values, checked by the reference-test job in [CI](.github/workflows/ci.yml). |
| Tests | Solver, profiles, kernel booleans/blends/measures, expressions, timeline, persistent naming, file formats and migrations, assemblies, sheet metal, engine end-to-end, hostile-input fuzz over every command and headless UI scenarios. `cargo xtask ci` reports the current passing counts. |
| Time to a useful alpha (M0–M6) | ≈ **82 agent hours** remain (sum of the M1–M6 estimates below) |
Optional GPU viewport ambient occlusion is available in View settings and Graphics preferences,
with persistent settings, Performance/Quality graphics presets (Quality combines AO, Fine tessellation and adaptive 2× supersampling; manual combinations show Custom), and the
`view.ambient_occlusion` and `view.graphics_preset` commands. See [display settings](docs/ambient-occlusion.md).
## Scope (owner decision, 2026-10-07)
The issue batch adds preservation of blind pockets in through cuts, associative projected
offsets and plastic face placements, XZ (+Y normal) with a v6 migration preserving older
| M1 | Sketch depth: command coverage implemented — projection/intersect/include as linked geometry (auto-project on faces, lost-reference warnings, break link), full SKETCH create/modify set (slots, polygons, tangent arcs/circles, ellipse, fit/control splines, conic, text, trim/extend/break, fillet/chamfer, offset, mirror, patterns, move/scale, blend curve, centerline), constraints incl. curvature and polygon with over-constraint refusal, Shift-picked line midpoints in Horizontal/Vertical using persisted points and ordinary constraints (#2087), driven/arc-length/linear-diameter dimensions, AutoConstrain, DXF/SVG insert, 3D sketch curves (Project To Surface, Intersection Curve, Isoparametric Curve, Spun Profile), exact free-form profiles (B-spline/NURBS edges, any spline degree, curves cut by crossings), sweeps and pipes along 3D sketch curves, canvases and decals, curvature combs. Exact free-form Extend and certified associative offsets now preserve geometry and IDs through edits, deletion and reopen. Occurrence-frame 3D wires now store explicit coordinate spaces and retain points and analytic fits through moves, refresh, undo and reopen. Constructive sketch solving (Owen 1991, Fudos & Hoffmann 1997) ahead of the numerical solver: points and rigid clusters are placed on the branch the sketch had, so dimension edits and drags keep their solution; edits that would turn a sketch over are refused; drags re-solve only the sketch per frame. Remaining workflow repairs: mixed offset junctions, legacy natural-spline seams, and damaged legacy wire links lacking frame provenance | in progress | 4 |
| M2 | Robust modelling: coplanar booleans in the kernel, holes (placed on faces, at distances from reference edges, and on sketch points), threads (cosmetic, then modelled), patterns, mirror, shell, draft, split, construction geometry | in progress | 16 |
| M1 | Sketch depth: command coverage implemented — projection/intersect/include as linked geometry (auto-project on faces, lost-reference warnings, break link), full SKETCH create/modify set (slots, polygons, tangent arcs/circles, ellipse, fit/control splines, conic, text, trim/extend/break, fillet/chamfer, offset, mirror, patterns, move/scale, blend curve, centerline), constraints incl. curvature and polygon with over-constraint refusal, Shift-picked line midpoints in Horizontal/Vertical using persisted points and ordinary constraints (#2087), drawing snaps to where two lines, circles or arcs cross (#4967), driven/arc-length/linear-diameter dimensions, AutoConstrain, DXF/SVG insert, 3D sketch curves (Project To Surface, Intersection Curve, Isoparametric Curve, Spun Profile), exact free-form profiles (B-spline/NURBS edges, any spline degree, curves cut by crossings), sweeps and pipes along 3D sketch curves, canvases and decals, curvature combs. Exact free-form Extend and certified associative offsets now preserve geometry and IDs through edits, deletion and reopen. Occurrence-frame 3D wires now store explicit coordinate spaces and retain points and analytic fits through moves, refresh, undo and reopen. Constructive sketch solving (Owen 1991, Fudos & Hoffmann 1997) ahead of the numerical solver: points and rigid clusters are placed on the branch the sketch had, so dimension edits and drags keep their solution; edits that would turn a sketch over are refused; drags re-solve only the sketch per frame. Remaining workflow repairs: mixed offset junctions, legacy natural-spline seams, and damaged legacy wire links lacking frame provenance | in progress | 4 |
| M2 | Robust modelling: coplanar booleans in the kernel, holes (placed on faces and on sketch points), threads (cosmetic, then modelled), patterns, mirror, shell, draft, split, construction geometry | in progress | 16 |
| M3 | General fillets and chamfers (curved edges, chains, vertex blends); saddle rolling-ball surfaces now use continuous seam sections and arc-length fitting with shared contact boundaries and bounded accuracy checks; tiny rim edges remain selected instead of nearby wall seams when filleting (#2498) | in progress | 30 |
| M4 | Files: STEP import (done — [docs/step-import.md](docs/step-import.md)), FreeCAD/Rhino solids through cadmpeg (done, desktop, browser and CLI), 3MF export and 3MF/STL import as mesh bodies (done), 3MF with B-rep face tags ([docs/formats/3mf-face-tags.md](docs/formats/3mf-face-tags.md), done), constant-section parametric mesh reconstruction, DXF sketches, OBJ import, PLY/XYZ point clouds as a modelling reference and PLY meshes (done — [docs/point-clouds.md](docs/point-clouds.md), #5151) | in progress | 6 |
| M5 | Persistent naming of faces and edges (docs/book/src/dev/naming.md): faces named from their exact kernel-reported history (sketch curve sides, caps, revolve sections, primitive sides, blends, shell inner walls, pattern/mirror copies; split pieces told apart by the faces around them, lumps of a cut body named after their faces), edges by their faces; fillets/chamfers, sketches on faces, shell/draft/offset/replace faces, hole placements, joint origins and face appearances resolve by name first and warn when the entity split or vanished: done. sheet metal edges by base sketch line and flange side, shell inner faces, sweep and loft roles: done. kernel-reported face history: done (merged faces answer to every name; operations without history yet fall back to geometric names, flagged unverified; format 14 keeps older designs' geometric names). Left: history through sheet metal, plastic, surface and emboss features; FaceSource certificates through history | in progress | 20 |
| M6 | UI depth: selection feedback and origin (done), timeline editing (done: hover and selection highlight modifier faces, including fillets and chamfers; edit feature, history marker drag, dependency-checked single and multi-item reorder, suppress, rename, delete with dependents, re-resolved references; redefining selections refreshes persistent edge/face references while retaining feature identity and dimension names; responsive feature history with width-driven icons/names/title, snapping width resize, bottom/left/browser-stack docking, shared-column collapse, rollback bar, multi-selection and persistent folders), right-docked dialogs (done), live previews for every feature dialog (done), keyboard-first commands with on-canvas value boxes and sketch inline dimensions (done), drag manipulators with automatic join/cut (done: extrude incl. two sides, fillet, shell, hole depth, move, pattern spacing, revolve rotator, extrude taper), dark theme (done), right-click menu with Repeat (done), measure tool (done), section analysis (done), selection filter (done), drag-solve of sketch points (done); appearance controls now preserve body/face/component targeting, named transparent looks, reuse/search and override removal; Browser Expand All / Collapse All (`browser.expand_all`, `browser.collapse_all`) on the design and component menus; preferences | in progress | 6 |
| M7 | Thin extrude (walls along open or closed curves: centre, inside, outside or two sides; mitred corners, crossing walls joined; #721), sweep, loft (incl. to a point), rib and web (to next / depth), emboss (planar faces and exact radial emboss/deboss of bounded orthogonal profiles on complete exterior cylinders/sleeves), thread (cosmetic, and modelled M3–M20 outside and inside: the ISO profile screwed in as a faceted cut), coil (smooth B-spline helical walls), pipe, pattern on path, patterns of bodies and components, replace face (parallel), align, remove, feature copy/paste: done. Unique eligible cylindrical targets are inferred for new Emboss features. Left: general curved-face emboss and exact (unfaceted) modelled threads | in progress | 12 |
| M8 | Surface workspace (#772). Done: Patch (sketch regions; edge loops filled G0, plus `crown`), Stitch, Thicken (planes and cylinders), Extend (straight edges of planar surfaces), Trim (by a plane, a face or sketch curves swept onto the surface, keeping a side or the picked regions; mutual trim of two surfaces, extended to meet and stitched, #767), Delete Face (healing), Boundary Fill, zebra/draft/curvature-map analysis, Surface Extrude/Revolve/Sweep/Loft from open and closed curves, Offset, Unstitch and Reverse Normal; the continuity-driven core (`kernel/src/bridge.rs`, G0–G3 Hermite rows) with **Blend** (`surface.blend`, #764: edge chains along one or several faces, closed rims paired seam to seam or from picked seams, a dialog), **Match** (`surface.match`), **Boundary** (`surface.boundary`, #763 — two sketch curves exact with G0–G3, three or four as the flat patch of their loop, and curve networks: Coons and Gordon surfaces through sketch, 3D-sketch or edge curves, a point for a three-sided side, G1/G2 against the faces along one direction's edges, joined into their body on request; G1/G2 on two sides that meet at a corner waits for the Gregory-type corner), **Fair** (`surface.fair`, CV fairing), **continuity analysis** (`inspect.continuity`, #770; a toolbar tool with edge markers and a legend, and curvature combs along face isocurves) and a multi-section **loft** with per-end continuity and crown, and guide rails on solid and surface lofts (one or two, scaling or stretching the sections between; #201). Also in the Class-A pass: G1…G4 blend curves in the sketch, chord and variable-radius fillets on curved edges (with honest refusals where the fit cannot hold), and a G2 corner patch where filleted edges meet (kernel; the command refuses until the patch's sections can be sewn to the blends' contacts). Left: bias/form-factor fillet sections (design recorded), a Gregory-type corner, surface Fillet between separate surface bodies (#769), Ruled (#762), Untrim (#761), curved-face Extend (#760), and every remaining #757–#772 item | deferred | — |
| M9 | Components and assemblies: New Component / Create Components from Bodies, active component, per-component origin/bodies/sketches/timeline entries, occurrences with transforms, Copy/Paste vs Paste New, nested Browser tree with visibility and isolate; Ground, Move/Copy of occurrences, joints (rigid, revolute, slider, cylindrical, pin-slot, planar, ball) by joint origins with limits and Drive Joints, closed kinematic loops (four-bar, slider-crank: the loop's other joints follow the driven one on its branch, and driving stops where a loop can no longer close), cams (`joint.cam`: a revolute cam drives a slider or revolute follower along a closed sketch profile or the cam's edges, knife, roller or flat followers, worked out again when the profile changes; drives, studies, closed loops, contact sets), As-built Joint, Rigid Group, Contact Sets (driving and motion studies stop where bodies in a set would pass through each other), Motion Study (joint values keyed along a timeline, playback, export of occurrence positions as JSON/CSV), motion analysis (joint values and a point's path charted over a study as value, velocity and acceleration; the parts that collide along a study, with the steps where they do), Exploded views (automatic or by hand, shown without changing placements, and exported as an animation coming apart and back together); interference, per-component physical properties; STEP with assembly structure; URDF export for robot simulators (links with meshes, mass and inertia; joint chains whose values match Drive Joints; docs/urdf.md). Physical properties now measure material-aware component/occurrence placements, descendants and repeated instances, including pending moves and body reassignment offsets; contact geometry and automatic exploded centres respect those offsets. Groundwork done: component tree, occurrences with transforms (instances, Paste New), world placement, active component as authoring frame, ground, Capture/Revert Position, moving bodies and sketches. UI: ASSEMBLE panel dialogs for New Component, Joint (snaps on faces, edges, circles and vertices; animated motion preview; offsets, flip, limits), As-built Joint, Joint Origin, Rigid Group, Drive Joints (live slider), Motion Link, Cam and Interference (overlaps shown red); joints in the timeline and the Browser | in progress | 32 |
| M10 | Sheet metal: flange (base, edge, contour), hem, unfold/refold, convert, flat pattern + DXF, rules: done. PLASTIC enclosure subset: Boss, Rib, Web, Lip/Groove, Snap Fit, Rest, Plastic Rules (library, edit, assign; rule draft and clearance as feature defaults): done, tested against hand-computed geometry. UI: SHEET METAL and PLASTIC tabs with dialogs (live preview, Edit Feature, pre-selection) for Flange, Hem, Unfold/Refold, Convert, Sheet Metal Rules, Create Flat Pattern (flat view with bend lines, bend table CSV/JSON and DXF export with exact circle/arc/line cutouts), Boss, Lip/Groove, Snap Fit, Rest, Manage and Assign Plastic Rules; Fold (bend along a sketch line); flanges meeting at a corner closed at any angle with the rule's gap; edge flanges with their own angle and height per edge in one feature; corner reliefs (notch, round, rectangular, tear) chosen by the rule; Convert to Sheet Metal from a solid or shelled body by its bend edges (flat faces, sharp bends) | in progress | 36 |
| M11 | 2D drawings | deferred | — |
| M12 | Release: cross-platform packages, verification and deliberate publication implemented; original current-UI demo refreshed. Trusted distribution signing and broader performance work remain | in progress | 16 |
| M14 | Configurations: a table of rows (parameter expressions and feature suppressions per row) switched with config.activate (undoable, all or nothing), kept through parameter renames and feature deletes (config.configure, config.table, config.column/row/activate). Per-row body materials and appearances (library looks, custom colour/opacity and clearing overrides), undoable activation and save/reopen: done. Ordered typed configuration rules, atomic validation and structured rule editing: done. Component appearance and certified semantic face style columns, atomic preview/apply and independent-copy remapping: done. Exact constructed sketch-side and immutable imported-face lineage now preserve legacy names and reject retargeting. Left: ambiguous shifted/two-sided caps | in progress | 4 |
| M13 | Never lose work: atomic saves (temporary file, flush, rename; a crash at any moment leaves the old file or the new one) with the replaced version kept as `<file>.bak`; autosave of unsaved changes to the recovery folder every 5 minutes (preference) and after big operations, only when something changed; "Recover unsaved design?" on the next launch (`doc.recovery_list`, `doc.recover`, `doc.recovery_discard`); a kill-mid-save test (process killed while saving a 0.5 MB design 25 times). File format: `"format": "solidcraft/12"`, older formats upgraded step by step (`doc::format`), newer ones refused with a message, out-of-range numbers and broken placements refused; a corpus of designs saved by M0, components-era, joints-era and current builds (apps/solidcraft-cli/tests/corpus) that must open and evaluate as saved; damaged and hostile variants (truncation, bit flips, hostile values, removed keys, repeated features, deep nesting) never panic | done | 6 |
| M15 | Frames and weldments (#4003): structural members along 2D and 3D sketch lines, butt and mitre corners, trim to faces and bodies, end caps, gussets, cosmetic welds and a cut list. Done: the profile library (`frame.profile.list`, `frame.profile.properties`, `frame.profile.insert`): bar, round bar, rectangular and round tube, angle, channel and I-beam, each SolidCraft's own parametric definition with exact fillet arcs and exact section properties (area, centroid, second moments), tested against hand-computed values; members (`frame.member`): one body per straight line of a 2D or 3D frame sketch, following parameter edits, with a built-in profile (its dimensions parameter expressions) or a profile sketch (its drawn material by alternating nested loops), anchor, angle and mirror; each member's name kept with its line. Left: corners, trims, end caps, gussets, welds, cut list, FRAME tab and dialogs | in progress | 60 |
M6 navigation preferences include **Touchpad**: Shift + pointer movement pans, Alt/Option +
pointer movement orbits without a mouse button, and vertical scrolling zooms. Other presets
retain their saved two-finger scroll preference; camera motion is available through
`view.navigate` and modifier-aware `ui.move` automation.
With the **Fusion** preset and two-finger pan enabled, two fingers pan and Shift + two fingers
orbit freely, with pinch zoom and mouse bindings unchanged. The shared navigation handler
applies the same gestures to model and Render views and blocks orbit on flat pages.
editable, and Automatic size returns to the face's suggestion. Cosmetic/modelled and
full/partial-length options remain available. This does not add tolerance classes or
left-hand threads.
- New Body extrusion and face-offset previews show completed intermediate geometry while a
drag continues, then catch up to the exact final value. Changes to the selected geometry,
operation or feature still discard obsolete results. Distance arrows and their value boxes
follow the current frame's input.
- Large fine modelled threads use pitch-based helix resolution and tighter faceted
reconstruction fallbacks, preserving thin crests and closed shells on large shafts.
## Physical material data
- Physical material card validation (#923/#922) provides typed SI mechanical and
thermal data, per-property sources/conditions and isotropic/orthotropic stability
checks through read-only `material.properties.validate`. It does not assign or
persist properties, provide a solver or enable rendering; see
[the contract and staged follow-ups](docs/physical-material-cards.md).
- Directional elasticity (#994) queries the validated card in material-local axes
through read-only `material.properties.directional`, with source/condition review.
It does not assign body frames, predict structural behavior or enable a solver.
## Library foundations (#785, #786, #787)
- Local scalar libraries (#846) inspect/export bounded portable catalogs and copy exact
selected appearance/material revisions into existing design definitions in one undo step.
Appearance offers local loading/search/export; physical materials use existing commands.
This slice is independent of the render stack and does not change the document format.
See [the contract and limits](docs/book/src/dev/local-libraries.md).
- Proposed phases cover offline bundled/project/user packs, physical materials and
appearances, editable wood mapping, and parameterized component/assembly families.
Stable identity/revision, copied design definitions, provenance and resource limits
apply from the first implementation. Existing material, appearance and standard-part
commands remain the application paths. See [the proposal](docs/book/src/dev/libraries.md)
for source/license review and acceptance gates. This is planning only: pack loading,
texture rendering and expanded component families are not implemented by this entry.
## Render foundation
- Original starter appearances (#1722): 34 opaque recipes through existing commands, with six explicitly unavailable glass recipes and preserved legacy looks; see [the contract](docs/render-starter-appearances.md).
- Bounded linear film accumulation (#1696) supplies caller-admitted mean/variance storage and complete-pass views; actual progressive consumers remain separate. See [the film contract](docs/render-film.md).
- Definition-local visual mapping (#1214) supplies a bounded numerical frame, physical repeat coordinates and a deterministic one-time grain-axis suggestion. It adds no saved record or texture consumer; see [the mapping contract](docs/render-visual-mapping.md).
- Explicit texture normalization (#1218) produces completed float RGBA with
role-aware linear color/data semantics, bounded matrix/TRC ICC conversion and
opt-in EXIF orientation. It retains high-precision raw samples and records lossy
gamut clipping only when explicitly requested. It is not a renderer/resource
consumer; see [the supported policy and limits](docs/render-texture-normalization.md).
- Opt-in opaque PBR (#1046) connects persisted surface parameters to the integrated
product viewport and CPU PNG, using a fixed direct-light studio. Geometry and mass
stay unchanged. HDRI, transmission and textures remain separate gates.
See [the consumer and limitations](docs/render-opaque-pbr.md).
- Combined integration is tracked in #842, with independent scalar libraries in
#863 and richer PBR/material-card transport in #1086. See [phase boundaries and validation gates](docs/render-integration.md).
Render/library consumers use the merged asynchronous picker (#1127). Material-card
UI (#1094), texture mapping and HDRI lighting remain explicit follow-ups.
- R4a adds bounded CPU product-image jobs, cancellation and atomic PNG export through
engine commands, the Render panel, CLI and persistent MCP. Immutable captures and
results stay outside persistence/undo; native workers and cooperative browser tasks
share the same work protocol. See [jobs, limits and export](docs/render-jobs.md).
- R3 adds an overlay-free product scene and the integrated Render workspace, with
runtime GPU host capabilities, per-document drafts and named camera/background
controls. Design keeps its camera and drawing policy. Raster transparency remains
approximate; PBR and environments are later slices. See
[workspace and scene contracts](docs/render-workspace.md).
- R2 adds bounded named render setups in document format 21 and the read/save
and selected occurrence frames; cancel and successful edits preserve one-step undo history.
Placed feature edits preview through the edited timeline entry, and cylindrical Emboss guides
use the sketch's actual target, author or uniquely placed third-component frame.
Revolve rotators follow the actual signed axis and support symmetric and two-sided extents;
dragging a To Object extent converts its derived value to an explicit angle. Legacy reversed-axis
symmetric geometry is preserved through format 11's explicit legacy convention. New revolves
use the sketch-normal direction for each selected profile, including To Object and curved
target trimming, independently of the selected axis sign.
- Cylindrical emboss/deboss wraps bounded orthogonal line profiles, including holes, concave
outlines and nested islands, onto complete exterior cylinders or coaxial sleeves with exact
radial walls. New features infer a unique eligible cylinder when the face field is empty;
ambiguous targets require an explicit face. Saved target
and author occurrence paths retain the chosen placements; missing/split faces refuse rather
than retarget. General free-form profiles, seams, stepped cylinders and other curved surfaces
remain unsupported. STEP round-trip and independent radial/volume checks cover this subset.
- Shell and offset faces work on bodies of planes, cylinders, cones, spheres and tori (convex or not:
filleted boxes, bosses, domes, countersinks; corners where four or more planes meet, such as a
floor falling to a drain, as long as the corner's edges are all convex or all concave); draft on convex planar bodies and on walls (planes, cylinders → cones) between caps; lofts through three or more sections are smooth (B-spline sides through every section), two
sections ruled. Circular-meridian barrels retain curved inner walls through either axial
opening (#2421). Solid lofts preserve holes through sections with matching hole counts
(#2435), including inner walls and caps with openings; a hole cannot appear or disappear
between sections, and sections with holes cannot collapse to a point.
- Sketch corner tools: fillet and all three chamfer modes pick a connected corner even when
its endpoint overlaps the fixed origin or a loose point. Ordinary drawing still snaps to
the origin; clicked UI regressions verify geometry, closed profiles and undo.
islands share the same boundaries in flat preview, DXF and unfolded solids. Detached material
pieces survive unfold/refold as one logical sheet group, including split flanged sheets.
Cut-outs made in the flat pattern cross bends at any angle or curved: their edges fold to
helices and curves on the bend, their walls square to the sheet. Free-form bases use bounded
sampling.
Parallel prismatic partial-depth cuts retain pocket side and depth through transfer and
unfold/refold, with separate pocket contours in preview and DXF. Planar Intersect preserves
exact holes and retained islands, including the union of multiple selected profiles, and
records complementary depth layers as pockets while retaining the stock gauge. Unsupported
tapered or bend-crossing machining refuses atomically instead of restoring material later.
Sketches attach to a sheet's flat faces (base and flanges, either side) by panel, in the
flat pattern's coordinates, and follow it through rebuilds and unfold/refold.
A flange on another flange's far edge (a return) follows that flange's closed end and keeps
clear of the flange beside it, and the returns of one feature are mitred toward each other
with the rule's gap where their flanges meet at a corner; returns of separate features, and
flanges on returns, are not closed toward each other.
- Rolling-ball fillet accuracy: independent offset-cylinder tee references check surface
distance, actual trimmed tessellation, supporting-cylinder contacts and tangency at two
scales. The measured trimmed-part error is below 0.0001 mm; this is an analytic geometry
check. Under-resolved fits return an error.
- Contact driving checks the trajectory between clear endpoints, including linked joints,
playback and sparse motion exports. Checks use bounded tessellation samples, not exact
continuous collision detection; local thin walls and small details remain limitations.
See [contact-set limits and recovery](docs/contact-sets.md).
## Assembly dragging
The design's first part is grounded when it is made, and a left-button drag repositions a free part (`occurrence.drag`: one undo step, exact occurrence paths including repeated jointed definitions, any floating-chain member carries the chain, grounded parts and joints hold where present, Escape cancels).
## 3D sketch bridges
3D sketch drawing snaps to points on other visible sketch planes without flattening them onto
the active plane. Bridge endpoints capture their source positions; associative endpoint links,
construction-plane selection of 3D wires and loft guide rails remain separate work.
Feature dialog header chevrons collapse and restore the form without discarding selections
or edited values, including the Appearance panel.
## Construction on 3D paths
Plane Along Path has a viewport path selector, fractional position, live preview and timeline
editing. Construction planes and points resolve drawn 3D wires without applying their sketch
plane a second time. Curved paths use sampled segments; multi-curve chains, absolute distance
and reference-object placement remain separate work, as do loft rails and centerlines.
## Design properties (#1015)
- Designs carry project, part number, revision, author, organization, description and licence
in an optional `metadata` object (File → Design Properties…, `document.metadata.*`,
`inspect_design`). STEP and IGES headers and 3MF model metadata take the fields their format
has a place for. Other tools keep their own data under a reverse-DNS key, kept as it is
through open, edit and save and never exported. See [design properties](docs/book/src/guide/files.md#design-properties).
Per-component properties (a part number per component) can follow with the same type.
## Document-opening performance
- Desktop STEP import prepares consecutive independent solids in bounded batches with up
to eight workers, retaining serial traversal, result order and persistent source identities.
The web path remains serial. Original multi-solid fixtures show 64–81% lower complete
import times; single-solid assembly parts are not scheduled across nodes. See
[measurements and limits](docs/perf.md#parallel-step-body-preparation-2026-10-12).
- Design containers (#303, phase one) separate and deduplicate STEP, raw images and
lossless binary mesh arrays while keeping plain JSON as a supported Save As/export
format. Generated-mesh storage measurements reduce serialization and decoding work;
a seekable reader defers original blobs until after provisional display.
See [container format](docs/design-container.md) and [performance measurements](docs/perf.md).
- Warm saves with explicit `cache: true` can include a versioned native B-rep cache through SolidCraft-owned topology
and geometry records, with a separate truck adapter and bounded binary reader. Exact
face-source certificates and operation history survive codec round trips. A provisional
picture can be generated before original blobs are read; editing and measurement wait
for authoritative reconstruction. Cached metadata binds preview and reconstruction to
the same document despite concurrent file writes. Default saves omit this cache and never force cold
evaluation or meshing; explicit `preview: true` remains a separate warm-mesh option.
Keys, resource budgets and document/revision guards reject incompatible, hostile or
stale results. A second modelling kernel still requires its own tested adapter.
- Warm saves with explicit `cache: true` can also carry a bounded PNG thumbnail from already cached meshes.
Home reads it without rebuilding the design when its local picture is missing;
mismatched or malformed thumbnails fall back to normal generation. The image is
kernel-independent and saving never forces cold meshing to produce it.
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 flags every command takes, at the end of the top-level usage.
constUSAGE_DEBUG: &str=" --debug, with any command: what SolidCraft logs (why a body has no display plan, why a save left its\n model cache out), on stderr; RUST_LOG=error|warn|info|debug|trace sets one level for every crate\n";
constUSAGE_DEBUG: &str=" --debug, with any command: what SolidCraft logs (why a body has no display plan, why a save left its\n model cache out), on stderr; RUST_LOG=error|warn|info|debug|trace sets one level for every crate\n --max-file-size SIZE, with any command: read STEP and import files and design files up to SIZE for this run\n (for example 2GB or 1500MB; at most 16GB; the defaults are 512 MB and 256 MB, and a smaller SIZE keeps them)\n";
/// The commands the CLI takes, in the order the top-level usage lists them.
"cmd.sketch_toggle_driven":"Skift mellem styrende og afledt mål",
"cmd.sketch_trim_path":"Trim langs bane",
@@ -1290,6 +1302,13 @@
"prefs.left_click":"Venstreklik",
"prefs.live_input":"Løbende input",
"prefs.live_preview":"Løbende forhåndsvisning",
"prefs.max_file_size":"Største fil, der kan åbnes",
"prefs.max_file_size_design":"Designfiler",
"prefs.max_file_size_flag":"Denne kørsel blev startet med --max-file-size {size}, som gælder, indtil SolidCraft lukkes. Disse indstillinger gælder ved start uden denne valgmulighed.",
"prefs.max_file_size_note":"Standard: 512 MB og 256 MB; højst 16 GB. Åbning af en fil kræver flere gange dens størrelse i hukommelse. Et design med en stor import kan kræve en højere grænse for designfiler for at kunne gemmes og åbnes igen.",
"prefs.max_file_size_step":"STEP- og importfiler",
"prefs.max_file_size_web":"Webappen åbner STEP- og importfiler op til 512 MB og design op til 256 MB: en browserfane kan ikke rumme større filer. Grænserne kan hæves i skrivebordsappen.",
"prefs.megabytes":"MB",
"prefs.minutes":"minutter",
"prefs.minutes_suffix":" min",
"prefs.mouse":"Mus og pegefelt",
@@ -1894,6 +1913,7 @@
"ui.constant":"Konstant",
"ui.constraints_conflict":"Bindinger er i konflikt",
"ui.part_size_needs_a_part":"Vælg en indsat standarddel (en skrue, møtrik, skive eller stift) for at ændre dens størrelse. Brug Skalering til at gøre et emne større eller mindre.",
"ui.part_size_shared":"Genopbygger delen og erstatter alle tilføjede features. Alle forekomster af komponenten opdateres.",
"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.selected":"Selected colour",
"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.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.theme_follow":"Follow a theme file",
"prefs.theme_follow_error":"Not applied: {error}. The last good theme stays on.",
"prefs.theme_follow_launch":"Followed for this run only (--theme-file); reloaded whenever another tool rewrites it.",
"prefs.theme_follow_note":"Reloaded whenever another tool rewrites it. Choosing a theme stops following it.",
"prefs.theme_follow_off":"Off",
"prefs.theme_follow_stop":"Stop following",
"prefs.trackpad_zoom_modifier.help":"Hold only this key to zoom at the pointer with vertical scroll. Off keeps the existing scroll behaviour.",
"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.",
@@ -52,6 +122,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",
@@ -80,6 +171,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",
@@ -98,18 +206,45 @@
"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",
"browser.calibrate_click_a":"Kliknite sliku da zadate tačku A ili upišite njene koordinate.",
"browser.calibrate_click_b":"Kliknite sliku da zadate tačku B ili upišite njene koordinate.",
"help.sketch.construction_line":"Napravite beskonačnu pomoćnu pravu kroz dvije tačke. Koristite je da rasporedite skicu bez razdvajanja profila.",
"help.sketch.construction_line.angle":"Napravite beskonačnu pomoćnu pravu pod uglom od ose X skice. Izaberite tačku i upišite ugao ili izaberite smjer.",
"help.sketch.construction_line.bisector":"Izaberite dvije prave linije skice koje se sijeku, pa jednu od dvije simetrale ugla. Rezultat prati izvorne linije.",
"help.sketch.construction_line.horizontal":"Napravite beskonačnu horizontalnu pomoćnu pravu kroz tačku. Njeno ograničenje horizontalnosti ostaje na snazi pri kasnijim izmjenama.",
"help.sketch.construction_line.parallel":"Izaberite pravu liniju skice, pa postavite paralelnu beskonačnu pomoćnu pravu. Desnim klikom izaberite drugu referencu.",
"help.sketch.construction_line.perpendicular":"Izaberite pravu liniju skice, pa postavite okomitu beskonačnu pomoćnu pravu. Desnim klikom izaberite drugu referencu.",
"help.sketch.construction_line.tangent_at":"Napravite tangentu u tački na krugu ili kružnom luku, ili u krajnjoj tački otvorenog splajna ili konike. Izaberite krivu, pa tačku dodira.",
"help.sketch.construction_line.tangent_external":"Izaberite krug ili kružni luk, pa vanjsku tačku i rješenje tangente. Pregledi prikazuju samo dodirne tačke na nacrtanom luku.",
"help.sketch.construction_line.tangent_two":"Izaberite dva kruga ili kružna luka, pa u pregledu izaberite zajedničku tangentu. Dodirne tačke moraju ležati na nacrtanim krivama.",
"help.sketch.construction_line.vertical":"Napravite beskonačnu vertikalnu pomoćnu pravu kroz tačku. Njeno ograničenje vertikalnosti ostaje na snazi pri kasnijim izmjenama.",
"prefs.persistent_ribbon_pressed":"Istakni aktivne komande na traci",
"prefs.persistent_ribbon_pressed_help":"Ističe komande na traci dok su njihovi efekti uključeni. Isključite da biste zadržali prethodni neutralni izgled i pomoć za komande.",
"prefs.sketch_construction_lines":"Prošireni alati za pomoćne prave",
"prefs.sketch_open_look_at":"Okreni kameru prema skici kad se otvori",
"prefs.sketch_open_look_at_note":"Isključeno: kad napravite ili uredite skicu, prikaz ostaje onakav kakav jest. Pogledaj okreće prikaz prema skici kad to želite.",
"recovery.discard_all":"Odbaci sve",
"recovery.discard_all_detail":"Ovo se ne može poništiti.",
"browser.calibrate_click_a":"Fai clic na imaxe para o punto A, ou escribe as súas coordenadas.",
"browser.calibrate_click_b":"Fai clic na imaxe para o punto B, ou escribe as súas coordenadas.",
"help.sketch.construction_line":"Crea unha recta de construción infinita por dous puntos. Úsaa para organizar un bosquexo sen dividir os perfís.",
"help.sketch.construction_line.angle":"Crea unha recta de construción infinita cun ángulo respecto do eixe X do bosquexo. Escolle o punto e escribe o ángulo, ou escolle a súa dirección.",
"help.sketch.construction_line.bisector":"Escolle dúas rectas do bosquexo que se crucen e despois unha das dúas bisectrices. O resultado segue as súas rectas de orixe.",
"help.sketch.construction_line.horizontal":"Crea unha recta de construción horizontal infinita por un punto. A súa restrición horizontal mantense nas edicións posteriores.",
"help.sketch.construction_line.parallel":"Escolle unha recta do bosquexo e despois coloca unha recta de construción infinita paralela. Fai clic co botón dereito para escoller outra referencia.",
"help.sketch.construction_line.perpendicular":"Escolle unha recta do bosquexo e despois coloca unha recta de construción infinita perpendicular. Fai clic co botón dereito para escoller outra referencia.",
"help.sketch.construction_line.tangent_at":"Crea unha tanxente nun punto dun círculo ou dun arco circular, ou no extremo dunha spline ou dunha cónica abertas. Escolle a curva e despois o seu punto de tanxencia.",
"help.sketch.construction_line.tangent_external":"Escolle un círculo ou un arco circular e despois un punto exterior e unha solución de tanxente. As vistas previas só mostran os puntos de tanxencia sobre o arco debuxado.",
"help.sketch.construction_line.tangent_two":"Escolle dous círculos ou arcos circulares e despois unha tanxente común na vista previa. Os puntos de tanxencia deben estar sobre as curvas debuxadas.",
"help.sketch.construction_line.vertical":"Crea unha recta de construción vertical infinita por un punto. A súa restrición vertical mantense nas edicións posteriores.",
"prefs.persistent_ribbon_pressed":"Resaltar as ordes activas da cinta",
"prefs.persistent_ribbon_pressed_help":"Resalta as ordes da cinta mentres os seus efectos están activados. Desactívao para manter o aspecto neutro e a axuda das ordes anteriores.",
"prefs.sketch_construction_lines":"Ferramentas ampliadas de rectas de construción",
"prefs.sketch_open_look_at":"Xirar para mirar cara ao bosquexo cando se abre",
"prefs.sketch_open_look_at_note":"Desactivado: crear ou editar un bosquexo deixa a vista tal e como está. Mirar cara a orienta a vista cara ao bosquexo cando o queres.",
"recovery.discard_all":"Descartar todo",
"recovery.discard_all_detail":"Esta acción non se pode desfacer.",
"ui.construction.angle_hint":"Recta de construción: fai clic nun punto e despois escribe un ángulo ou escolle unha dirección",
"ui.construction.bisector_hint":"Bisectriz: escolle dúas rectas que se crucen e despois a solución desexada",
"ui.construction.common_hint":"Tanxente común: escolle dous círculos/arcos e despois a solución desexada",
"ui.construction.contact_hint":"Tanxente: escolle un círculo/arco ou unha spline/cónica aberta e despois o punto de tanxencia (curvas de forma libre: extremos)",
"ui.construction.external_hint":"Tanxente: escolle un círculo ou arco, un punto exterior e despois a solución desexada",
"ui.construction.free_hint":"Recta de construción: fai clic en dous puntos; clic dereito para reiniciar",
"ui.construction.point_hint":"Recta de construción: fai clic no punto polo que debe pasar",
"ui.construction.reference_hint":"Recta de construción: escolle unha recta e despois un punto; clic dereito para cambiar a referencia"
"browser.calibrate_click_a":"Klik gambar untuk Titik A, atau ketik koordinatnya.",
"browser.calibrate_click_b":"Klik gambar untuk Titik B, atau ketik koordinatnya.",
"help.sketch.construction_line":"Buat garis bantu tak hingga melalui dua titik. Gunakan untuk menata sketsa tanpa memecah profil.",
"help.sketch.construction_line.angle":"Buat garis bantu tak hingga dengan sudut terhadap sumbu X sketsa. Pilih titiknya dan ketik sudutnya, atau pilih arahnya.",
"help.sketch.construction_line.bisector":"Pilih dua garis sketsa lurus yang berpotongan, lalu pilih salah satu garis bagi sudut. Hasilnya mengikuti garis sumbernya.",
"help.sketch.construction_line.horizontal":"Buat garis bantu horizontal tak hingga melalui sebuah titik. Batasan horizontalnya tetap berlaku pada pengeditan berikutnya.",
"help.sketch.construction_line.parallel":"Pilih sebuah garis sketsa lurus, lalu tempatkan garis bantu tak hingga yang sejajar. Klik kanan untuk memilih referensi lain.",
"help.sketch.construction_line.perpendicular":"Pilih sebuah garis sketsa lurus, lalu tempatkan garis bantu tak hingga yang tegak lurus. Klik kanan untuk memilih referensi lain.",
"help.sketch.construction_line.tangent_at":"Buat garis singgung pada sebuah titik di lingkaran atau busur lingkaran, atau pada titik ujung spline atau kurva konik yang terbuka. Pilih kurvanya, lalu titik singgungnya.",
"help.sketch.construction_line.tangent_external":"Pilih lingkaran atau busur lingkaran, lalu sebuah titik luar dan satu solusi garis singgung. Pratinjau hanya menampilkan titik singgung pada busur yang digambar.",
"help.sketch.construction_line.tangent_two":"Pilih dua lingkaran atau busur lingkaran, lalu pilih garis singgung persekutuan di pratinjau. Titik singgung harus berada pada kurva yang digambar.",
"help.sketch.construction_line.vertical":"Buat garis bantu vertikal tak hingga melalui sebuah titik. Batasan vertikalnya tetap berlaku pada pengeditan berikutnya.",
"prefs.persistent_ribbon_pressed":"Sorot perintah ribbon yang aktif",
"prefs.persistent_ribbon_pressed_help":"Sorot perintah ribbon selama efeknya aktif. Matikan untuk mempertahankan tampilan netral dan bantuan perintah sebelumnya.",
"prefs.sketch_construction_lines":"Alat garis bantu lanjutan",
"prefs.sketch_open_look_at":"Putar untuk menghadap sketsa saat dibuka",
"prefs.sketch_open_look_at_note":"Mati: membuat atau mengedit sketsa membiarkan tampilan apa adanya. Look At menghadapkan tampilan ke sketsa saat Anda menginginkannya.",
"recovery.discard_all":"Buang semua",
"recovery.discard_all_detail":"Aksi ini tidak dapat diurungkan.",
"ui.construction.angle_hint":"Garis bantu: klik sebuah titik, lalu ketik sudut atau pilih arah",
"ui.construction.bisector_hint":"Garis bagi sudut: pilih dua garis lurus yang berpotongan, lalu solusi yang diinginkan",
"ui.construction.common_hint":"Garis singgung persekutuan: pilih dua lingkaran/busur, lalu solusi yang diinginkan",
"ui.construction.contact_hint":"Garis singgung: pilih lingkaran/busur atau spline/kurva konik terbuka, lalu titik singgung (kurva bentuk bebas: titik ujung)",
"ui.construction.external_hint":"Garis singgung: pilih lingkaran atau busur, sebuah titik luar, lalu solusi yang diinginkan",
"ui.construction.free_hint":"Garis bantu: klik dua titik; klik kanan untuk mengulang",
"ui.construction.point_hint":"Garis bantu: klik titik yang akan dilalui",
"ui.construction.reference_hint":"Garis bantu: pilih garis lurus, lalu sebuah titik; klik kanan untuk mengganti referensi"
"browser.calibrate_click_a":"Klik imej untuk Titik A, atau taip koordinatnya.",
"browser.calibrate_click_b":"Klik imej untuk Titik B, atau taip koordinatnya.",
"help.sketch.construction_line":"Cipta garis binaan tak terhingga melalui dua titik. Gunakannya untuk menyusun lakaran tanpa memisahkan profil.",
"help.sketch.construction_line.angle":"Cipta garis binaan tak terhingga pada sudut daripada paksi X lakaran. Pilih titik dan taip sudutnya, atau pilih arahnya.",
"help.sketch.construction_line.bisector":"Pilih dua garis lakaran lurus yang bersilang, kemudian pilih salah satu pembahagi dua sudut. Hasilnya mengikut garis sumbernya.",
"help.sketch.construction_line.horizontal":"Cipta garis binaan mendatar tak terhingga melalui satu titik. Kekangan mendatarnya kekal apabila disunting kemudian.",
"help.sketch.construction_line.parallel":"Pilih garis lakaran lurus, kemudian letakkan garis binaan tak terhingga yang selari. Klik kanan untuk memilih rujukan lain.",
"help.sketch.construction_line.perpendicular":"Pilih garis lakaran lurus, kemudian letakkan garis binaan tak terhingga yang serenjang. Klik kanan untuk memilih rujukan lain.",
"help.sketch.construction_line.tangent_at":"Cipta tangen pada satu titik pada bulatan atau lengkok bulat, atau pada titik hujung splin atau lengkung kon yang terbuka. Pilih lengkung itu, kemudian titik sentuhnya.",
"help.sketch.construction_line.tangent_external":"Pilih bulatan atau lengkok bulat, kemudian satu titik luar dan satu penyelesaian tangen. Pratonton hanya menunjukkan titik sentuh pada lengkok yang dilukis.",
"help.sketch.construction_line.tangent_two":"Pilih dua bulatan atau lengkok bulat, kemudian pilih tangen sepunya dalam pratonton. Titik sentuh mesti terletak pada lengkung yang dilukis.",
"help.sketch.construction_line.vertical":"Cipta garis binaan menegak tak terhingga melalui satu titik. Kekangan menegaknya kekal apabila disunting kemudian.",
"prefs.persistent_ribbon_pressed":"Serlahkan arahan reben yang aktif",
"prefs.persistent_ribbon_pressed_help":"Serlahkan arahan reben selagi kesannya dihidupkan. Matikan untuk mengekalkan rupa neutral dan bantuan arahan yang terdahulu.",
"prefs.sketch_construction_lines":"Alat garis binaan lanjutan",
"prefs.sketch_open_look_at":"Putar menghadap lakaran apabila dibuka",
"prefs.sketch_open_look_at_note":"Mati: mencipta atau menyunting lakaran membiarkan pandangan seperti sedia ada. Pandang Tepat menghadapkan pandangan ke lakaran apabila anda mahu.",
"recovery.discard_all":"Buang semua",
"recovery.discard_all_detail":"Tindakan ini tidak boleh dibuat asal.",
"ui.construction.angle_hint":"Garis binaan: klik satu titik, kemudian taip sudut atau pilih arah",
"ui.construction.bisector_hint":"Pembahagi dua sudut: pilih dua garis lurus yang bersilang, kemudian penyelesaian yang dikehendaki",
"ui.construction.common_hint":"Tangen sepunya: pilih dua bulatan/lengkok, kemudian penyelesaian yang dikehendaki",
"ui.construction.contact_hint":"Tangen: pilih bulatan/lengkok atau splin/lengkung kon terbuka, kemudian titik sentuh (lengkung bentuk bebas: titik hujung)",
"ui.construction.external_hint":"Tangen: pilih bulatan atau lengkok, satu titik luar, kemudian penyelesaian yang dikehendaki",
"ui.construction.free_hint":"Garis binaan: klik dua titik; klik kanan untuk mula semula",
"ui.construction.point_hint":"Garis binaan: klik titik yang akan dilalui",
"ui.construction.reference_hint":"Garis binaan: pilih garis lurus, kemudian satu titik; klik kanan untuk menukar rujukan"
"browser.calibrate_click_a":"Klikk på bildet for punkt A, eller skriv inn koordinatene.",
"browser.calibrate_click_b":"Klikk på bildet for punkt B, eller skriv inn koordinatene.",
"help.sketch.construction_line":"Opprett en uendelig hjelpelinje gjennom to punkter. Bruk den til å legge opp en skisse uten å dele opp profilene.",
"help.sketch.construction_line.angle":"Opprett en uendelig hjelpelinje i en vinkel fra skissens X-akse. Velg punktet og skriv inn vinkelen, eller velg retningen.",
"help.sketch.construction_line.bisector":"Velg to rette skisselinjer som krysser hverandre, og velg deretter en av vinkelhalveringslinjene. Resultatet følger kildelinjene sine.",
"help.sketch.construction_line.horizontal":"Opprett en uendelig vannrett hjelpelinje gjennom et punkt. Den vannrette bindingen følger med ved senere endringer.",
"help.sketch.construction_line.parallel":"Velg en rett skisselinje, og plasser deretter en parallell uendelig hjelpelinje. Høyreklikk for å velge en annen referanse.",
"help.sketch.construction_line.perpendicular":"Velg en rett skisselinje, og plasser deretter en vinkelrett uendelig hjelpelinje. Høyreklikk for å velge en annen referanse.",
"help.sketch.construction_line.tangent_at":"Opprett en tangent i et punkt på en sirkel eller sirkelbue, eller i endepunktet på en åpen spline eller kjeglesnittkurve. Velg kurven, og deretter berøringspunktet.",
"help.sketch.construction_line.tangent_external":"Velg en sirkel eller sirkelbue, deretter et eksternt punkt og en tangentløsning. Forhåndsvisningene viser bare berøringspunkter på den tegnede buen.",
"help.sketch.construction_line.tangent_two":"Velg to sirkler eller sirkelbuer, og velg deretter en felles tangent i forhåndsvisningen. Berøringspunktene må ligge på de tegnede kurvene.",
"help.sketch.construction_line.vertical":"Opprett en uendelig loddrett hjelpelinje gjennom et punkt. Den loddrette bindingen følger med ved senere endringer.",
"prefs.persistent_ribbon_pressed":"Fremhev aktive kommandoer på båndet",
"prefs.persistent_ribbon_pressed_help":"Fremhev kommandoer på båndet mens effektene deres er slått på. Slå av for å beholde det forrige nøytrale utseendet og den forrige kommandohjelpen.",
"prefs.sketch_open_look_at":"Orienter mot skissen når den åpnes",
"prefs.sketch_open_look_at_note":"Av: å opprette eller redigere en skisse lar visningen stå som den er. Orienter mot vender visningen mot skissen når du vil.",
"recovery.discard_all":"Forkast alle",
"recovery.discard_all_detail":"Dette kan ikke angres.",
"ui.construction.angle_hint":"Hjelpelinje: klikk på et punkt, og skriv deretter inn en vinkel eller velg en retning",
"ui.construction.bisector_hint":"Vinkelhalveringslinje: velg to rette linjer som krysser hverandre, og deretter ønsket løsning",
"ui.construction.common_hint":"Felles tangent: velg to sirkler/buer, og deretter ønsket løsning",
"ui.construction.contact_hint":"Tangent: velg en sirkel/bue eller åpen spline/kjeglesnittkurve, og deretter berøringspunktet (frie kurver: endepunkter)",
"ui.construction.external_hint":"Tangent: velg en sirkel eller bue, et eksternt punkt, og deretter ønsket løsning",
"ui.construction.free_hint":"Hjelpelinje: klikk på to punkter; høyreklikk for å starte på nytt",
"ui.construction.point_hint":"Hjelpelinje: klikk på punktet linjen skal gå gjennom",
"ui.construction.reference_hint":"Hjelpelinje: velg en rett linje, og deretter et punkt; høyreklikk for å bytte referanse"
"browser.calibrate_click_a":"A Noktası için görüntüye tıklayın veya koordinatlarını yazın.",
"browser.calibrate_click_b":"B Noktası için görüntüye tıklayın veya koordinatlarını yazın.",
"help.sketch.construction_line":"İki noktadan geçen sonsuz bir yardımcı doğru oluşturun. Profilleri bölmeden bir eskizi kurgulamak için kullanın.",
"help.sketch.construction_line.angle":"Eskizin X ekseninden belirli bir açıyla sonsuz bir yardımcı doğru oluşturun. Noktayı seçip açıyı yazın veya yönünü seçin.",
"help.sketch.construction_line.bisector":"Kesişen iki düz eskiz çizgisini seçin, ardından iki açıortaydan birini seçin. Sonuç, kaynak çizgilerini izler.",
"help.sketch.construction_line.horizontal":"Bir noktadan geçen sonsuz yatay bir yardımcı doğru oluşturun. Yatay kısıtlaması sonraki düzenlemelerde de korunur.",
"help.sketch.construction_line.parallel":"Düz bir eskiz çizgisi seçin, ardından paralel bir sonsuz yardımcı doğru yerleştirin. Başka bir referans seçmek için sağ tıklayın.",
"help.sketch.construction_line.perpendicular":"Düz bir eskiz çizgisi seçin, ardından dik bir sonsuz yardımcı doğru yerleştirin. Başka bir referans seçmek için sağ tıklayın.",
"help.sketch.construction_line.tangent_at":"Bir daire veya dairesel yay üzerindeki bir noktada ya da açık bir spline veya konik eğrinin uç noktasında teğet oluşturun. Önce eğriyi, sonra değme noktasını seçin.",
"help.sketch.construction_line.tangent_external":"Bir daire veya dairesel yay seçin, ardından bir dış nokta ve bir teğet çözümü seçin. Önizlemeler yalnızca çizilen yay üzerindeki değme noktalarını gösterir.",
"help.sketch.construction_line.tangent_two":"İki daire veya dairesel yay seçin, ardından önizlemede ortak bir teğet seçin. Değme noktaları çizilen eğriler üzerinde olmalıdır.",
"help.sketch.construction_line.vertical":"Bir noktadan geçen sonsuz dikey bir yardımcı doğru oluşturun. Dikey kısıtlaması sonraki düzenlemelerde de korunur.",
"prefs.persistent_ribbon_pressed_help":"Şerit komutlarını, etkileri açık olduğu sürece vurgula. Önceki nötr görünümü ve komut yardımını korumak için kapatın.",
"prefs.sketch_construction_lines":"Genişletilmiş yardımcı doğru araçları",
"prefs.sketch_open_look_at":"Eskiz açıldığında görünümü ona doğru döndür",
"prefs.sketch_open_look_at_note":"Kapalı: eskiz oluşturmak veya düzenlemek görünümü olduğu gibi bırakır. Bak komutu, istediğinizde görünümü eskize çevirir.",
"recovery.discard_all":"Tümünü at",
"recovery.discard_all_detail":"Bu işlem geri alınamaz.",
"recovery.discard_all_title":"{count} otomatik kayıt atılsın mı?",
"recovery.discard_all_title_one":"1 otomatik kayıt atılsın mı?",
"ui.construction.angle_hint":"Yardımcı doğru: bir noktaya tıklayın, ardından bir açı yazın veya bir yön seçin",
"ui.construction.bisector_hint":"Açıortay: kesişen iki düz çizgiyi, ardından istenen çözümü seçin",
"ui.construction.common_hint":"Ortak teğet: iki daire/yay, ardından istenen çözümü seçin",
"ui.construction.contact_hint":"Teğet: bir daire/yay veya açık spline/konik eğri, ardından değme noktasını seçin (serbest biçimli eğriler: uç noktalar)",
"ui.construction.external_hint":"Teğet: bir daire veya yay, bir dış nokta, ardından istenen çözümü seçin",
"ui.construction.free_hint":"Yardımcı doğru: iki noktaya tıklayın; yeniden başlamak için sağ tıklayın",
"prefs.max_file_size_flag":"To uruchomienie używa --max-file-size {size} aż do zamknięcia SolidCraft. Te ustawienia obowiązują przy uruchomieniu bez tej opcji.",
"prefs.max_file_size_note":"Domyślnie: 512 MB i 256 MB; maksymalnie 16 GB. Otwarcie pliku wymaga pamięci kilkakrotnie większej od jego rozmiaru. Projekt z dużym importem może wymagać wyższego limitu pliku projektu, aby go zapisać i ponownie otworzyć.",
"prefs.max_file_size_step":"Pliki STEP i importu",
"prefs.max_file_size_web":"Aplikacja internetowa otwiera pliki STEP i importu do 512 MB oraz projekty do 256 MB: karta przeglądarki nie pomieści większych plików. W aplikacji komputerowej można zwiększyć te limity.",
"ui.part_size_needs_a_part":"Zaznacz wstawioną część znormalizowaną (śrubę, nakrętkę, podkładkę lub kołek), aby zmienić jej rozmiar. Aby powiększyć lub pomniejszyć bryłę, użyj polecenia Skala.",
"ui.part_size_shared":"Przebudowuje część, zastępując wszystkie dodane elementy. Wszystkie wystąpienia tego komponentu zostaną zaktualizowane.",
"cmd.sketch_trim_path":"Aparar ao longo do caminho",
@@ -1259,6 +1271,13 @@
"prefs.left_click":"Clique esquerdo",
"prefs.live_input":"Entrada ao vivo",
"prefs.live_preview":"Visualização ao vivo",
"prefs.max_file_size":"Tamanho máximo de arquivo a abrir",
"prefs.max_file_size_design":"Arquivos de projeto",
"prefs.max_file_size_flag":"Esta execução foi iniciada com --max-file-size {size}, que vale até fechar o SolidCraft. Estas configurações se aplicam ao iniciar sem essa opção.",
"prefs.max_file_size_note":"Padrões: 512 MB e 256 MB; máximo de 16 GB. Abrir um arquivo exige várias vezes seu tamanho em memória. Um projeto que mantém uma importação grande pode precisar de um limite maior para ser salvo e reaberto.",
"prefs.max_file_size_step":"Arquivos STEP e de importação",
"prefs.max_file_size_web":"O aplicativo web abre arquivos STEP e de importação de até 512 MB e projetos de até 256 MB: uma aba do navegador não comporta arquivos maiores. O aplicativo para desktop permite aumentar esses limites.",
"prefs.megabytes":"MB",
"prefs.minutes":"minutos",
"prefs.minutes_suffix":" min",
"prefs.mouse":"Mouse e trackpad",
@@ -1855,6 +1874,7 @@
"ui.constant":"Constante",
"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",
@@ -2152,6 +2172,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.",
"provenance":"Original SolidCraft mapping of Gruvbox (MIT palette)",
"format":"solidcraft-theme/1",
"name":"Gruvbox Dark Soft",
"base":"dark",
@@ -111,7 +113,7 @@
"form_dim":"#bdae93",
"form_primary":"#83a598",
"form_error":"#fb4934",
"form_error_fill":"#702b28",
"form_error_fill":"#5f2320",
"form_success":"#b8bb26",
"form_success_fill":"#343a28",
"dialog_body":"#32302f",
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.