Commit Graph
289 Commits
Author SHA1 Message Date
bherbruckandClaude Opus 5.5 dd67472252 Orbit from empty space turns around the visible geometry (#464)
With "Orbit around: Point under the cursor", a drag that starts over
empty space used to pivot on the point under the cursor at the target's
depth, so an orbit begun near the edge of the view swung the model
around an off-screen point. It now pivots on the centre of the visible
bodies and sketches (the same bounds Fit frames), or on the view centre
when nothing is visible. A drag that starts on the model is unchanged.


Claude-Session: https://claude.ai/code/session_01HWLPLy7ug7URSK5WybjRNa

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 21:01:49 -04:00
NOWN-DEVandClaude Opus 5.5 fb4c1d74b9 feat: edge flanges with their own angle and height per edge (#447)
One edge flange feature gave all its flanges one height and one angle. A
part whose flanges lean differently took one feature per angle, and
flanges of separate features don't close toward each other, so its
corners stayed open.

- sheet.flange (edge): an entry of `edges` is a point [x, y, z] as before,
  or {point, angle?, height?} whose angle and height (expressions, checked
  like the feature's) replace the feature's for that edge. Bad input is
  refused with the edge named. Before, the object form was read for its
  point and the rest was ignored without a word.
- The feature stores its edges as FlangeEdge. An edge without values of
  its own is written as the plain point it always was, so designs without
  them are read and written as before; an unknown key in an edge is
  refused, not dropped.
- The edges' values are feature inputs of their own (Edge angle, Edge
  height, after the feature's), so parameters, renames and
  parameters.change reach them. timeline.edit names the edges again when
  it changes them, so each value stays with its edge.
- SheetBody::add_flanges_each trims every edge by its own setback and
  adds the flanges. The corners between them close as between equal
  flanges: the closing already works from the two flanges it joins (each
  one's fold and leg), so the flat pattern follows by hand from the
  angles, the heights and the rule (the tests derive it).
- The Flange dialog shows a row per picked edge with an angle and a
  height box (empty: the dialog's value) once more than one edge is
  picked, and brings them back on Edit Feature.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 20:33:49 -04:00
selune ddd528abaf fix(ui): accept toolbar drops across panel labels and gaps (#454) 2026-10-12 20:25:38 -04:00
selune 5c7149e57d fix(ui): put Point on the default sketch toolbar (#445) 2026-10-12 20:13:31 -04:00
selune 006a23a767 Add design containers with deduplicated blobs and JSON compatibility (#327)
* Add fast design containers with lossless deduplicated payloads

* Keep display caching opt-in to avoid default save overhead
2026-10-12 19:15:41 -04:00
f290382498 fix(ui): wrap long toolbar panel labels and centre one-button panels (#409) (#410)
A panel label too wide for its panel (beside its caret) wraps between words
onto a second line; a single word wider than the panel stays whole. Each line
is centred, and the label's hover area grows to hold both lines. A panel's
buttons are centred in the panel, so a one-button panel such as Finish Sketch
lines its icon up with its label.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
2026-10-12 18:57:20 -04:00
deb96318a1 fix: fillet fit review follow-ups (#404)
* fix: fillet fit review follow-ups

From the review of #388 (merged before these landed):
- The Fillet dialog's Applied Radius showed nothing when editing a fillet
  that was already reduced: the preview found the radius by diffing the
  states before and after. The engine's preview now keeps the command
  replies, and the dialog reads the radius of the feature they name.
- Pattern copies of a reduced fillet try the radius the original was
  reduced to first, instead of each searching again (up to 16 fillets).
- fit: reduce with a chord or variable radius is refused instead of being
  silently ignored.
- One helper appends a note to a feature's warning; too_large is imported
  like unsupported in the loop and curve blends.

Refs #293

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TWc1smHYwTSVrVc8SNvS44

* Use the solidcraft_doc crate name after the rename

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HWLPLy7ug7URSK5WybjRNa

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
2026-10-12 18:57:09 -04:00
11ecb64059 feat(ui): heading bands for dialogs, sketch status label, tighter palette footer (#418)
- frame::heading draws a panel's title row (heading_bg) or a section heading
  (subheading_bg) on a band darker than the dialog, in both themes. Every
  docked command dialog, the Sketch Palette and Redefine Sketch Plane use it.
- Sketch Palette: "Options" and a new "Sketch Status" heading are section
  bands; the status line sits under its own heading.
- Sketch Palette: Finish Sketch no longer centres in the leftover height, so
  the palette ends one line below it, one line after the status.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
2026-10-12 18:55:57 -04:00
Tobias BreckleandClaude Opus 5.5 0bb5b7eb09 fix(ui): Sketch Palette line type buttons follow the selected curves (#422)
With sketch curves selected, the palette's Normal / Construction / Centerline
buttons now show the line type of the selection (each type of a mixed
selection); with nothing selected they show the drawing mode, as before.
Previously no button was highlighted while curves were selected.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 18:45:19 -04:00
bherbruckandClaude Opus 5.5 cddbfedb28 Remove the Generative Design workspace placeholder (#412)
It isn't planned, so it no longer appears in the workspace menu's
"coming later" list. Drops ui.generative_design from every locale and
checks the menu doesn't list it.


Claude-Session: https://claude.ai/code/session_01RjGw4gA5ZvKrU8MQP6sBgT

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 17:31:21 -04:00
bherbruckandClaude Opus 5.5 296683229d Rename SolveCraft to SolidCraft everywhere, with compatibility shims (#389)
* WIP deep rename (not for push)

* WIP2

* WIP3

* WIP4 wxs fix

* rustfmt

* Rename the remaining identifiers in merged crash-report code

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RjGw4gA5ZvKrU8MQP6sBgT

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 17:17:01 -04:00
27415e4dd7 Crash reports: replayable, bounded, local, reachable by agents (#384)
Every panic (in a command, in the kernel, anywhere else) and every unclean
exit (abort, stack overflow, kill) leaves a report in a local crash folder:
message, file:line, a trimmed backtrace, version, commit, system and renderer,
the command and recent commands, and when possible the design and replay.json
that reproduce it (solvecraft-cli crashes replay <id>).

- Cost: nothing until the app installs the hook (tests, CLI and web never
  do); then one small journal line per top-level command of the design on
  screen, taken back with a truncate when the command changed nothing.
- Unclean exits: each run holds a lock on its journal; a journal nobody holds
  at the next start belongs to an app that died. Autosaves restart the
  journal from the autosaved design.
- Bounded: a crash's signature (place + message, numbers ignored) is counted,
  not rewritten; at most 5 new reports per run, 20 reports, 50 MB, 90 days;
  recovered kernel panics are pruned first.
- Agents: diagnostics.crash_reports / crash_report / crash_reports.clear
  commands, the MCP crash_reports tool, solvecraft-cli crashes, solvecraft
  --crash-log; diagnostics.crash_test (with SOLVECRAFT_CRASH_TEST) checks it.
- People: the Crash Reports window opens after a crash (Help > Crash
  Reports…) with a reviewed issue draft per report; nothing is sent.
- Release builds keep line tables for file:line in backtraces.

Tests: child processes that really panic in a command, in the kernel, on a
thread, and abort; dedup counts, limits, hostile folders, journal parsing,
the UI window and draft, the MCP tool. docs/crash-reports.md.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
2026-10-12 16:50:49 -04:00
c62879b00d feat: fillet fit option reduces a radius too large for its edges (#388)
* feat(kernel): typed TooLarge refusal and fillet_max search

Blend refusals caused only by the size (larger than the neighbouring faces,
taller than the walls, longer than the next edge, larger than a corner
radius) now return KernelError::TooLarge, displayed as before. fillet_max
finds the largest radius below a request at which the fillet of the edges
works, to three significant digits, by building candidates; it is an explicit
call (up to 16 fillets), not run on every refusal.

A corner edge that doesn't run square to the edge is now reported as such
instead of as a blend larger than the neighbouring faces.

Refs #293

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TWc1smHYwTSVrVc8SNvS44

* feat: fillet fit option reduces a radius too large for its edges

solid.fillet takes fit: exact (the default, as before: a radius too large
for the geometry around the edges is refused) or reduce: the radius is
reduced to the largest that works for all the picked edges (kernel
fillet_max, three significant digits, every candidate built). The feature
stores the requested radius, so it refits on every evaluation; when it
reduces, the feature's warning says so and the command returns
radius_applied. Pattern copies of the fillet use the same rule. The file
format writes fit only when it is reduce.

The Fillet dialog gets a Radius Fit row (Exact / Reduce to Fit, constant
radius) and, when the preview reduced the radius, an Applied Radius line;
editing the feature keeps the choice.

One radius for all edges for now; per-edge radii and taper follow.

Refs #293

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TWc1smHYwTSVrVc8SNvS44

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
2026-10-12 16:21:17 -04:00
Konrad 3c94f59d43 feat(ui): trackpad pan with two fingers, zoom with pinch (#386)
Two-finger scroll pans the model instead of zooming; pinch-to-zoom
zooms toward the cursor and honors reverse zoom. Trackpad scrolls
arrive bracketed (Start..End, momentum included) while mouse wheels
send bare moves, so phases tell them apart: bracketed gestures pan,
bare vertical input zooms and bare horizontal input pans (thumb wheel).
Mouse line/page input keeps zooming, and ui.scroll now sends line
input so scripts and agents are unaffected. New Navigation preference
'Two-finger scroll pans' (default on) restores the old behaviour
when off.
2026-10-12 16:15:22 -04:00
Vitali M.andVitali M. 6efbd6ff14 Place a dimension that would over-constrain the sketch as a driven one (#366)
Sketch Dimension refused a dimension the sketch couldn't take, with
"that dimension would over-constrain the sketch; add it as a driven
(reference) dimension with driven: true", which the tool gave no way to
do. It now places it as a driven dimension instead: while placing, the
preview shows it in parentheses in the driven colour, and the click adds
it at once (it has no parameter and no value to edit), with a status line
saying why. Other refusals, such as an unsupported pick, are reported as
before, since the driven dimension is refused too.

Refs #320

Co-authored-by: Vitali M. <6042958+vitalibr@users.noreply.github.com>
2026-10-12 14:54:34 -04:00
Pablo López c51735c885 Toolbar drop-downs scroll when taller than the window (#360)
A panel drop-down taller than the window ran past its bottom edge, so the
last rows (Sketch → Create → Intersect and below) could not be reached. The
rows now scroll inside the window, and a row reached with the arrow keys
scrolls into view. Row ids no longer depend on the enclosing Ui, so the
keyboard focus still finds them inside the scroll area.
2026-10-12 14:38:27 -04:00
7db66bb824 Pick a horizontal or vertical dimension from where its text is placed (#345)
Between two points, or on a line, that don't line up with an axis, the
Sketch Dimension tool only offered the distance between them: an auto
dimension ignored where its text went. Now the placement picks what it
measures: across from the two (between the perpendiculars through them)
the distance between them, as before; past that, above or below them the
horizontal distance, beside them the vertical one. With no text, an
explicit type, or two ends already on an axis, nothing changes.

The tool previews the dimension from the cursor, so the choice follows
the pointer while placing it.

Fixes #257

Co-authored-by: Vitali M. <6042958+vitalibr@users.noreply.github.com>
Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 14:18:11 -04:00
Vitali M.andVitali M. b2fc6d1fac Keep a zoomed-in profile's hover fill: count the slabs each edge crosses (#336)
fill_triangles bounded the slab triangulator by edges x slabs, as if every
edge crossed every slab. A ring sampled finely at a close zoom has hundreds
of edges and slabs but only four edges in each slab, so the estimate ran a
hundred times over the hover highlight's budget and the fill was dropped,
leaving only the outline. Count the slabs each edge actually spans instead.

Region2::triangulate tested every edge against every slab, so its work was
the overestimate. It now sweeps upward with the edges crossing the current
slab, giving the same triangles in time proportional to them.

Fixes #164

Co-authored-by: Vitali M. <6042958+vitalibr@users.noreply.github.com>
2026-10-12 13:47:11 -04:00
Tobias BreckleandClaude Opus 5.5 e6a95615a0 fix(ui): start with an anti-aliasing the graphics device can't do (#329)
Choosing 8x anti-aliasing in Preferences › Graphics on a device that
only allows 1x and 4x for the window's format made every later start
panic in wgpu's pipeline validation (`Sample count 8 is not supported
by format Bgra8Unorm on this device`), so the app could not be opened
again without editing graphics.json by hand.

- egui-wgpu (vendored): request TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES
  when the adapter has it (WebGPU only guarantees 1x and 4x), and clamp
  the sample count to the largest one the device supports for both the
  colour target and the depth/stencil format. The supported counts are
  exposed as `RenderState::msaa_supported`.
- At start, a saved count the device can't do goes back to the default
  (4x, or the largest supported count below it), is written back, and a
  "Settings were changed" notice says what changed.
- The Anti-aliasing list greys out unsupported counts, `ui.prefs`
  rejects them, and an invalid saved `msaa` loads as the default.
- New messages translated in en, de, ja and ko.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 13:46:54 -04:00
palaniraja 6dab0fb2c1 openscad navigation (#211)
* openscad navigation

* addressed pr feedback: fixing box selection conflict with openscad and use constant for openscad
2026-10-12 13:46:40 -04:00
Iraklis KourtisandClaude Opus 5.5 fe4d171e22 feat(sketch): live drag that keeps its solution, ended by sketch.drag_end (#341)
* feat(sketch): constructive solving ahead of the numeric solver

Part 1 of splitting #251: the sketch crate only. A graph-constructive front
end (Owen 1991; Fudos & Hoffmann 1997) places what it can by ruler-and-compass
loci and cluster decomposition, Levenberg-Marquardt finishes and verifies the
rest, and the rank analysis stays authoritative for degrees of freedom. Solves
can follow a reference (the previous solution), so edits and drags keep their
branch instead of flipping.

New API: solve_with (hold, reference, analysis, budget) and
solve_after_value_edit; solve and solve_holding keep their signatures as
wrappers, so no caller changes here.

When the seed moves an arc's centre, the arc's unconstructed ends start carried
along (the arc translates) instead of left behind for LM to turn; found by the
reference test 20-slot-constrained.

Refs #203

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TWc1smHYwTSVrVc8SNvS44

* feat(doc): keep a sketch's solved state across value edits

Part 2 of splitting #251 (builds on part 1, the sketch crate):
- Sketch edits solve for the document's values continuing from the stored
  solved state (Document::solve_sketch), so a dimension edit keeps the
  sketch's branch instead of re-solving from scratch.
- A value edit (parameters, a configuration, the units) that would make a
  sketch that solved before fail or turn over is refused with the
  conflicting constraints named; otherwise the solved sketches are written
  back into the document (Model::adopt_solved_sketches), which changes what
  saved designs store.
- Later features are keyed by a sketch's solved geometry (to a 1e-6 mm
  quantum), so setting a value back reuses their earlier results.
- Solved sketches in a model state are shared (Arc) between states.

Refs #203

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TWc1smHYwTSVrVc8SNvS44

* feat(sketch): live drag that keeps its solution, ended by sketch.drag_end

Part 3 of splitting #251 (builds on part 2):
- sketch.drag follows a long move in short held steps, so the geometry keeps
  its branch, and reports how far it got (reached, at).
- live: true runs one frame of an interactive drag: the sketch is solved
  once and shown, later features keep their last result (marked stale)
  until sketch.drag_end or any command that reads the model evaluates it
  (CommandSpec::live, reads_model, doc_only).
- sketch.drag_end adds the constraint the drag let go on and evaluates the
  model once.
- Undo and redo evaluate a new document once (execute does) instead of
  twice.
- The viewport drives drags through live frames and drag_end; dimension
  text drags start only off sketch geometry.

Refs #203

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TWc1smHYwTSVrVc8SNvS44

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 13:45:48 -04:00
Pablo López fc8a5b21cf Loop blends: accept straight wall seams that are not lines (#335)
* Loop blends accept straight wall seams that are not lines

Filleting or chamfering the top outline of two overlapping cylinders whose
concave edges were rounded failed with "the wall seams must be straight":
the round's contact lines with the cylinders are straight NURBS curves, and
the loop blend only accepted seams stored as lines. A seam now counts as
straight when every sample lies on its chord; it is trimmed as a line as
before.

* Fillet and chamfer arrows point into the body

The radius arrow pointed out of the body along the edge's bisector, so
dragging it away from the edge grew the round. It now points into the
material, like the shell's thickness arrow: dragging toward the body grows
the radius or distance, dragging out of it shrinks it.
2026-10-12 13:15:30 -04:00
bherbruckandClaude Opus 5.5 198f45edee Toolbar: no cap on pinned buttons (Fixes #323) (#326)
* Toolbar: no cap on pinned buttons (Fixes #323)

Every pinned command gets a toolbar button; `promoted()` is only the
default layout of a fresh or reset toolbar. A toolbar too narrow for every
button now collapses panels into their drop-down (one button that opens it,
listing every command, pinned ones included) from the right, then moves the
last panels into the » menu, instead of pushing a wide panel and everything
after it into ». Nothing is dropped.

Scenario s48 pins ten commands into SOLID > CREATE: all ten show at a wide
window, a narrow one collapses CREATE into its drop-down (all ten listed),
narrower still puts it in », and Reset Toolbar restores the defaults.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RjGw4gA5ZvKrU8MQP6sBgT

* Narrow-toolbar test: panels collapse into their drop-down before »

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RjGw4gA5ZvKrU8MQP6sBgT

* kernel: drop a needless borrow that fails clippy -D warnings on the current stable

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RjGw4gA5ZvKrU8MQP6sBgT

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 12:35:19 -04:00
Tobias BreckleandClaude Opus 5.5 06e517eb4e Home: turn off commands that act on the hidden design (#322)
The Home page hides the open design, yet Save, Undo, Redo, File › Insert,
Save As and Export, and the design's keyboard shortcuts (Ctrl+S/Z/Y,
Delete, V, F2, F6, Esc, S and tool keys such as E) still acted on it:
Delete removed the selected body unseen, Save wrote a design the user
could not see, and tool keys left Home without being asked to.

On Home the top bar's Save, Undo and Redo are dimmed with a translated
"open a design first" tooltip, the File menu's design items are greyed
out, and only New, Open and Preferences keep their shortcuts. Everything
works as before once Home is closed.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 11:44:23 -04:00
4d4b3a715f Create construction planes along drawn 3D sketch paths (#240)
Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 11:44:19 -04:00
Vitali M.andVitali M. 516d80ae8d Clip lines at the near plane so the ground grid stays visible at low angles (#305)
A line whose end lies behind the eye has no screen point: vs_line divided
by a non-positive w, which flipped the segment or dropped it whole. At a
low perspective angle the grid lines running along the view direction
reach behind the camera and vanished. Cut each segment at clip z = 0
before the screen-space expansion.

Fixes #182

Co-authored-by: Vitali M. <6042958+vitalibr@users.noreply.github.com>
2026-10-12 11:44:11 -04:00
Tobias BreckleandClaude Opus 5.5 250d8a2c04 Preferences open over the Home page (#313)
The Home page returned from the frame before the Preferences window was
drawn, so the gear, Ctrl+, and Home's Preferences / Keyboard Shortcuts
links only set the flag and the dialog appeared after leaving Home.
Draw the Preferences window on Home too.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 11:44:07 -04:00
e6e0a7ebb5 Snap 3D bridge lines to points on other sketch planes (#229)
* Snap 3D bridge drawing to points on visible sketch planes

* Keep cross-sketch point picks out of sketch tools

`pick()` returned every visible sketch's points while 3D drawing was on,
so `sketch_point_at` and the sketch tools could take another sketch's
point as an active-sketch local point plus id (flattening it or making a
bad coincident reference). `pick()` is local again; the bridge snap uses
`pick_3d` explicitly and hover mirrors it for feedback. `sketch_point_at`
also ignores foreign points defensively.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(sketch3d): check local point scope with a sketch tool active

Main's Project preselection fix now offers other sketches' points to idle
selection, so assert the local scope where it still applies.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style: rustfmt

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>
2026-10-12 11:25:39 -04:00
bherbruckandClaude Opus 5.5 3b8028a040 Retire legacy reference data and the external command catalog (#302)
- Remove xtask/data/fusion-catalog.tsv and xtask/data/fusion-ids.tsv.
- Remove the legacy command-id table (crates/engine/src/legacy_ids.rs) and its use in
  find_command and in the shortcut, recents and pinned-command loaders. Only SolidCraft's
  dotted ids name commands; any other id is an unknown command.
- Boundary Fill's id becomes solid.boundary_fill.
- Replace the parity report with SolidCraft's own feature checklist: `cargo xtask features`
  writes docs/features.md from the command registry.
- docs/oracle.md, docs/step-import.md (corpus section) and docs/iges-import.md: reference
  results are being rebuilt on analytic and open-source (FreeCAD) values; see #292.
  `cargo xtask oracle` and `cargo xtask step-corpus [--iges]` write these placeholders.
- AGENTS.md, CLAUDE.md, CONTRIBUTING.md, README.md, ROADMAP.md and the book describe
  SolidCraft as independent, with reference tests on analytic values and open-source tools,
  and agents do not drive or extract data from commercial CAD products.


Claude-Session: https://claude.ai/code/session_01RjGw4gA5ZvKrU8MQP6sBgT

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 11:19:15 -04:00
db6ecaf446 Add Korean translation (#234)
* Create ko.json

* Update ko.json

* Update i18n.rs

* Fonts: add a Hangul fallback so Korean doesn't render as boxes

Only the first CJK font found was loaded, and on Windows and macOS that is
a Japanese font without Hangul (Meiryo, Hiragino). Load the first readable
font of each group: the existing CJK list, then Malgun Gothic (Windows),
Apple SD Gothic Neo (macOS) or Nanum Gothic (Linux). Also rustfmt the
Korean catalog test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RjGw4gA5ZvKrU8MQP6sBgT

---------

Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 11:06:03 -04:00
bherbruckandClaude Opus 5.5 611422e4df Alias navigation preset uses Shift+Alt with LMB/MMB/RMB (Fixes #304) (#306)
Navigation presets are now a table of bindings (button, required modifier
keys, camera mode) instead of string matches. The Alias preset binds
Shift+Alt+left to orbit, Shift+Alt+middle to pan and Shift+Alt+right to
zoom. A left press with a binding's keys is latched until release, so that
drag neither box-selects nor drags sketch geometry; plain left drags behave
as before. Shift only requests snapping when the binding without Shift
still orbits.

The scenario harness sends Alt with ui.drag/ui.click, and camera checks can
assert panned/zoomed since the last camera step. The user guide notes Alt+drag
grabs by KDE and GNOME and how to free them.


Claude-Session: https://claude.ai/code/session_01RjGw4gA5ZvKrU8MQP6sBgT

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 10:33:53 -04:00
bherbruckandClaude Opus 5.5 2cac9cad6a Toolbar: drag commands from drop-downs onto the toolbar; pin icon; Customize Toolbar… (#295)
* Toolbar drop-downs highlight the row under the pointer and the keyboard focus (#288)

A panel drop-down's command rows are full-width rows that take the theme's hover background
(icon, label and shortcut on it) when hovered or focused. Up/Down move the focus through the
enabled rows, Enter starts the command, Escape closes the drop-down. Rows publish
`dropdown:<id>` and the highlighted one `dropdown_hot:<id>`; scenario s46 checks pointer and
keyboard highlight.

Fixes #288

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RjGw4gA5ZvKrU8MQP6sBgT

* Toolbar: drag commands from drop-downs onto the toolbar; pin icon; Customize Toolbar… (#289)

- A panel drop-down row drags onto the toolbar (a button or a panel's free space, with the
  drop line) and is pinned there; the dragged command's icon follows the pointer.
- A toolbar button dragged off the toolbar, or back into the open drop-down, leaves it (its
  icon shows a cross while releasing would remove it).
- A hovered drop-down row shows a pin at its end: click to pin or unpin.
- Right-click on the toolbar's empty space: Customize Toolbar… (a window listing the tab's
  commands with check boxes, and Reset Toolbar) and Reset Toolbar; command menus have
  Customize Toolbar… too.
- Still per workspace and tab, kept with the preferences. Toolbar buttons also publish
  `button:<id>`; scenario s47 drags a row to the toolbar, unpins by dragging off and into the
  drop-down, uses the pin icon and Customize Toolbar…, and checks it survives a restart.

Fixes #289

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RjGw4gA5ZvKrU8MQP6sBgT

* Toolbar drop-down pin: a push pin drawn in code (upright and filled when pinned); control protocol lists the toolbar handles

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RjGw4gA5ZvKrU8MQP6sBgT

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 10:27:25 -04:00
f77da83c93 Edit sketch text content and placement from its outlines (#253)
* Expose interactive editing and placement of sketch text

* Respect document units when editing sketch text

* Route Edit Text through the shared feature dialog

The sketch text editor is now a Kind::Text dialog opened through
Dialog::for_command (frame.rs docking, standard OK/Cancel, Enter/Esc).
Its fields are ordinary dialog fields: they take expressions, parameters
and units, and bare lengths are in the design's units. Untouched fields
keep their exact saved values. The dialog stays open across unrelated
design changes and closes only when its text is gone, the sketch ends,
or another command starts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RjGw4gA5ZvKrU8MQP6sBgT

---------

Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 10:08:15 -04:00
62847173f4 Honor design units in sketch dimensions and measurements (#241)
* Honor design units in sketch dimensions and measurements

* fix: apply document units to specialized length controls

* Accept explicit units in length fields; document design-unit dimension values

Length fields in a design-unit control now evaluate what is typed as a
length expression: a bare number stays in the design's units, while an
explicit unit ("10 mm", "0.5 in", "1 in + 5 mm") is converted.
Document that a bare numeric sketch.dimension value from scripts, the
control channel or MCP is read in the design's units.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RjGw4gA5ZvKrU8MQP6sBgT

---------

Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 10:08:11 -04:00
bherbruckandClaude Opus 5.5 df09fdbc32 Preferences: one fixed size, buttons flush right, a real gear icon (#290)
- The dialog draws every page into a fixed, clipped 744x400 area, so no page
  (or translation) changes the window width; descriptions wrap and tall pages
  scroll inside it.
- OK / Cancel / Apply sit flush right in the bottom bar (right-to-left layout
  within the fixed width); Restore Defaults stays left.
- The "settings" icon is a gear: a solid ring with six square-ish teeth and a
  see-through centre hole, instead of the sun-like rays.
- Scenario s46 visits every page and checks the window keeps its width and
  that Apply ends at the bottom bar's right edge (new `same_width` and
  `right_edge` scenario checks).

Fixes #290

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 10:07:23 -04:00
bherbruckandClaude Opus 5.5 0a526c6f85 fix(i18n): keep saved language on font failure, tighten CJK font matching
- Web: a failed CJK font fetch shows English for this session only; the
  saved language preference is kept (CjkFontLoader, persisted on save).
- Web fetches the CJK font only when a CJK language (ja) is selected.
- fonts.rs: directory scans match CJK-specific names only (no "noto" or
  "gothic"); CJK stays the last fallback after the Latin UI font.
- Revert unrelated clippy rewrites in component_clone.rs, coplanar.rs,
  canvas.rs and issue_report.rs to match main.
- Tests: default English, fallback order, CJK-only matching, fetch only
  for CJK, failed fetch keeps the saved language.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RjGw4gA5ZvKrU8MQP6sBgT
2026-10-12 10:06:50 -04:00
Mithril-3d 1fee62dd54 feat: add web CJK font loading and robust cross-platform font detection 2026-10-12 10:06:50 -04:00
lillith df4c8a2994 Reconstruct constant-section meshes as parametric solids 2026-10-12 10:06:42 -04:00
lucas-aguilar89andClaude Opus 5.5 8eda982489 Pattern and Mirror dialogs copy picked bodies, fillets included (#193)
With Object Type = Bodies, the Mirror, Rectangular Pattern and Circular
Pattern dialogs sent the feature that created each picked body
({"features": ["Extrude1"]}), so the copies replayed only that feature and
lost everything done to the body afterwards (a fillet, a chamfer, a cut).
They now send the bodies themselves ({"bodies": [...]}), which the commands
already support and the Path Pattern dialog already did. Picking Features
is unchanged.

Editing a body Mirror or Pattern from the timeline now reopens the dialog
with its bodies selected.

Copies made this way take the body-copy names ("Body1 (2)"), as the
commands already give; pattern_direction_edge now looks up "Body1 (3)".

Tests: UI scenarios mirror_body_keeps_fillets and pattern_body_keeps_fillets
(filleted cube, copy and edit through the dialogs); both fail without the
fix with the copies' sharp 8000 mm3 volume.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 10:06:18 -04:00
lucas-aguilar89andClaude Opus 5.5 cab698bf12 Offset Plane from a planar body face (#167)
construct.plane.offset now takes a planar face as its base
({"face": [x,y,z], body?, index?, path?}), captured with the same exact
face attachment a sketch on that face uses. The Offset Plane dialog accepts
planar faces as well as origin and construction planes.

A construction plane on a face is resolved through its face anchor when the
timeline is evaluated, so it follows the face when upstream features change
(and after reopening), instead of keeping the plane as it was when picked.

Tests: engine (top and side faces, follows a box resize, survives reopen,
hostile picks are refused without adding a feature) and a UI scenario that
picks a face in the dialog and builds a pad on the new plane.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 10:06:10 -04:00
lillith 874315db8e fix(sketch): apply Project to complete preselection 2026-10-12 10:05:52 -04:00
lillith 8c13914d6c Ignore repeated initial sketch points while drawing 2026-10-12 09:30:18 -04:00
lillith 732447d2dc Fix dialog header chevrons to collapse and restore forms 2026-10-12 09:08:31 -04:00
lillith db0a2a007a Apply remaining sketch constraints to complete preselection 2026-10-12 08:48:05 -04:00
ich777 53b092d287 feat: add German (de) UI localization
Adds German (Deutsch) as a third UI language, following the procedure outlined in docs/localization.md
2026-10-12 08:10:14 +02:00
bherbruck ff9653a353 Merge current main into wheel zoom smoothing option 2026-10-11 20:36:16 -04:00
bherbruck 400f200efa Speed up cold document geometry rebuilds 2026-10-11 20:22:19 -04:00
bherbruck 6dd202d277 Fix desktop launch, app icons and keyboard parameter editing 2026-10-11 19:09:47 -04:00
lillith 16bf3ba593 feat(ui): allow disabling wheel zoom smoothing 2026-10-12 01:07:23 +02:00
lillith 8601e38d4b fix(ui): restore free Shift-middle orbit 2026-10-12 00:30:05 +02:00