* claim: #5641
* Cone primitive: solid.cone with base and top radius (#5641)
A cone or truncated cone placed like the cylinder: base centre, axis,
base radius (or diameter), top radius (0, the default, is a full cone)
and height, with the new body / join / cut / intersect choice. It is
built from what the kernel already exposes, a revolve of its half
section about +Z through the origin, moved into place. Toolbar entry
after Torus, a Cone dialog that also places it on a picked face, Edit
Feature, a timeline icon, pending UI strings and attribution, and one
pending design-format change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Browser: surface bodies get the surface icon in the Bodies folder (#4165)
Every row in the Bodies folder had the solid body icon, so a surface (an
open shell, such as a Surface Revolve of an open curve) looked like a
solid. A body whose shell does not close now shows the existing surface
icon; solids keep the body icon.
Body::is_surface walks every shell's edges, so ModelBody caches it once per
body (shared by clones, like outline_bounds) instead of the browser
checking it every frame.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Add the surface icon to the requested-icons manifest
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>
* feat: add colored components inspection toggle
* feat: persist component colors and add cycling controls
* feat: assign component colors in creation order
* fix: update component color menu integration and translations
* fix: proper description for pending format change.
* fix: deleted duplicated format change description from docs/pending.
* Compare the component colour counter in the features-only check
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Vadim Kalinsky <vadim@kalinsky.com>
Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #976
* One set of X, Y and Z colours for every axis (#976)
The origin axes, the navigation cube's triad, 3D sketch triads and joint
origins drew `axis_x/y/z` (255,0,0 / 0,255,0 / 2,2,248), the view triad and
the headless picture drew `triad` (220,60,60 / 60,170,60 / 60,100,230) and
the Move gizmo drew `gizmo` (225,70,60 / 80,190,80 / 70,130,240). The
`triad` and `gizmo` tokens are gone: every axis reads `axis_x/y/z`, now the
view triad's values (the engine's `view::colors::AXIS_*`, so headless
pictures are unchanged, and the set the red-green table was measured for).
Older theme files' `triad` and `gizmo` lists still read and set the axes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #4094
* Sheet metal: Path Flange, a chain of bends on an existing sheet edge (#4094)
Path Flange (`sheet.path_flange`): sketch an open chain of lines across a sheet edge, pick the
edge and one line; each line becomes a flange as long as the line and each corner a bend, one
feature and one body. The edge flange takes further bends (`then`), each on the far edge of the
one before, and the Flange dialog shows them as rows with a bin each.
Placing a Single hole put it exactly where the click landed, with nothing to line it up by and no
way to move it but taking it out and picking again; the dialog was a list of size fields.
- Preferences › Design › Create Hole has two settings, both off by default, so a new installation
places holes as before:
- Snap to face and edge centres (`hole_snaps`): the planar face under the cursor offers its
middle and the centres of its round edges (a boss, a rounded corner, a hole already there) as
marks; a pick near one, or on or beside such an edge, takes that centre and the mark is named.
Sketch points on the face still come first; Alt or Ctrl held places it where clicked.
- Move holes by clicking or dragging (`hole_moves`): each placed hole has a handle that drags it
(snapping when snaps are on) and takes it out when clicked, and a pick elsewhere on its face
moves it there. Edit Feature shows the handle too, so a hole can be moved later (#1047).
- The Hole dialog draws a section of the hole as its options make it, with each size in a field
beside the dimension it sets; a hovered or focused field lights its dimension. The layout is a
new kit element, `ui_kit::figure_rows` (a caller-painted figure beside captioned fields).
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #4922
* UI: let themes own corner radii
* UI: use four-point panel and window radii in SolidCraft 95
* UI: align the 95 radius assertion with four-point panels
* claim: #4953
* UI: let themes own structural and control borders
* UI: single Blueprint outlines and dotted secondary rules
* UI: separate container outlines and add theme-owned 95 bevels
* claim: #5007
* UI: apply theme palettes to icons, workspace tabs and Gruvbox settings
* claim: #5045
* Viewport: invalidate cached geometry colours when the theme palette changes
* UI: preserve legacy theme logo colours with the borderless mark
* claim: #3838
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SBN24y2TcqRcm2zn8Gkj27
* Cancel Sketch: leave a sketch without keeping its changes (#3838)
sketch.cancel (Cancel Sketch, ribbon Exit group and the sketch's
right-click menu) puts the design back as it was when the open sketch
was created or opened for editing: a new sketch is removed with its
dimension parameters, an edited one returns to how it was. It is one
undo step, so Undo goes back into the sketch.
The session remembers the design and the undo history at the point a
sketch is entered. Cancel uses that only while the history still runs
through it (the same steps before, every step since taken in this
sketch); otherwise it refuses and points to Undo, so a stale entry can
never throw away later work.
The icon is the approved close glyph in the sketch accent (alias
sketch_cancel); ja and zh carry the new label.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SBN24y2TcqRcm2zn8Gkj27
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #4003
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Frames: the profile library (#4003)
The first slice of frames and weldments: the cross-sections structural members
will be made of, each SolidCraft's own parametric definition built in code
(no vendor or standard size tables are shipped).
- solidcraft_doc::frame: bar, round bar, rectangular and round tube, angle,
channel, I-beam and a generic T-slot extrusion as exact line/arc regions,
with defaults worked out from the profile's size, refusals that name the
dimension that does not fit, anchors and placement, and exact section
properties (area, centroid, Ixx/Iyy/Ixy by Green's theorem).
- frame.profile.list, frame.profile.properties (with mass per metre for a
density) and frame.profile.insert (draws a profile into a sketch, locked by
default, at a point, anchor and angle).
- Tests against hand-computed values (rectangles, circles, composite
rectangles by the parallel-axis theorem, fillet spandrels), hostile input,
extrusion volume, undo; catalog entries in every locale; docs/frames.md;
ROADMAP M15.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Frames: drop the built-in T-slot profile (#4003)
Aluminium extrusions and other vendor sections come from the user's own
sketch (drawn or inserted from the vendor's DXF), so the library keeps the
plain steel shapes only.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Frames: members along frame sketch lines (#4003)
frame.member lays one body per straight line of a frame sketch (2D lines or
3D sketch lines), its cross-section square to the line from end to end:
- profile: a built-in profile, or profile_sketch, a sketch whose material
(nested loops alternating, so two circles make a tube) is the section in
its own x and y around its origin; profiles picks override it.
- anchor (origin, centre, centroid or a bounding-box corner or edge middle),
angle (an angle expression, so a parameter), mirror.
- Orientation: the profile's y as near world up as the line allows, x
square to both; a vertical member's x is world X (frame::member_axes).
- Members follow sketch and parameter edits, undo as one step, save and
reopen (format: pending/frame-members.rs), and are named after the profile
(Rect Tube 40x40x3, (2)…), numbering on past names already taken.
Tests against hand-computed volumes and bounding boxes (rectangle, 3D
tower lines, parameter edit, sketch tube profile and picks, anchor/angle/
mirror), refusals, undo/redo and reopen; walk fixtures and goldens; docs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Frames: fixes from the adversarial review (#4003)
- Default member lines are what was drawn: projected and other linked curves
and 3D wires (a face sketch's edges) and lines of no length are left out.
- A profile sketch's material starts from its drawn profiles, as
solid.extrude's "all" does, so a section sketched on a face works; the
even-odd test uses outlines and boxes worked out once, capped at 256.
- Member names are pinned to their lines (`names`, by line id; `body_name`
is the base): deleting or adding a line, or deleting another feature, no
longer renames other members, and frame.member stores every name it gives.
body.rename renames a member on its line (also for later member features);
a copy whose body_names were set still names its members by position.
- Built-in profile dimensions are expressions (ProfileSpec): they follow
parameters, bare numbers take the design's units, and they are feature
inputs. frame.profile.properties/insert read them the same way.
- Default root/toe radii shrink to the room a thick section leaves; radii
given are checked by name; a radius must be 0 or at least 0.01 mm; error
messages report the wall or flange value given, not twice it.
- A line within 1e-6 rad of vertical orients as vertical, so rounding never
turns a post a quarter turn.
- A 2D frame sketch in a differently placed component is mapped into the
member's frame, and member features recompute when placements change.
- Caps before lookups for a design's `lines`; a line listed twice is
refused; a 3D wire that runs back on itself is not a line.
Regression tests for each, from the review's scenarios.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Frames: the Frame Member dialog on the ribbon (#4003)
- Frame Member (MODEL > Create) opens a dialog: the frame sketch's lines
picked in the view (or All Lines), a built-in profile and its dimensions or
a section picked in another sketch, anchor, angle, mirror and name, with a
live preview. Edit Feature reopens it filled in.
- Lines picked in the view keep their sketch (a SKETCH_LINES input picks
ExternalSketchCurve): line ids are each sketch's own, so two 3D sketches
both have a `w2`, and the members went on the newer sketch's lines.
- timeline.redefine takes frame.member. Members keep their names through an
edit unless it gives them a new body_name; frame.member takes `names`.
- A member whose section is a whole profile sketch keeps it when edited
(the dialog shows the sketch instead of picking every region, which would
fill a tube's hole).
- An original frame_member icon, open for review, on the ribbon, timeline
and browser.
- The angle profile is listed as L-Angle, and web and flange as Web/Flange
thickness, so they translate apart from the existing Angle, Web and Flange;
the dialog's labels are in en, ja and zh. en-GB no longer repeats the frame
command labels it spells the same.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #5461
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* App icon: draw the borderless mark everywhere (#5461)
app_icon.rs drew the older mark with white lines between the faces and a
white outline around it, so the Windows, Linux, web and README icons did
not match the macOS icon, the social card or the title bar. It now draws
the borderless mark from the faces and gradients of the macOS artwork
(03-solid-blue-cutout.svg): five faces with transparent gaps between them.
The filler fills even-odd spans so the concave faces work.
The Linux hicolor PNGs and both Windows .ico files are regenerated with
`cargo xtask app-icons`. The README logo, the web app's icons and every
package pick them up from there.
The logo colour token's fourth entry (the white edge) is gone: nothing
draws it any more.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* claim: #2027
* Redesign the home page as the File view (#2027)
* Match the File view with the accepted component design (#2027)
* Restore the Start subtitle and simplify the recovery icon (#2027)
* Integrate File view with main's shared UI containers
* Vertical history strip: left of the browser, compact rows, hollow-circle marker, collapsed rail hover
* Vertical history strip: seam handle to expand and collapse names, drag to resize with snap
* Vertical history strip: no empty gap below the transport
* Show the history seam arrow only on hover (#2028)
* claim: #2021
* Redesign the Assistant chat panel with shared UI components
* Show Assistant in the shared floating panel shell
* Extend floating Assistant to viewport height and shift navigation
* Dock Assistant before history and match docking preview bounds
* Align Assistant top margin with floating shell cards
* Extract a shared panel drag grip
* Rebuild responsive history sizing and three-way docking
* Share browser and History collapse hover styling
* Show the History title as soon as its text fits
* Animate stacked browser and History expansion and collapse
* Make panel folding responsive and independent of frame delays
* Let folding panel contents shrink continuously to header height
* Use four explicit History transport actions in every layout
* Resize the shared Browser and History column from either card
* claim: shared compact-mode containers (#2029)
* Centralize layout-aware UI containers and enforce their use
* Keep docked Assistant at the outer edge beside tool sheets
* Use shared surface policy for Assistant tool space
* Let tool sheets drag over shell panels while avoiding Assistant
* Update Parameters drag bounds assertion for shell overlap
* Keep dragged tool sheets below ribbon and workspace selector
* Enforce universal UI containers in mandatory repository checks
* Remove legacy report windows and require shared window composition
* Remove retired UI styling and require shared controls
* claim: compact layout preference (#2029)
---------
Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
The menus no longer ask for offset_one_side, offset_symmetric,
offset_two_sides, pattern_center or pattern_flip, so the manifest check
failed on main and on every UI pull request.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* ui: spacing, radius and type tokens, per-tab accents, agent and on-accent tokens
* ui: per-tab accent behaviour and a larger application bar with a Start page button
* icons: silhouette master and flat assistant and agent link icons (open for review)
* icons: apply review notes (sparkle assistant, refined plug, silhouette halo)
* icons: wire the silhouette master to inspect.silhouette; record the accepted icons
* ui: application bar to the design: separators, Save, house, assistant icon, readable hovers
* ui: ribbon structure: Model to View tabs, contextual Sketch and Sheet Metal, migrated pins
* ui: ribbon painting to the compact design
* ui: smaller icon button highlight and assistant mark on the bubble corner
* ui: short ribbon labels that wrap at spaces
* ui: ribbon buttons fit their labels and Finish Sketch shows its full name
* ui: constraint gallery, workspace menu, section list headings and richer tooltips
* ui: lighter selected tab label, ribbon surface and dimmer bar separators match the dark design
* ui: document tabs as rounded chips with a dimmer inactive label
* ui: document tabs fit their names and show close only when active or hovered
* icons: close and plus masters for the document tabs (open for review)
* ui: centre the new-tab plus and overflow button on the bar
* ui: a quiet rule under the ribbon tabs on non-contextual tabs
* ui: the application bar mark separates its faces with the bar colour, not white
* ui: open ribbon section label uses the selected tint and a chevron
* ui: dim the tab close and plus icons until hovered
* ui: every ribbon dropdown uses the chevron, the workspace card included
* ui: the active document tab uses the design's surface colour
* ui: the bug button's right padding mirrors its icon side
* ui: square constraint cells and a rule before the grid
* ui: constraint cells back to their earlier size, keeping the rule
* ui: smaller constraint cells and icons, centred in the section
* ui: constraint cells fit the height of the large buttons' icon and label
* ui: sketch Create titles share the other labels' line
* ui: format the application bar and ribbon changes
* ui: active tab surface token and the colour and hover tests that follow the redesign
* tests: the merged parameter and narrow toolbar tests follow the compact ribbon
* i18n: the Malay Gap label has an English source and its Japanese and Chinese translations
* apply_off_thread tests: the evaluation lingers in tests, so still computing never depends on machine speed
---------
Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
* Iterate instead of indexing in nine loops
Replace index loops that look up neighbours by position with enumerate,
zip(cycle().skip(1)) for wrap-around pairs and zip(skip(1)) for
consecutive pairs. No behaviour change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* geom: ring_edges for the edges of a closed ring
Every walk over a polygon's edges, the last back to the first, spelled
x.iter().zip(x.iter().cycle().skip(1)); eighteen of them now call
geom::ring_edges. Three redundant take(len) calls go with them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* geom: Ring, a closed ring that knows it is closed
A Ring holds items in order around a closed polygon, loop or fan; edges()
walks each with the next and the last with the first, corners() each
with both neighbours. Ring<Vec2> has signed_area and even-odd contains.
Loop2::polyline returns a Ring, as do doc's face_loops and
boundary_loops, kernel's coplanar wire samples, CapLoop's segments,
tess pieces' rings, the gizmo's plane quads and the navigation cube's
facets. Their walks take edges() or corners() instead of wrapping an
index; sketch's text-hole test and doc's poly_area become contains and
signed_area. Loop2::joints pairs each segment with the next.
Replaces the ring_edges free function.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* geom: Chain, Ring and Path; bounds for a chain of points
Ring becomes Chain<T, Closed> beside Path = Chain<T, Open>: edges()
closes a ring and stops at a path's end, Path::close makes the ring,
and bounds() (the empty box for no points) serves both. Eight
hand-rolled min/max folds over a loop's polyline call bounds(); doc's
flat_polyline returns a Ring.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* geom: area and inside on Ring for any planar point
Ring's signed_area and contains take any Planar point (Vec2 or
[f64; 2]). The cam profile in joints_cam is a Ring: its own even-odd
test goes, and its edge walks, gap and area take edges(). face_params'
loop area, the text path's winding, tess pieces' ring area and the
copies in three tests call signed_area or contains; the formulas are
the same term for term.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* doc: sheet's closed outlines go through Ring
dedup takes and returns a Ring (dropping a last point back on the first
is what makes it a ring's), near_outline walks a Ring's edges, and the
copy of it beside fold checks calls it. point_in goes: its callers ask
the Ring. Its formula anchored each crossing on the other end of the
edge, so the two can differ only for a point on an edge to the last
digit; one caller sets such points aside first, the others test a
region's interior point, a cut's midpoint or another outline's corners.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* doc: cam tests build their outlines with Ring::from
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* geom: area_vector on Ring<Vec3>; three Newell sums use it
Ring<Vec3>::area_vector is half the sum of a × b round the ring. heal's
loop_area, lip's outer-loop turning and the revolve's loop orientation
summed it by hand with a wrapped index; they take it from the Ring
(the half is exact, and two of them only read its sign).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* geom: Aabb3 from points; boxes built and asked through it
Aabb3 collects from points (skipping non-finite ones, as add does) and
gains inflated, contains and meets. Fifteen hand-rolled min/max folds
over points or boxes in kernel, doc and engine become collect or union,
and their "within tol of the box" tests become inflated(tol).contains.
blend's refused-edge boxes and naming's face index hold an Aabb3.
A NaN point is now outside every box, where the old negated
comparisons let it in; an infinite point no longer stretches a box.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* geom: Aabb2, the plane's box; 2D folds and box tuples use it
Aabb2 mirrors Aabb3 (collect, add, union, inflated, contains, meets)
and adds relative, a point's place in the box. sketch's link and
link_loops each had that formula; link_loops' boundaries, prism's
segment and loop boxes, profiles' shape boxes and grid, and ends'
segment box hold an Aabb2 instead of a (low, high) pair. Folds in the
cam, sketch bounds, sheet's perforation grid and the sketch clipboard
collect one.
Boxes are built from the same terms (a round's box keeps c - r - tol).
As with Aabb3, a NaN point is outside every box and an infinite point
no longer stretches one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* kernel: prism's segment boxes are plain Aabb2s
The SegBox alias named a (low, high) pair; Aabb2 says it itself.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* geom: Chain::bounds is an Aabb2; the boxes #2382 left as pairs follow
Chain::bounds returns an Aabb2. Its callers keep it: sheet's hole and
cut boxes (loop_box), letter boxes, island outlines and line-near-cut
test use meets, contains and inflated; profile's outline overlap and
max_clearance, profiles' box tests and the cam test read its corners.
flat_size and the flat pattern's JSON size are the union of the loops'
boxes (0 × 0 for no loop, where the pair fold gave -∞).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* geom: Aabb3::intersection; hand-grown and hand-met boxes use the box
Aabb3::intersection is the box both hold. ops' four overlap boxes of a
boolean's operands take it, and seven boxes grown by hand (ops' body
boxes, the distance set's bounds, coplanar's face box, eval_more's
padded mesh box, compare's group and region boxes) call inflated with
the same amount on every side.
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>
* Fix large modeled threads and thin crest reconstruction
Helix stations follow the pitch (radial chord error under 1% of it, 48 to
720 stations), and the faceted rebuild retries at the BSP's precision when
the usual weld tolerance collapses a thin crest facet.
Rebased onto main as one commit (the branch's own commits touched UI files
that later moved to #2263 and #2264). Adapted to main's UnitVec3 thread
axis: `*k` in thread_groove and `*cylinder.axis` in the new flange test.
* Fix icon review revisions for same-timestamp edits
On Windows, Git's default core.autocrlf checks the icon masters and the
palette out with CRLF, and `cargo xtask icons` failed four ways:
- the accepted-icon hashes (lint_icons.py and the review page's
review_state.master_hash) were taken over the files' bytes, so every
accepted icon read as changed; both now use master_hash, which hashes
the text with CRLF read as LF;
- icon_palette.revision hashed the bytes while save_colours hashed the
text it read, so every palette save was refused as stale; revision now
reads the text too;
- the tools and their tests read and wrote text in the locale's encoding
(cp1252 can't write U+2194); they now say utf-8;
- the icon inventory listed UI files with backslashes; it writes /.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
* Bill of materials: part properties on components and assembly.bom
A component definition now carries part properties (part number,
description, revision, other tools' keys) and free text properties,
saved with the component and shared by its occurrences.
component.metadata and component.metadata.set read and change them in
one undo step; Paste New copies them; parts.insert numbers standard
parts from their standard and size, and parts.resize renumbers them.
assembly.bom counts the placed components (flat: parts at any depth
through their subassemblies; top: direct children), grouped by part
number or by definition, sorted by part number, and writes CSV (fixed,
quoted columns) or JSON. The app gets a Bill of Materials window on the
INSPECT panel that lists, edits and exports the rows.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Bill of materials: name a merged row's equal definitions once
Two standard parts with the same size are separate definitions with the
same name; their row now reads "SHCS M6x20" instead of repeating it.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Window widths test: close the Bill of Materials window between commands
The test closes the floating windows a command opens before starting
the next one; the Bill of Materials window was left open and measured
against the next dialogs' limits.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Bill of materials: sub-parts in their part's row, and part properties without re-evaluating
assembly.bom takes group_children: true. A part with a part number keeps
what is placed inside it (a bolt's washer and nut) in its own row, listed
under it as children (items 1.1, 1.2, with each and count) instead of rows
of their own; bolts with different contents stay different rows, and
total_occurrences is the same either way. The window has a switch for it
and shows the children under their part; the CSV lists them after their row.
Changing part properties re-evaluated the model, re-seated every joint and
rebuilt the placed model: 361 ms per change with 30 jointed bolts. A change
that only touches components' metadata and properties now takes the same
path as a render-only change (proved on the whole Document), also for its
undo and redo: 0.6 ms.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Bill of materials: look the structure up once, not per subassembly
Deciding whether a subassembly is a part (it has bodies of its own) scanned
every body of the model for each subassembly, and each body looked up its
feature by a linear search of the timeline: subassemblies × bodies × features.
assembly.bom now indexes the placements, the body owners
(Document::body_owners: one pass over the bodies with the timeline in a map,
the same rule as model_body_component) and the numbered components once per
call. 600 bodies and features with 60 subassemblies: 8.1 ms → 1.3 ms; the
old cost grows with the cube of the design's size.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
* claim: #2544
* S command search and the command line: names, places and matching in the current language (#2544)
The S box showed each command's English label and toolbar place in every language, and found
only what was typed in English; the command line matched English only too. Rows, places and
tooltips now go through the label lookup, and the query matches the translated label, tab and
panel as well as the English ones and the id. The pin tooltips get catalog entries.
A new sweep (i18n_painted_tests) runs every language, reads the text each frame paints, and
fails on English painted where the language has a translation, skipping the design's own names
and key names, so a painter call that skips the lookup can't come back unnoticed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* The painted-text sweep also covers every Preferences page and four right-click menus
Each surface must show (the Preferences title in that language, or new text for a menu), so the
sweep can't pass by painting nothing.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* S box rows: labels stay 'static; the place's separator is drawn, level in every language
A font's "›" (and one centred on the row) sits on the baseline beside Japanese or Chinese words,
where it reads as a comma. The separator is now a drawn chevron (glyphs::breadcrumb) centred on
the ink of the panel's name. search() returns the place as (tab, panel).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* A new design's name, Untitled, shows translated in the browser too, like the other default names
The tab and the save prompt translated "Untitled" through their own special case; the browser's
top row showed it in English. It is now one of the default names the label layer translates
(Locale::default_name, with Sketch1, Body2, Component3), so every place that shows a given name
shows it translated, and the two special cases go. The painted-text sweep no longer skips a design
name that the language translates, and splits a tab's dirty mark off, so it catches a place that
shows the raw name.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Motion links can carry a coupling (gear pair, rack and pinion, screw) whose ratio follows radii, picked round faces or pitch. Design format 23.
Co-authored-by: SychPL <SychPL@users.noreply.github.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Agent chat prototype: drive an ACP agent the user already has from a side panel
A desktop-only chat panel (off by default) that starts a coding agent over the
Agent Client Protocol and gives it SolidCraft's MCP server, bridged to the open
window through the token-protected control channel with the default folder
limits. It finds installed agents (Claude Code and Codex adapters, Gemini CLI,
Copilot, Qwen, OpenCode, goose) with version and login state, streams text,
tool calls and plans, shows the agent's permission requests, and when an agent
isn't signed in shows its own login methods without handling any credential.
Design notes and findings: docs/design/in-app-agent-chat.md.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs: say only what the end-to-end run showed
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Agent chat: gate, one undo per message, docking, process groups, discovery polish
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Agent chat: Settings → AI, several agents with a switcher, OpenAI-compatible endpoints, permission modes, toolbar button
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Agent chat: translations, tool cards, Add agent dialog fixes, test agent saves on its own
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Agent chat: third-party licences for the keychain crates; clippy
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Agent chat: badge text from the theme tokens
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Agent chat: Auto-allow model edits is the default permission mode
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Colour vision modes (Protan, Deutan, Tritan) in Preferences > Graphics, with palettes, shader and icon colours adapted per mode, translations and tests.
At start, and at most once a day, a background thread asks GitHub for the latest stable
release (one HTTPS GET to releases/latest, nothing else sent; 15 s timeout; silent when
offline; GitHub's rate limit respected). When it is newer than this build, an Update button
appears next to Report a bug. It opens a window with the version, the release notes and
Download (the asset that fits this installation), Skip this version and Remind me later.
- Versions compare as semver; a nightly counts as the release it was built after, so it is
offered only a newer stable release.
- The installation is detected (AppImage, Flatpak, Snap from the store or a file, deb, rpm,
tar.gz, Windows MSI/portable/exe, macOS app); Download picks the fixed-name asset.
Development builds and the Flatpak (no network permission) never check.
- Preferences > General > Updates: Check for updates automatically (on) and Check now.
- HTTP via ureq + rustls with the operating system's certificates (desktop app only).
- docs: Updates and privacy page.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>