Compare commits

...

182 Commits

Author SHA1 Message Date
CamWight a7c0310aba Drawings: drawing.create and drawing.set refuse a misspelt setting instead of ignoring it (#3890) (#12625)
A script that calls drawing.create or drawing.set with a misspelt setting now gets an error
naming the setting. Before, the call reported success and the setting was lost. The two
commands now read their parameters through input schemas, like every other drawing command.

- schema/drawing.rs gains DrawingCreateParams and DrawingSetParams. Each field's reader keeps
  the refusal words the hand readers used.
- The hand readers for units, projection, updates and revision_numbering move into the schema.
- The drawing's line_group reads through cmd::line_group_of, so it keeps its sheets' words.
  drawing.view.edit is unchanged.
- The output types are their own slice.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 08:42:27 -05:00
CamWight e859b77cf4 Drawings: drawing.status catches up before it reads, so a reopened drawing's views say current (#4892) (#12573)
Straight after a drawing opened again, drawing.status said out_of_date for views whose kept lines
still show what the model shows, and drawing.update counted them as waiting. The first extract then
found them up to date and drew nothing. status read the cache as it was: the kept lines came back
and the keep pass ran only inside extract and the drawing step.

One function, catch_up, now brings the cache to the drawing open and the world state: the kept
lines come back, then each view that shows the same takes the state. extract, step, begin and
drawing.status (and so drawing.update) all start there, so each says the same of a view.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 06:09:23 -05:00
CamWight f50edc1ed0 Drawings: a drawing's views are worked out beside the window, so it keeps responding (#4892) (#11126)
* Drawings: views drawn beside the window (#4892 B7)

`drawing_scene::begin` asks for the first view out of date, `Job::run` works
its lines out on a copy of the session (pending moves cleared: the design's
placements, as dimensions use), on any thread, and `finish` takes them only
for the latest request while the revision, the model's world state and the
view as it resolves are as they were; `cancel` forgets the request. Request
numbers come from one counter for every session, so a drawing closed and
opened again never takes an old job's lines.

A job carries what `step` draws in one swap: the view's lines, what they
show, and the view's dimensions through its new frame. `step` and `begin`
start from one helper, so both keep the views that show the same and both
draw nothing while a deferred drawing holds. `step` is the same three run at
once. The window's hook is `drawing_jobs::tick` (the shape of `save_job`): no
caller yet, no UI.

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

* Drawings: a built sheet leaves uncaptured moves out too (#4892 B7)

`build` (every export) draws the parts where the design places them, as the
provider's jobs do: one rule for lines, dimensions and files, and once
nothing waits the provider's scene is still the built one. Exports change
only while an occurrence move isn't captured.

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

* Drawings: the window reads a sheet's dimensions too, once no view is waiting (#4892 B7)

Sheets now read their dimensions after their views (`drawing_scene::step`). Drawing views beside
the window went through `begin`, which asks only for views, so with every view drawn the
dimensions out of date were never read. With no view waiting, `tick` now steps on the window as
before; reading dimensions is quick. The tick test adds a dimension and ends with none out of date.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 02:10:50 -05:00
CamWight ecf75d9088 Drawings: a drawing, a sheet or a view saves the line group its lines are drawn in (#4890 D2d) (#12516)
A drawing saves the ISO 128 line group its views and dimensions are drawn
in; a sheet or a view may save its own. A view's group is resolved like its
other inherited settings: its chain, then its sheet, then the drawing, and
inspect says which gave it. The group joins the display settings a projected
view follows. drawing.create and drawing.set take it; the sheet commands and
drawing.view.edit take it or null to follow.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 01:29:01 -05:00
CamWight 855ce28fda Drawings: a deferred drawing keeps its lines until it is updated (#4892 U2) (#12449)
A drawing's views follow the model automatically, or, when deferred, only on
drawing.update, which lets them draw from the model as it is now, for this
session only. The next model change holds again, and a close drops the update,
so a reopened deferred drawing waits. While it waits, its views show their last
lines, out of date.

drawing.set and drawing.inspect take and say `updates`, saved only when
deferred (a drawing-format pending note). drawing.scene, its hit and line
commands and drawing.export read what the page shows and list what is out of
date; drawing.status says held.

The ribbon's Update Drawing button stays planned as drawing.update.planned with
its art awaiting review, as Export does; the command itself takes the existing
update icon.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 20:53:02 -05:00
CamWight b4242e9844 Drawings: a view draws again only when what it shows changes (#4892 U1) (#12423)
A view's entry keeps what it was drawn from: each body of the world state by
its occurrences, name, reference mark and geometry key. A view drawn from a
world state that showed the same takes the new one and draws nothing, so a
sketch, or an edit taken back, projects no view.

A drawing closed keeps its views' lines in the sheet scene's provider, and
opened again starts from them: with nothing changed it draws no view, and with
the model changed its last lines show, out of date, until drawn. Dimensions are
read again. Only in memory, never saved; a deleted drawing's lines go with it.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 18:53:53 -05:00
CamWight 41dcdd1273 Drawings: which line of a view is under a point, and drawing.scene.line (#4816) (#12396)
* Drawings: which line of a view is under a point, and drawing.scene.line (#4816)

A view's group now keeps what drew each of its strokes (the body, and the edge or silhouette of it),
one entry per item with none for its pictures, from the same walk that paints the strokes
(paper_lines). line_at finds the view under a point (hit, over the views alone), then the nearest
of its strokes within a band, the one painted later of two as near, and names it as
drawing.view.snapshot names its lines (one From<Source> for both). drawing.scene.line draws the
sheet's views out of date first, as the page does, and evaluates nothing. The two tests that built
the two-part design inline now share one fixture.

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

* Drawings: a pressed line names what drew it on each placement (#4816, #4892 Re)

A test: a round bar placed twice. A press on either one's outline names the silhouette's face
through the occurrence pressed, and a press on its top names the top rim's edge.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 16:48:19 -05:00
CamWight 1b069e1a33 Drawings: a view's dimensions change in the same step as its lines (#4896) (#12287)
The sheet page draws through the provider, which brings one thing up to date per tick. After a model
change it drew a view's new lines in one step and that view's dimensions in a later one, so for a
step the old dimensions sat on the new lines. `step` now reads the view's dimensions through the
frame it has just drawn and keeps them in the same swap; an edited dimension is still read alone and
projects nothing.

Keeping drawn dimensions is one method, `Cache::keep_dimensions`, which the view step and
`step_dimensions` both call. The same reads happen as before, a step earlier.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 14:06:54 -05:00
CamWight 2c052b4b5f Drawing export: a sheet PNG draws its text (#4890 B2b) (#12355)
A sheet's text is drawn from the same glyph outlines the SVG writer fills,
filled nonzero so a glyph's overlapping contours fill whole, where the PNG
writer refused any text before. Text counts toward the point cap and is
drawn band by band like any fill, in every colour.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 13:56:22 -05:00
CamWight 90fc4f39e0 Paper text: lines measured before they are set, wrapped between words to a width (#4890 B3b) (#12349)
paper_lines measures a text for paper in the drawing's font and language:
its lines, each with its width, and the pitch between them, broken at each
line break and, given a width, between words (a word is never cut, so a
one-word line may be wider). A word the font lacks is measured in the font
it falls back to. PaperLines::set only places what was measured, and
paper_text is paper_lines then set, so one rule measures and sets text.

Ported from the personal PC's abefb3713 onto today's fallback fonts.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 13:49:59 -05:00
CamWight 157c6469d6 Drawings: what is under a point of a sheet, and drawing.scene.hit (#4816) (#12346)
To pick a view or a dimension on a sheet, something has to say which one is under the pointer.
drawing_scene::hit takes the objects a sheet's scene lists, a point and a band: of the boxes that
hold the point within the band, the smallest, then the topmost, and whether the point is on the
box's edge. A view's box is its boundary, so one that can't be drawn can still be picked; the
drawing border is never hit. It is one pass over the objects, cheap enough for every hover.

drawing.scene.hit answers it for scripts and agents, from the sheet as drawing.scene draws it:
read-only, with input and output schemas, its label in a pending locale file and an EXEMPT line.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 13:37:20 -05:00
CamWight 69709bdbf1 Paper scene: every line takes its width from its kind and line group (#4890 D2a, D2d) (#12232)
* Paper scene: kinds of line and their widths by ISO 128-20 (#4890 D2a)

A line's kind (visible, hidden, centre, thin) and a line group give its width in one place:
the standard's series of widths, and a group's wide and narrow lines two steps apart. Nothing
reads it yet; the next commit draws every line on paper from it.

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

* Paper scene: every line on paper takes its width from its kind and line group (#4890 D2d)

A view's lines and a dimension's lines are drawn in a line group: a visible edge is its wide
line, a hidden edge, a reference part's edge and a dimension's lines its narrow one, and a
hidden line is dashed 12 line widths on and 3 off. The five width constants that waited for
this go. Every caller draws in the default 0.5 mm group, so today's sheets are unchanged; a
drawing, a sheet and a view choose their own group next.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 04:39:10 -05:00
CamWight 8067a0af23 Kernel cases: list the two drawing repair commands as tested in the engine (#12216)
#12170 added drawing.balloon.repair and drawing.dimension.repair without a
line in tests/kernel-cases/EXEMPT, so the kernel rig's coverage check now
fails on every drawings pull request that runs it ("NO CASE"). Like the
other drawing commands, both are tested in their area's test files
(drawing_balloon_tests.rs, drawing_dimension_tests.rs), so each gets the
same line, in sorted order.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 03:13:29 -05:00
CamWight 5792393fb2 What a saved view shows: source and hide, following its parent (#4816) (#11873)
A saved view now keeps the occurrences it draws and the ones it leaves out,
as the snapshot takes them. drawing.view.base, edit and inspect take them; each
follows the parent until a view sets its own, and a copy made alone keeps what
it showed. Boundaries, fitted scales and lines all read them through one rule.

A path whose occurrence was deleted never refuses the file: a hide path goes
quiet, and a view with nothing left is a placeholder that says why.

Replayed onto drawings fd4b7a7bc: the fit and the lines take reference_shown_percent
(the rename of reference_margin_mm in #7316) through shows(); the path limits now live in doc.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 03:07:08 -05:00
CamWight 188192cb8b Drawings: a lost reference lists what it may now be, and a repair points it there (#4892 B5) (#12170)
A dimension or balloon whose edge, corner or face is lost now lists up to 16 leads, offered and never taken: the
blend face named after a filleted edge, that face's edges, then the faces the same feature made. Lookup still never
writes the record, so undo brings a lost reference back.

drawing.dimension.repair {dimension, slot, pick} and drawing.balloon.repair {balloon, pick} point a lost or
ambiguous reference at a pick of the same kind, on the reference's own occurrence path: same id and paper place, one
undo step, through one shared helper. Only a reference that no longer finds the one thing it names is repaired, so a
dimension's other, still-found reference is kept, and silhouettes repaired onto one cylinder are refused as add
refuses them. A pick takes the forms add takes, so a lead of the slot's kind from inspect passes back as it stands.
The saved format is unchanged.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 21:48:01 -05:00
CamWight 371d25dadd Drawings on the web: Arabic, Korean and Chinese text is set in fonts the web app fetches (#4890) (#11790)
The web covered Japanese only (F6): any other script on a drawing was
refused. Now each script the web covers has its own font, fetched once
from the web app's origin the first time a drawing's text needs it:

- kana and Japanese Han: BIZ UDPGothic, as before;
- Hangul and Korean Han: Noto Sans KR;
- simplified or traditional Chinese Han: Noto Sans SC or TC;
- Arabic: Noto Sans Arabic.

Han takes the font of whose Han the text wants (Han::of, the desktop's
rule), so a drawing in English with Han asks for the simplified Chinese
font, as a desktop does; F6 sent all Han to the Japanese font. A text in
two scripts waits for both fonts; one whose fetch failed is refused by
name, and the rest sets. Thai is still refused by name (Hebrew is in the
built-in font).

The four fonts and their licences are unmodified, in assets/fonts/<family>/
with ATTRIBUTION rows, and trunk copies each to fonts/<family>/. Four
files rather than one CJK collection: a session fetches only its own
language's font (4.6 to 8.3 MB), where the collection is one large file.

The folder the web app fetches fonts from is named once (FONTS), and a test
checks that index.html copies each font from its asset into that folder.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 21:20:50 -05:00
CamWight e821b3ea3c Drawing export: the last two tests that read text read svg (#12164)
#12152 put an SVG's text back under `svg` in drawing.export's reply and
missed two readers: the delete test that selects a drawing's objects and the
pictures test that draws an image under the views.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 21:08:15 -05:00
CamWight e6e6df0d86 Drawing views: a default base view fits around the title block (#3875) (#10987)
fit works inside the sheet's free space by the same rule as leaves (drawing_view::Room): it takes the
largest preset the border allows, then steps down until the view's box is off the block. A sheet with
no block fits as before; the snapshot, with no sheet, passes none. The default position stays the
sheet's middle.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 20:11:48 -05:00
CamWight bf32aa1fdc Drawing export: an SVG's text is under svg in the reply again (#12152)
#10681 renamed the reply's `svg` key to `text`, and that broke the commands'
existing readers: three tests on drawings read `svg` and failed. The SVG goes
back under `svg`; a PNG's base64 stays under `data`. The output schema and the
params doc follow, and #10681's own export tests read `svg` again.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 19:59:03 -05:00
CamWight 30942265a8 A new dimension goes on the side away from what it measures (#4896) (#11932)
drawing.dimension.add, with no offset_mm, puts a linear, length or chain
dimension 10 mm on paper to the side of its line away from the material
beside it: it looks half a paper millimetre either way across the line, in
the view's plane, at the depth of what it measures, and asks each shown
body's B-rep exactly (Body::classify_point; inside or on its boundary is
material). With material on both sides or neither, it goes away from the
middle of the view's model box; on that middle, the fixed rule is above or
to the left (+10). Radial, angle, cylinder and silhouette dimensions keep
+10, and an offset given keeps its sign.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 19:31:48 -05:00
CamWight 13cd786c50 Kernel cases: the note, revision tag, revise and table drawing commands are exempt (#12137)
The rig's "Every command has a case" check failed on drawings for the ten
commands #11898 added. Each is tested in crates/engine/src/cmd/drawing*_tests.rs,
as the other drawing commands are, so they join them in EXEMPT.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 18:41:35 -05:00
CamWight f7a24278bb Drawing export: a sheet can be exported on Blueprint or Prussian Blue paper (#10679) (#10681)
* Drawing export: a sheet as PNG at a chosen dpi and colour, and one reply shape (#4890 EX1)

drawing.export {format: png, dpi?, colour?} writes the sheet's scene
through the sheet PNG writer: 25 to 2400 dpi (300 when not given), grey,
black and white, RGB or transparent (grey when not given). dpi and
colour are refused on an SVG. A path must end in the format's extension,
which ExportFormat::extension gives for every format.

One reply shape for every format: text (SVG) or data (base64 PNG) when
no path is given, in place of svg; report.pixels gives a PNG's size. A
PNG held for the reply stops at 64 MiB, as an SVG does, and the refusal
says to give a path; a file may be as large as the sheet needs.

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

* Drawing export: a sheet on blue paper, in SVG and PNG (#10679)

One rule for the two papers and their ink, used by both writers. White stays the default, byte for byte. A blue paper's PNG is rgb; the other colours are refused by name. A picture keeps its own colours.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 18:01:47 -05:00
CamWight da576824b2 Drawing views: a view's look is its visual_style, the viewport's name for it (#5307) (#11579)
A drawing view's setting for how it is drawn (visible lines, with hidden lines, shaded)
was display_style while the viewport calls the same idea its visual style. The field,
its command parameters, its schemas and its replies are now visual_style, on a view's
settings and on its own overrides. display_style is refused as an unknown field, as
drawings haven't shipped and no saved design holds it.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 17:57:32 -05:00
CamWight f2b0b39393 A parts list's ballooned column, for its editor only (#3874) (#11964)
A table's column can be left off paper (printed: false, saved only then): the
layout places the printed columns only, while filters and overrides still read
every column. No printed column may read the ballooned row field, and a table
prints at least one column.

A new parts list gets a seventh column, ballooned, not printed.
Drawing::balloons_by_list works out once which list each balloon reads, and
balloons_by_row counts one list's balloons row by row, every row listed, 0
included; drawing.inspect gives parts_lists with each
list's counts. Worked out on each read and never saved.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 17:42:48 -05:00
CamWight d0da5e5277 Mesh lines: a refusal says what happened, and each caller says what to do (#5866) (#11826)
The view lines' errors were the view DXF's, remedies included, so a drawing
view could say "DXF export cancelled" or "increase tolerance". The module now
answers a Refused (cancelled, over the work or memory limit, across the eye
plane, invalid) that says only what happened. The view DXF adds what to do in
a DXF's terms (hide bodies or sketches, increase tolerance, zoom out), and a
drawing view in a drawing's (raise chord_mm, or choose a smaller scale).

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 17:38:31 -05:00
CamWight b3af8346cf Drawings: a part's drawing starts on A3 and an assembly's on A2, each with its tolerances note (#3875) (#12000)
* Drawing templates: the built-ins carry SolidCraft's title block again, so a new drawing starts with it (#3875)

Every built-in template, ISO A4 to A0, is one sheet with the ISO border and
the built-in title block, so drawing.create with no template (iso-a3)
gives a drawing with the block. #7208 made them the border only; the
decision since is that a drawing starts with SolidCraft's block, and a
user replaces or changes the default template.

- A drawing saved as a template is the user's own: a copy of the built-in
  with a company's name in the block and its tolerances typed in starts
  drawings that keep both (tested through drawing.template.save and
  drawing.create).
- The built-in block by name stays, for a sheet without one.
- The general tolerances stay empty, and the five names (iso-a4 to
  iso-a0) and the one drawing_template setting stay: the part and
  assembly versions would be the same.
- The tests that gave a new drawing the block first no longer do.

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

* Drawing templates by kind: a part starts on A3, an assembly on A2, each with its tolerances note (#3875)

Ten built-ins, iso-a4-part and iso-a4-assembly up to iso-a0-part and iso-a0-assembly, each with its kind's general
tolerances note typed over the title block cell. template::default_for(assembly) is the one rule for the default, and
template::is_assembly says a design that places components is an assembly. drawing.create with no template uses it,
and drawing.template.list marks the design's default and says whether each built-in is an assembly's. The older names
iso-a4 to iso-a0 still read as that size's part template.

Preferences gets one setting per kind in place of the one; a saved name of the other kind reads as the kind's default,
and the old drawing_template key is ignored.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 17:38:27 -05:00
CamWight 6be23a3e19 Hidden lines on paper: a view in visible_hidden draws them dashed (#5866 V7b) (#11839)
A saved view or a snapshot's scene in visible_hidden now draws its hidden lines,
0.25 mm wide in ISO 128's dashed line, under the visible ones; a reference part in
visible_hidden draws its hidden lines the same way. Paint order from the bottom:
hidden lines, reference parts' visible lines, the model's. The sheet no longer fails
for that style, so it exports; only shaded styles with lines still refuse.
The paper scene's output schema takes a stroke's dash pattern, so a reply
with hidden lines still matches it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 16:58:17 -05:00
Mateusz Sych e81a201ff8 Drawing W06a: parallel hidden lines and shared work limits (#2455) (#3013)
* geom/hlr: per-curve work on worker threads, shared work limits

Silhouette tracing, screen sampling, shadow cuts, classification, coverage and
the crossing search (by grid rows) with crossing refinement run on worker
threads through one ordered map: results go back in item order, so drawings
are the same bit for bit on any number of threads. Options::threads picks the
workers (0 = one per core, small views stay on the calling thread); the
browser build stays serial. Placements sharing a body and a turn share one
silhouette tracing.

Cancellation is polled before every item and inside long ones from every
worker; the first error stops the others; a panicking worker becomes
HlrError::Internal instead of escaping. Ray, curve, crossing, shadow and
coverage budgets are totals over all threads (shared counters; crossing pairs
are counted before the search).

Limits: placements and the default max_tracks are hlr::MAX_ITEMS, which is
section::MAX_ITEMS (also the brep record limit); trim_samples must be 1..=4096.

* docs/drawing-hlr: link the parallel-work PR

* geom/hlr: clearer limits and parallel-work docs, faster thread tests

docs/drawing-hlr.md now separates input limits (with the error each returns:
body records fail with Invalid, not Budget) from the work budgets, names every
budget's error, and states how errors and panics behave on worker threads.
Code comments say the same in plainer terms. The budget and cancellation
tests drop the oblique torus, and the freeform pass of the identity test
compares one parallel run, which keeps the HLR suite under 4 s.

* geom/hlr: poll cancellation inside long items, find spline spans by bisection

Review found two items a worker could run for seconds without seeing a
cancellation: tracing a body with many curved surfaces, and sampling one long
spline (up to 4096 control points). Silhouette tracing now asks before each
surface, screen sampling at every refinement pass, the cuts at left-out faces
every 4096 chords and coverage every 65536 checks.

The long spline was slow because every point and every part hull scanned all
of its knot spans. de Boor evaluation now finds its span by bisection over the
ordered knots, and a part's hull starts at its first piece and stops after
its last: the same spans, so the same bits (checked on the fixture scenes),
and a 4096-point spline edge prepares in 0.9 s instead of 2.2 s and samples
in 0.23 s instead of 1.47 s.

* geom/hlr: poll cancellation while forming and trimming silhouette sets

Review found that forming a body's silhouette sets scanned each face's whole
surface group before the first poll, quadratic in the faces on one surface
(about 4e8 steps for 20 000 faces). The group scan now stops at its first
supported face (groups list faces in order, so that is the same face), the
loop polls every 256 faces, and trimming a traced curve asks before each
face. A cancellation seen while trimming drops the partial curves, even if
the callback answers false later. Output unchanged on the fixture scenes.

* geom/hlr: poll cancellation per boxes met when cutting at left-out faces

Review found that one chord crossing the screen boxes of very many left-out
faces bisects every side without a poll (polls came only every 4096 chords).
It now also polls every 1024 boxes met. The docs state the cancellation
contract precisely: polls are where listed, and the work between two polls is
bounded by the input limits rather than by a time.

* docs/drawing-hlr: say where coverage polls cancellation

* geom/hlr: body records have brep's own, larger limit now

Main raised brep::MAX_ITEMS to 1 000 000 (#3093, which stores whole evaluated
models), so it no longer equals section::MAX_ITEMS. hlr::MAX_ITEMS stays
section's 100 000 for placements and the default curve budget; the docs and
the limits test now say that a body's records are bounded separately, by
brep's limit.

* geom/hlr: a panicking worker stops the others; the ray budget is HLR's own

A worker that panics now sets the shared stop flag while it unwinds, so the
other workers end the phase early instead of finishing work the call throws
away. The docs no longer compare the ray budget with the occlusion work
budget of file.export_view_dxf: the two count different work.

* geom/hlr: an ignored timing test, prepare and drawing timed apart

hlr_timing prepares four bodies, then draws 100 differently turned placements
on one thread, two workers and one worker per core, printing the total, the
phase timings and the ray count. SOLIDCRAFT_BENCH_HLR sets the placement count.

* geom/hlr: timing scenes for sharing and crossings; placements keep their lines when permuted

hlr_timing now starts its prepare clock after the fixtures are built, adds the
same grid at one turn (shared definitions against one per placement) and
sixteen thin bars crossing 8x8 from above, rotates the thread order over three
rounds and checks the drawings match across thread counts outside the timed
part. A new test permutes three placements of one prepared box and removes one:
each line keeps its own placement index, and the boxes' lines stay exact.

* docs/drawing-hlr: the review's limits and fixtures in the parallel tables

* geom/hlr: follow the B-rep record into solidcore-record

The record, its version and its item limit now come from solidcore-record
(#11496): the new tests build their bodies at `solidcore_record::brep::VERSION`
and make a spline's curve with `C3::bounded`, as the other fixtures do, and
the limits table gives the record's own limit, 4 000 000 items, checked by
`brep::check::validate`.

---------

Co-authored-by: MateuszSychXpl <151755914+MateuszSychXpl@users.noreply.github.com>
2026-11-04 16:56:07 -05:00
CamWight 96bb41c480 Drawings: notes on a drawing, the record and commands (#4896) (#11898)
* Issue a revision: drawing.revise (#3874 R1)

drawing.revise appends a copy of a row to the issued history: an existing row as
it reads, or a new revision with the texts given, in one undo step. A row is
issued once. The copy keeps what it read through an edit, a hide or a delete, and
nothing is filled in: no date, checker or approval.

Issuing used to make a drawing unreadable, as a copy has its row's id and the
drawing's check refused any id seen twice. Each id now counts once, and issued
ids are unique among themselves.

model_version becomes {path, change}, on issued copies only. One function on the
session answers it, and answers nothing until version control exists.

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

* Drawing revisions: numbered 1, 2, 3 (#3874)

A drawing's new revisions are numbered 1, 2, 3, never lettered
(@CamWight, 3 Nov). drawing.set's revision_numbering is {start: 0 or 1},
the first number. A revision typed by hand is kept as typed; the next one
follows the highest number, and drawing.inspect says where numbering
starts. A record in letters is refused (drawings haven't shipped).

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

* Revision tags on a sheet: add, edit and delete (#3874 R3)

A tag gains the sheet it sits on and its place there. drawing.revision.tag.add,
.edit and .delete keep them, inspect and every revision reply list them with what
each shows, and a sheet's delete counts and takes the tags on it. A deleted row
still leaves its tags showing the revision they had. Tags draw on paper once scene
text lands.

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

* Saved tables on a sheet: drawing.table.add, .edit and .delete (#3874)

A drawing saves its parts lists and revision tables, each on a sheet with its
definition and place. Rows are never saved. A parts list starts at the top right
listing what its sheet documents, else the design; a revision table at the bottom
left; headings on top, QTY, rows of 7 mm.

Placing a list saves the numbers its view shows, once per view, so what is printed
doesn't move. Numbers are the design's: undoing the placing puts back only the
drawing, as every drawing step does, and the numbers stay. That one command changes
both, and the proof that nothing needs evaluating now allows it.

The tables are a ById list, as the balloons and images are, and keep its rules: at
most 1000, listed by id. Revision tags take their own id type.

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

* A balloon reads its row in a parts list (#3874 L1)

A balloon can name the parts list it reads; else it reads its sheet's only list,
else the drawing's only one, else none, with the choices listed. Its row's item is
worked out on each read and never saved: drawing.balloon.inspect gives item and
row_status (ok, no_list, reference, no_row or not_found). A lost row never removes
the balloon. A list's delete reports the balloons that named it and clears their
links.

Bom::item_of cuts a path from the top of the design to start below the assembly
listed, and row_of is no longer test-only.

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

* Notes on a drawing: the record and commands (W19e, #4896)

A note sits on a sheet, a view, or an edge or corner of a view's model, saved by
name as a dimension's is, with the title block's text and fields, its place and
its text height. drawing.note.add, .edit and .inspect, one undo step each, no
model evaluated. Its one leader is worked out, never saved: a lost reference keeps
the note and its text, with no leader. Anchor is one type so other symbols placed
on a sheet can use it.

A view's notes go with its copy and delete; a sheet's with the sheet. The plan left
delete for later, but selection.delete now handles a drawing's objects, so notes
join it. Balloons and notes share Lookup::middle. Drawing them waits for text in
the paper scene (slice 12).

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 16:53:53 -05:00
bherbruck ca6a095795 Merge pull request #12087 from tangency-labs/sync/main-into-drawings-1104
Sync main into drawings (Nov 4)
2026-11-04 15:51:54 -05:00
bherbruck 0273441236 Sync: the three dimension functions that read the display mesh, on drawings only
Picker::placed, face_corners and BodyEdges::new decide with the display mesh.
They are listed on this branch only and must be cleared before drawings lands
on main, where the list takes no new lines.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 15:24:44 -05:00
bherbruck 58a00f41be Sync: the repository checks know the drawings' commands and modules
The kernel rig's coverage check exempts the drawing and bill of materials
commands, each naming where it is tested. The mesh decisions check follows the
view line modules to mesh_lines/, and skips the two that only draw and count a
drawing's view lines.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 15:23:15 -05:00
bherbruck b841eb939f Merge main into drawings (Nov 4)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 11:39:05 -05:00
PeppaWutz 8f67c7fb95 Sketch: constraint icons stay while the sketch fills the view (#10518) (#11399)
* Sketch: constraint icons stay while the sketch fills the view (#10518)

Constraint glyphs hid as soon as a millimetre of the sketch plane drew smaller than two pixels
(#8980), whatever the sketch's size, so a 500 mm sketch zoomed to fit lost every glyph although
it still filled the view. Below that scale the glyphs now hide only while their anchors span
less than twenty glyphs (300 px) on screen: a small sketch far away still loses them, a large
one that fills the view keeps them. The preference "Hide constraint glyphs when zoomed out" is
unchanged; off, glyphs stay at every zoom as before.

Test: constraint_glyphs_hide_when_zoomed_out adds a 500 mm sketch that keeps its glyphs zoomed
to fit and loses them far away; glyph_zoom_tests covers the span threshold.

* Sketch: glyph span measures only drawn glyphs and stops at the threshold (#10518)

The span check now walks the same constraints the glyph pass draws (one MidPoint glyph per shared midpoint, a pattern's constraints once) and returns as soon as the bounds reach MIN_GLYPH_SPAN_PX, so a large sketch zoomed out costs no more than before.

* Sketch: filter glyph constraints lazily, midpoints in a set (#10518)

* Sketch: measure the sketch, not its glyph anchors, before hiding glyphs (#10518)

* Sketch: collapse the midpoint check into one if (#10518)
2026-11-04 12:06:26 +00:00
CamWight 49eedf122c Drawings: a reference to a suppressed or failed feature says so (#4892) (#11823)
* Labelled measurements on a view's snapshot (#5866)

measure: linear, aligned, angle from model points, radius from an edge, with
units and decimals for the labels. No new maths: drawing_measure's rules, and
a dimension's radial rule through one Lookup::whole_arc. Labels go through one
function, reading_label, which dimensions' text now uses too.

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

* Pieces on one circle are that circle, and one piece is that edge (#4892)

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

* A dimension between two flat faces (#4892)

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

* A suppressed or failed feature's references say so, not lost (#4892)

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 06:40:17 -05:00
CamWight 669aaa7744 Drawings: a dimension finds its circle again after the edge is split (#4892) (#11869)
* Labelled measurements on a view's snapshot (#5866)

measure: linear, aligned, angle from model points, radius from an edge, with
units and decimals for the labels. No new maths: drawing_measure's rules, and
a dimension's radial rule through one Lookup::whole_arc. Labels go through one
function, reading_label, which dimensions' text now uses too.

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

* Pieces on one circle are that circle, and one piece is that edge (#4892)

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 06:40:10 -05:00
CamWight a3375c58e3 Render: a triangle tests only the samples of each row it can touch (#5624, W17b) (#11042)
The raster walked a triangle's whole box, testing every sample, and the pixel budget counted that box.
A plain plate's few long triangles have boxes that are mostly misses, so a view at 2:1 counted 18 tests
a sample. Now each row of the box tests only the span the triangle can touch there, and the budget
counts those spans (an empty row costs one unit, as the walk does).

row_span works the span out from each edge's weight along the row, allowing for the test's own
tolerance and the rounding of its arithmetic, so it is never short; sample_weights is the raster's test
pulled out unchanged. Pictures are the same to the byte. A product image shares the raster and the
count, so its 128 M admits more; the tests built on its old count hold more triangles.

With the misses gone nearly every counted test paints, and a painted surface costs about 240 ns a
test on one thread, so a picture's limit falls from 400 M to 250 M to stay near a minute at worst.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 06:12:20 -05:00
CamWight a301359652 Dimensions on the sheet: lines and arrowheads in paper millimetres (#4896) (#11867)
Each dimension of a view on the sheet is one group, ObjectKind::Dimension,
after the views: extension and dimension lines and filled arrowheads
(Terminator::place, #9466), laid out by dim_layout_sized in paper millimetres, so it is the same size at any
view scale. All seven kinds draw; a chain is one line of frames at one offset
with each extension line once, an angle sits where its lines meet on the view,
and a cylinder's line runs across its silhouettes at the end on its offset's
side.

drawing.scene and drawing.export list each dimension: its status, whether it
is drawn, and why not. A lost one, or one on a view that isn't drawn, has no
lines and leaves the sheet and the file written. The provider keeps each
dimension's group by its record, world state, view drawing and units: a moved
view moves them unread, and an edit reads that dimension alone, in one short
step after the views. Text joins with B1.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 04:53:24 -05:00
github-actions[bot] 6bca7d2854 Regenerate generated files and fold pending files [skip ci] 2026-11-04 08:13:26 +00:00
Frank eeb686f164 Measure: a circle and a line now show 'Line to centre' instead of 'Center to center' (#11524)
* Measure: a circle and a line show 'Line to centre' instead of 'Centre to centre'

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Measure: US spelling for Line to center, UK in en-GB

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

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 07:38:21 +00:00
Nathan Dane 9e3e403b3f Browser and History: keep row tools working when either panel is hidden (#11303) (#11416)
* claim: #11303

* Keep Browser and History row interactions independent

* Browser tests: use solidcore_record::Vec3 after the layer split

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-11-04 07:15:40 +00:00
dudedagger42 0231eec4c6 Nix flake: zenity on the app's PATH, for file dialogs without a portal (#2177) (#5183)
rfd opens file dialogs through the XDG desktop portal and falls back to zenity
when there is none. The flake's package didn't bring zenity, so on a system
without a portal or zenity, Open and Save showed nothing. The wrapper now adds
nixpkgs' zenity at the end of PATH, after the system's own.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 06:53:37 +00:00
erman 69f202f1c8 Measure: show complete point coordinates (#11069) (#11085)
* Measure: show complete point coordinates (#11069)

Render measurement item details as wrapping text instead of fixed-height cells. Keep the existing values, units and precision. Add a regression test in the existing measurement UI test module.

Local validation: the regression test compiled, but the Windows GNU test binary crashes with 0xc0000005 even for --list, before tests run. Runtime verification and visual evidence remain pending; this contribution is a draft.

* Keep repository LF line endings in the coordinate fix
2026-11-04 06:53:04 +00:00
austin62jake 1cbfb889d5 Agents and scripts: Compare Bodies describes its exact parameters and reply (#10994) (#10995)
* claim: #10994

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

* Compare Bodies: input and output schemas (#10994)

inspect.compare gets a typed contract and reply types in
schema/compare.rs and leaves unconverted.txt. Each key reads as the
handler reads it, with its message; `color` checks each colour with the
handler's own color_param, now pub(crate) and re-exported from cmd. The
reply is built from Comparison and its parts, with the same JSON.

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

* Compare: drop the json import the typed reply no longer uses

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

* Compare schema: colour strings carry the hex pattern

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
2026-11-04 06:45:10 +00:00
thocsmith ee3cc32525 Settings profiles: export and import stay inside an agent's folders (#11362) (#11390)
* claim: #11362

* Settings profiles: export and import go through vfs, inside an agent's folders (#11362)

settings.profile.export and settings.profile.import read and wrote their
path with std::fs, so the folder scope an MCP request's allow_paths sets
up never saw it: an agent could write a profile anywhere, or probe any
path. Both now go through vfs (len, read_to_string, write), as file.export
and ui.screenshot do. requests_with_allow_paths_stay_inside_them covers
export and import inside and outside the agent's folders.

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

---------

Co-authored-by: thocsmith <193096300+thocsmith@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 06:28:11 +00:00
Colin Faulkingham 954232cef1 AI client setup: preserve configuration files when a write fails (#11363) (#11410)
* claim: #11363

* AI client setup: replace configuration files atomically (#11363)
2026-11-04 06:27:37 +00:00
github-actions[bot] 9092df9b97 Regenerate generated files and fold pending files [skip ci] 2026-11-04 07:25:51 +00:00
semmelrinde c1a23f8f97 Section Analysis: the drag arrow is back when the dialog is reopened (#11423) (#11432)
Edit reopens the dialog from the section's own geometry (Show viewport
section status, on by default): the plane is kept as numbers and nothing
is picked. The arrow was placed from the first pick only, so it was gone.
With nothing picked, it now stands on the kept plane, its tip at the cut.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 06:11:46 +00:00
austin62jake 0fc4cd2747 Agents and scripts: the render stage commands (Ground, Backdrop, Clear) describe their exact parameters and reply (#11455) (#11456)
* claim: #11455

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

* Render stage: input and output schemas for Ground, Backdrop and Clear (#11455)

The three handlers' strict parameter types move to schema/render_stage.rs as
GroundParams, BackdropParams and ClearParams, with descriptions and
JsonSchema, and become the contracts; the handlers read the same types. The
handlers build the shared StageSaved reply instead of json! literals, and the
saved setup is serialized with an error instead of json!'s panic path.

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-11-04 06:11:09 +00:00
leValdemar19 6d83f95fba Isolate a body: other components are hidden as components, in one step (#7896) (#7951)
* claim: #7896

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

* Isolate a body: other components are hidden as components, in one step (#7896)

Isolating a body hid every other body one by one: browser.visibility with `items` writes only
hidden_bodies, so every other component's Browser row kept an open eye and normal text, and
its eye could not bring it back (the first click hid an already invisible component).

browser.visibility takes `isolate: true` with `items` (bodies), as it does with `paths`. The
picked bodies' own placements stay open, their other bodies (and the root's) are hidden one by
one, and every other placement is hidden as a placement. Isolate in the Browser and the view
runs that one command, so it is also one undo step instead of two.

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

* Isolate a body: a hidden Bodies folder or group it is in shows again (#7896)

Isolating a body inside a hidden Bodies folder or group left that folder or
group hidden, so the picked body stayed hidden. Isolate now shows the
folders and groups (with the groups they are in) that hold the picked
bodies; their other bodies are hidden one by one as before.

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

* Isolate a body: look for hidden folders only when some are hidden, and find body owners once (#7896)

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 06:06:23 +00:00
austin62jake bf625dff05 Box, Sphere and Torus: clicking a point of a sketch places them there (#9403) (#9405)
* Box, Sphere and Torus: a sketch point picks where they go (#9403)

The Point input of the Box, Sphere and Torus windows took a vertex, a point on an edge or face
and the origin, but not a sketch's points, so a sphere could not be centred on a point drawn in a
sketch. It now takes the points of every shown sketch (POINTS | EXTERNAL_POINTS, as Measure
does): a click fills X, Y and Z with where the sketch puts the point in the world, and a point
selected before the window opens places the primitive there, as a vertex does. The input's chip
names the point and its sketch as Select Other does ("Point 2 · Marks").

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

* Box, Sphere and Torus: the sketch point chip label for every complete locale

Main's i18n check wants each message in the complete catalogs (ar, he, ja,
vi, zh); the label has no words, so each carries the same "{point} · {sketch}".
With main's new selection kinds and this one, every Sel variant has its arm,
so the catch-all arm is unreachable (clippy) and goes.

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

* Primitive at a sketch point: a point picked in the sketch being finished still places it

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
2026-11-04 06:03:39 +00:00
semmelrinde ed3510a91f Typed schemas for the design properties commands (part of #2829) (#10920)
document.metadata, document.metadata.set and document.metadata.remove
get input and output schemas: contracts in schema/metadata.rs, checked
in front of the unchanged handlers, and reply types the handlers now
build their replies from. Their lines leave unconverted.txt.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 06:03:21 +00:00
github-actions[bot] 8f0130a61b Regenerate generated files and fold pending files [skip ci] 2026-11-04 06:39:31 +00:00
github-actions[bot] 72df682af7 Regenerate generated files and fold pending files [skip ci] 2026-11-04 06:27:43 +00:00
Davit Matchakhelidze 41a450be7c i18n: add Georgian (ka) interface language (#11501)
A Georgian catalog (assets/locales/ka.json) carrying every English message,
registered in crates/ui-egui/src/i18n.rs, with a row in the new-language test,
a pending attribution file and a terminology note in docs/localization.md.
The catalog is Mkhedruli only; egui's built-in Hack already draws it.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 06:00:45 +00:00
HiroKenEz b227924be9 Render: the Cancel button keeps its full label during a render (#11222) (#11241)
* claim: #11222

* Render: the progress row keeps Cancel whole (#11222)

The progress card is an egui area, laid out within last frame's size. When
the status grew (9 % to 10 %, a longer phase or name), the caption and the
Cancel button were truncated, down to "..." with a long shot name.

progress_action now takes the name and the status apart and gives each part
an explicit width from the room the caller offers (the canvas for the
floating card): Cancel and the status always show whole, a long name is cut
with an ellipsis (full name on hover), then the meter narrows.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 06:00:31 +00:00
Mateusz Sych 28b47059bb Agent chat: a text selection dragged past the edge scrolls the conversation (#11527) (#11533)
* claim: #11527

* Agent chat: a text selection dragged past the edge scrolls the conversation (#11527)

Holding a text selection near or past the top or bottom of the conversation
scrolls it, faster the further out, so a long answer can be selected in one
drag. egui scrolls a label's selection only for the label under the cursor, and
not at all for a table, whose label sits in its own horizontal scroll area.

egui also leaves a label out of the text selection while it is scrolled out of
view: a selection whose start had scrolled away was dropped, and Copy missed
the hidden text. The conversation's labels now take part out of view too,
clipped to nothing, so they keep their end of the selection and give their
text to Copy without being drawn or moving the selection.

* Agent chat: only a drag towards the edge scrolls the conversation (#11527)

A press on a label creates an empty selection on the press frame, so a click,
a double click or a held press on the line by the edge scrolled the
conversation, and so did the start of a drag from there inwards. Scrolling now
waits until the pointer has moved more than 8 points towards that edge since
the press.

* Agent chat: approval titles and pending messages keep a selection scrolled away

They were plain selectable labels, so a selection that started in one was dropped
once its label scrolled out of view, as in the rest of #11527. They now use the
same off-screen-aware label as the conversation's text.

---------

Co-authored-by: MateuszSychXpl <151755914+MateuszSychXpl@users.noreply.github.com>
2026-11-04 06:00:28 +00:00
Mateusz Sych a19a32ddd7 Interference: Compute checks in the background, so the window no longer freezes (#11483) (#11541)
* claim: #11483

* Interference: intersect and mesh each overlap on a kernel worker (#11483)

Compute in Inspect > Interference intersected each overlapping pair and
meshed the shared volume on the window thread. Its stack is 1 MiB on
Windows, too small for the kernel's recursion, so the app could close
without a crash message. Each pair now runs through
kernel::map_with_local_pools, on a worker with the kernel's 8 MiB stack,
as inspect.interference and inspect.clearance already do.

* Interference: Compute checks off the window thread (#11483)

On 0.30.0 the report's two parts took the window thread 95 s (x64) to
154 s (x86) in the overlap boolean, and the window stopped responding
until the app was closed. Compute now runs inspect.clearance and the
shared volumes on a copy of the session, on a kernel thread, when the
app runs commands off the window thread (as Find Limits does). The
dialog shows the elapsed seconds with Cancel meanwhile, and drops the
reply of a design changed since.

* Interference: Cancel on its own row while Compute checks (#11483)

---------

Co-authored-by: MateuszSychXpl <151755914+MateuszSychXpl@users.noreply.github.com>
2026-11-04 06:00:15 +00:00
Homero Thompson 606e264f6d UI: the Dependents button of the INSPECT HISTORY ribbon no longer fails without a feature (#11642) 2026-11-04 05:59:52 +00:00
semmelrinde d25f8198fa Pieces of a cut keep their names when the design is evaluated again (#11658)
A cut that leaves a body in pieces names them after their faces
(`order_lumps`): the piece whose least face name sorts first takes the
first name, and so on. It moved the names but left the pieces where the
kernel put them. The session then pinned the feature's body names in
that state order, and the next full evaluation (opening the design, or
editing a parameter upstream) handed them out again in a different
order: two pieces swapped names, and a later feature that named one met
the other. Found by a local model fuzzer.

The pieces now also take the family's places in name order, so the
names pinned are the order the next evaluation hands them out in. A
design saved before keeps the names a fresh evaluation gave it.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 05:59:42 +00:00
Hainlein4711 464f4ac748 Intel OpenGL: restore the grid and sketch axes (#8053) (#11573)
* claim: #8053

* Intel OpenGL: restore the grid and sketch axes
2026-11-04 05:49:15 +00:00
semmelrinde 03b5248dc1 Sketch: Line and Construction Line refuse a second point next to the first (#11595)
A through-point 0.0000001 mm from the start made an infinite line whose
direction nothing held, and a Line that short had both ends connected to
the origin, a line of length zero. Both now refuse points closer than the
distance at which a new point is connected to an existing one.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 05:49:05 +00:00
Colin Faulkingham f9dd77e383 Configurations: reject invalid suppression values when saving rows (#9088) (#11571)
* claim: #9088

* Configurations: reject invalid suppression values before saving rows (#9088)
2026-11-04 05:39:04 +00:00
semmelrinde 4afd24d29f Design tabs: the tooltip says when a saved design has unsaved changes (#11408) (#11590)
* Design tabs: the tooltip says when a saved design has unsaved changes (#11408)

A tab marks unsaved changes with a bullet after the design's name, and a
long name cut short by the tab hides it. The tab's tooltip, which says
where the design is saved, now adds "Unsaved changes" below for a saved
design with changes not saved yet. A design never saved already says
"Not saved yet". The new string is in a pending locale file, with ten
translations taken from the same words elsewhere in the catalogs.

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

* Design tabs: the unsaved line in every complete catalog (#11408)

Arabic, Hebrew and Vietnamese are complete catalogs, which must carry
every English message: they get the words they already use for
"Unsaved changes" elsewhere.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 05:29:03 +00:00
kaltenbrunnermarkus 8a74336713 Tests: the autosave recovery test no longer fails at random on a busy machine (#11815)
* claim: Recovery: the save test waits for the autosave worker before it checks

* Recovery: the save test waits for the autosave worker before it checks

saving_drops_the_entry_and_a_clean_close_leaves_nothing marked the
session saved right after the tick that started an autosave. Under load
the worker was still writing, so the next tick found the write running,
took nothing up and dropped nothing; the file landed afterwards and the
check that it is gone failed (seen in CI on #9021).

The test now ticks, bounded, until the autosave has caught up
(Autosaver::caught_up), as the app's own frames would, before it marks
the session saved. A small helper in the test module does this. The
app's behaviour is unchanged.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 05:22:20 +00:00
Colin Faulkingham 5e4c52a7cb Sketch Transform: keep the applied rectangle at the previewed rotation and size (#11617) (#11620)
* claim: #11617

* Sketch Transform: preserve dimensioned geometry on apply
2026-11-04 05:21:27 +00:00
github-actions[bot] 77e0e11775 Regenerate generated files and fold pending files [skip ci] 2026-11-04 05:42:40 +00:00
Lars Jansen 7bd66b7c06 Sketch dimensions: enter the value in a small window with step arrows (#7246) (#7251)
* claim: #7246

* feat(sketch): enter a dimension's value in a small window with step arrows (#7246)

* Dimension window: fit the layer split, and keep the wheel off the camera over the window

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-11-04 05:12:31 +00:00
Daniel 86db05e538 Sketch: regression test for a Line started on a snap point (#11171) (#11322)
* claim: #11171

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

* Sketch: test that a line starts on a snap point of an existing shape (#11171)

The Line tool's first click on a line's midpoint, a circle's centre or edge,
a rectangle's edge or a point part-way along a line starts the line there and
ties its start to that shape. In 0.30.0 and 0.30.1 the start was dropped on the
next hover (fixed by #11184); this covers each shape the reports name.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 05:11:14 +00:00
ABDULLAH d912f3533b Arabic: better wording for Appearance, Graphics, Assistant, Layout, Sketch and Navigation preferences, thread drill notes and other messages (#11358)
* Arabic: viewport term in the Appearance section heading, desktop app badge, deeper-overlap note in Find Limits

* Arabic: drop the stray direction mark and match the Refresh wording in the old Node.js note

* Arabic: tanween in the thread fit hint, tap drill wording, crease term in continuity, spline label and setback wording in the fillet help

* Arabic: sweep and boolean terms in the body check note

* Arabic: badge term for the status chips note and summary wording for Fold items in Graphics preferences

* Arabic: tanween, catalog terms and clearer labels in the Assistant, Design and Tools preferences

* Arabic: tanween in the shortcut key prompt, Look At wording in the Assistant follow help

* Arabic: complete the Several choice for bodies folders and use a verb form for the dialogs Open row

* Arabic: orbit style term, Select Other and center-point arc names in Sketch preferences, place-from-origin wording, tanween in the radial menu help, tap makers in the tap drill notes

* Arabic: graphics device term in the blur notes, boss and turn wording in the fillet corner option
2026-11-04 05:10:29 +00:00
kaltenbrunnermarkus 9d7bbb7fa9 Tests: two Properties tests no longer fail at random on a busy machine (#11750)
* claim: Properties: hold the measuring worker in tests until the card is checked

* Properties: hold the measuring worker in tests until the card is checked

open_object starts the measurement on a worker and looks for the reply
once without waiting. Under load the window side could be held up
between the two, the reply was already in, and two Properties tests saw
the card filled where it should still be measuring (about 0.3 % and 4 %
of runs with 18 tests at a time).

measure_beside now runs a hold before sending the reply. In tests it
waits on the app's existing apply_gate, which feature evaluation already
uses for the same purpose; outside tests it does nothing. Both tests set
the gate, check the card is measuring, then let the worker go.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 05:08:36 +00:00
leValdemar19 6312c2760a MCP list_commands: ask for commands by id prefix or panel, ids and labels only, or a page at a time (#11726) (#11788)
* claim: #11726

* MCP list_commands: ask for commands by id prefix or panel, ids and labels only, or a page at a time (#11726)

list_commands answered with the whole catalog, every key of 575 commands
(about 350 KB), and could only narrow it by a substring. It now also takes
`prefix` (start of the id), `panel` (a whole panel or tab name), `compact`
(id and label), `fields` (the keys to keep) and `limit`/`offset` (pages, as
{commands, total, offset, next_offset}). With none of them the reply is the
same as before. A batch step naming list_commands narrows the same way, and
a bad argument is an error that names it.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 05:07:29 +00:00
SilentWolf75 3a23d21795 Lip: clearer guidance when the face is not a shelled rim (#10437) (#10871)
* claim: #10437

Co-authored-by: Cursor <cursoragent@cursor.com>

* Lip: say Shell first when the face is not a rim (#10437)

A solid box top left OK disabled with a vague shelled-body hint. Name Shell and the rim-around-an-opening requirement in the refusal, the dialog tip, and the command help.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Lip: keep the dialog hint as it was, match the new refusal text in the test

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>
2026-11-04 04:42:08 +00:00
austin62jake e199f9aeff Agents and scripts: Match Surface describes its exact parameters and reply (#10981) (#10982)
* claim: #10981

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

* Match Surface: input and output schemas (#10981)

surface.match gets a typed contract in schema/surface_match.rs and the
shared FeatureResult as its output, and leaves unconverted.txt. Each key
reads as the handler reads it, with its message; like solid.scale it
declares `name` and `participants` itself, since a match makes no body.

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

* Follow main's kernel merge: world_state and the shared continuity reader

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

* Surface match schema: Vec3 comes from solidcore_record now

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
2026-11-04 04:14:11 +00:00
austin62jake e06d6e36d8 Agents and scripts: the material card checks describe their exact parameters and reply (#11036) (#11037)
* claim: #11036

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

* Physical material property commands: input and output schemas (#11036)

material.properties.validate and material.properties.directional get
typed contracts and reply types in schema/material_properties.rs and
leave unconverted.txt. The card stays the handler's to check; the
direction has the handler's messages. The replies mirror the document's
Validation and DirectionalModulus, destructured in full.

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-11-04 04:08:15 +00:00
Kai Evans 9d1ad9f0b9 Feature editor: OK accepts the joint placements a roll back re-solves (#11094) (#11095)
* Feature editor: a roll back's re-solved placements are not a changed design

Rolling the marker solves the joints again and stores where that puts each occurrence, so the
editor's pinned original differed from its source there and OK was refused with "the original
editor model does not match its source design". The check now takes the placements as the roll
leaves them.

* Feature editor: also accept the ratio and lift of coupled joints that rolling the marker works out

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-11-04 03:56:46 +00:00
austin62jake d39a2d28db Agents and scripts: the clearance check describes its exact parameters and reply (#11051) (#11052)
* claim: #11051

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

* Clearance: input and output schemas (#11051)

inspect.clearance gets a typed contract and reply types in
schema/clearance.rs and leaves unconverted.txt. Each key reads as the
handler reads it, with its message; MAX_CLEARANCE and MAX_NEAREST move
to the schema module so both read one limit. The reply is built from
Clearance and its item types, with the same JSON as before.

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-11-04 03:44:59 +00:00
github-actions[bot] 1e649653ef Regenerate generated files and fold pending files [skip ci] 2026-11-04 03:44:17 +00:00
bherbruck 6988eb0043 CodeRabbit: flag changes to existing defaults (#11832)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 03:15:51 +00:00
Tyler Chiriboga 23c2244307 Inspect: center of mass reports the same volume as measure (#6173) (#11262)
* claim: #6173

* Inspect: center of mass reports the same volume as measure (#6173)

inspect.center_of_mass measured each body's display mesh, whose flat
facets cut curved faces short, so its volume and centre differed from
inspect.measure's (a sphere of radius 10: 4183.10 vs 4188.49 mm3). It now
takes both from the same cached kernel measurement Measure uses, and like
Measure it leaves out bodies that enclose no volume, counting them in
open_bodies_skipped.

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

* Centre of mass tests: read the skipped list that main now returns

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

---------

Co-authored-by: Tyler Chiriboga <18270301+Tyler-Chiriboga@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
2026-11-04 03:13:56 +00:00
semmelrinde a52ee850aa 15 languages: translate the 21 messages main gained after batch 4 (batch 5) (#8573)
* 15 languages: translate the 21 messages main gained after batch 4 (batch 5)

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

* Pending locales: drop the strings the catalogs already have

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>
2026-11-04 03:08:32 +00:00
bherbruck 65d23a78a5 Preferences: the 3D mouse speed readout shows × instead of garbled text (#11785)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 02:46:30 +00:00
bherbruck 481687fff3 Modeled threads: the thread runs down to a flat face with no uncut block, so a nut can bottom out (#11782)
* Modeled threads: run down to a flat shoulder so a nut can bottom out

An external thread running the whole face stopped with its groove clear of
a shoulder, leaving up to a turn uncut above it. The groove now runs on past
the plane of the face's end and is trimmed there, so its root reaches the
shoulder at every angle and nothing past the plane is cut.

A shoulder rising back along the face keeps the square stop, as do internal
threads, tapers, runouts and lengths stopping short. Where the kernel refuses
the trimmed cut, the groove also keeps the square stop and the feature warns.

Closes #11610

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

* Kernel ladder: record the thread cases that now reach their shoulder

Six cases gain the faces of the trimmed last turn; every other case is
unchanged apart from its timing.

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

* Thread at a shoulder: review fixes, and a test of the fallback

The shoulder check starts just off the face, as the free-end check does, so
a slope rising from the face counts. A groove the trim leaves nothing of now
falls back to the square stop too. A test forces the fallback and reads its
warning. The ladder's order and report go back to main's, changed only for
the cases this moved.

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

* Thread at a shoulder: check all the radii the groove cuts

The check for a shoulder rising along the face looked a fixed distance out,
which falls short of the groove on an undersized shaft. It now covers the
groove's reach. A test reads how each kind of shoulder is classified.

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

* Thread at a shoulder: test the trim that leaves nothing of the groove

The test now reaches the path where the half-space keeps nothing, which is
the one that must fall back to the square stop.

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

* Thread at a shoulder: say so when the shoulder can't be checked

If the kernel can't make the rising-shoulder check, the groove keeps the square
stop as before, and the feature now warns that its last turn is left uncut.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 02:43:36 +00:00
github-actions[bot] 46a9cf80b8 Regenerate generated files and fold pending files [skip ci] 2026-11-04 01:45:15 +00:00
jyviko f74c6605c4 Fillet: Mitre or Swept corners where two rounds meet (#11498)
* SMLib wrapper: a swept or mitred turn for constant rounds (Turn)

Brep::blend_dropping takes a Turn: Mitre (the default, as before) or Swept,
which sets solidcore's FilletSession::set_swept_turns before the corners are
made. A swept turn solidcore refuses at CreateFilletCorners comes back as
Error::TurnRefused with its reason and place (NoRoom at a concave side edge,
else CornerUnsupported). Swept is for Blend::Round only.

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

* Kernel: fillet rounds turn in a mitre or swept (FilletTurn)

fillet_removing, fillet_max, fillet_max_each and fillet_each take a
FilletTurn and hand it to SMLib, through the reblend of earlier rounds at a
later corner too. A swept corner solidcore does not build is refused as
invalid input with its reason and place: no smaller radius builds it.

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

* Design: a fillet saves its turn, mitre (default) or swept

FeatureKind::Fillet gets `turn` (FilletTurn: mitre, the default and not
saved, or swept), one pending format file. Evaluation hands it to the kernel,
pattern copies included, and refuses a swept turn with a style, fit, corner
or setbacks the kernel's swept corner does not take (SWEPT_TAKES).

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

* solid.fillet: the turn parameter, and the command's input and output schemas

`turn: mitre|swept` on solid.fillet, refused when unknown or with options a
swept corner does not take. solid.fillet now has its contract (FilletParams)
and reply type (FilletResult), registered with checked!, and leaves
schema/unconverted.txt.

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

* Fillet dialog: a Turns choice, Mitre or Swept

A Constant radius fillet's Turns row sends `turn: swept`; choosing a narrowed
radius or a curvature corner puts it back to Mitre, and Swept puts those back.
Its tooltip says what each does where two rounds turn round a corner edge.
New strings in a pending locale file (en, de, ja, ko, zh); glossary entry.

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

* Fillet turn: the schema tests' fixes and the complete catalogs' strings

The no-contract example in the schema tests is solid.chamfer now that
solid.fillet has one; the reply test checks the mid-radii form against the
contract (solidcore refuses that law's corner yet, #430) instead of building
it; Arabic, Hebrew and Vietnamese, complete catalogs, get the new strings.

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

* Fillet schema test: variable and spline forms checked against the contract

solidcore fails a variable round on a box edge at this pin (status 1001, as
variable_radius_on_a_box_edge), so the reply test checks those forms against
the contract instead of building them.

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

* Fillet turn: the swept rig cases pass; the MCP no-contract example is solid.chamfer

cov-fillet-turn-swept-boss-base-r1 and
cov-fillet-turn-swept-pocket-sharp-corner-refused-r1 pass now that
solid.fillet takes `turn`: off the rig's known-failing list. The MCP bridge
test's example of a command without a contract was solid.fillet, which has
one now.

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

---------

Co-authored-by: jyviko <2501648+jyviko@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 20:36:00 -05:00
github-actions[bot] 215cc17c52 Regenerate generated files and fold pending files [skip ci] 2026-11-04 01:33:39 +00:00
Nathan Dane c5bc849913 Blur: keep themed backing and notice actions accessible (#9810) 2026-11-04 00:52:09 +00:00
leValdemar19 6178d1a45e Selection fields: remove one picked item from the dialog (#3683) (#4232)
* claim: #3683

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

* Selection fields: remove one picked item from the dialog (#3683)

A dialog field that takes several picks (edges for Fillet, Chamfer or
Flange, faces, bodies) showed "N selected" with one x that cleared them
all. It now has the kit's disclosure arrow beside that x: open, it lists
the picked items by name, each with its own remove button (the rows
Sketch Modify dialogs already used), in a short scroll area. Removing an
item works like picking it again to toggle it off: the hand-picked edges
of a Fillet and the explicit profile choice of Extrude, Revolve and
Emboss follow. The x still clears everything and folds the list.

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

* Selection fields: hovering a picked row lights its item; every multi field lists alike (#3683)

Review follow-up. Hovering a row of a dialog's picked-items list, or its
remove button, lights that item in the view with the normal hover shading
(and not as picked): faces, edges, vertices, profiles, planes, axes and
sketch geometry as the hover, bodies, components and features as their
Browser and timeline rows light them.

Every multi-pick field now has the same folded list: Sketch Modify fields
lose their always-open list for the shared toggle, and Shell with Tangent
Chain lists the faces its chip counts (removing a face its chain brought
takes out the pick that brought it). Removing a row works as picking the
item again: a rim or surface split into pieces is one row and goes whole.
Tests cover removing a row from Combine, Loft rails, Sweep path, Blend To
edges, Rigid Group components and pattern features: the preview's
commands follow.

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

* Selection fields: build on main's fallible world state; Sketch Mirror's label test opens the list (#3683)

The world state is now a Result on main: the remove and hover paths treat a
failed one as nothing to remove or light. The Sketch Mirror label test from
#10770 opens the picked list first, as this field now lists its picks behind
the arrow like every other multi-pick field.

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

* Selection fields: removing a Tangent Chain row takes out the pick that brought it (#3683)

With Tangent Chain on, a Fillet's list shows every edge of each picked
edge's chain. Removing one of those rows left the picked edge behind, so
ticking Tangent Chain off and on brought the chain back. The row now goes as
picking it again would: with the picks whose chain holds it and their whole
chains. A Shell row whose model can't be built goes on its own instead of
silently staying.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 23:45:06 +00:00
jyviko e12afd11cc Ellipse and spline extrudes make one wall; coplanar faces never lose their names to a budget (#11687)
* Planar sources: decide coplanar faces by support group, no budgets that drop sources

map_planar_sources kept two budgets, `metadata` (2 MB) and `identity_bytes`
(16 MB), charged by each face's source descriptor and by every extra
candidate on its plane. When one ran out, faces silently lost their source
and with it their persistent name: a 300-hole plate whose top a comb of
grooves cuts into 62 strips lost the cap source from strip ~47 on.

The budgets bounded the real cost: each result face looked at every
candidate on its plane, faces x candidates when many faces are coplanar.
The candidates are now grouped by support once (`same_support` against
each group's first plane, through the existing PlaneIndex), each group's
chosen source, ambiguity and inner flag decided once, and the faces
without a role together with the competing tool faces grouped the same
way. Each result face then looks up its plane's few groups. The decision
is the old one: blocked by an unknown or competing support, else the
first candidate on the plane unless another candidate there has a
different plane, source or role.

Work: grouping looks at each candidate's index window once per group
(each candidate is assigned on its group's first look), and each face
looks at the group representatives near its plane, so the planes looked
at grow with faces + candidates. A counter (`looked`) checks it: n
pieces of one cap and n faces on its plane take exactly 2n looks (n*n
before), 3n with n blocking supports.

Tests: the holed plate keeps the cap source on all 62 strips and the
bottom (fails on the old code: face 352 lost its source); the existing
planar source tests pass.

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

* Extrude: an ellipse or a spline sketch curve sweeps one wall

An extruded ellipse had four side faces (`F2:side:e1#1` to `#4`), one
per conic quarter, and a spline one per span: the profile holds such a
curve as several exact segments and SMLib got each as its own curve.

The Extrude feature's builder (`extrude_whole_sides`) now takes a
`one_curve` test from the caller. Each run of free-form segments of one
degree that it says are one curve goes to SMLib as one B-spline: the
Béziers' control points in a row, knots repeated the degree times at
each joint, each span as long as keeps the speed through the joint the
same (so the curve is C1 there and SMLib makes one face over it, as it
would not over a C0 parametrisation), weights scaled to meet. Exact: the
same curve, reparametrised. A loop starts at a run's start, so no run
wraps round it. The wall's history lists every piece, and its
construction source is its first piece's (as joined collinear lines).

The document says which segments are one curve: ExactSketchCurves now
also holds each free-form sketch curve's exact segments
(`find_free_form`: a segment that is one of them, either way round), and
the profile roles use it, so the wall is named `side:<curve>` from its
history rather than by the curve nearest its mesh centre.

Saved designs rebuild with these walls renamed (`F2:side:e1#k` ->
`F2:side:e1`); accepted, no alias kept.

Tests: kernel `pieces_of_one_curve_extrude_into_one_wall` (an ellipse in
four conics is 3 faces, its volume pi a b h to 1e-6, the wall generated by
all four pieces; a 3-span spline closed by lines is one wall; the same
solid as a face per piece); doc
`an_extruded_ellipse_or_spline_is_one_wall_named_by_its_curve` (one
`F2:side:e1`, no piece names, the same names on a second build and after
reopening).

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

* Tests: a tray's far rim name is checked on the exact B-rep face

The tray test took the point `sheet:...:rim.far` finds as right when it
lay within 1e-3 mm of the strip z = 30, 199 <= y <= 200: a slack sized to
the mesh. It now asserts on the face the name finds, on exact geometry:
the point's distance to that face's surface (FaceSurface::nearest) is
within rounding at the part's scale (1e-12 x its size), and the point is
inside the face's boundary, counted by crossings of its record edges
(each asserted to be a line, the rim being planar).

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

* Tests: delete tests/naming/, the records no test reads since the naming corpus stopped comparing against them

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

* Viewport test: smooth wall joins drawn from eight G2 spline walls

The PR extrudes each spline or ellipse sketch curve as one wall, so the
offset-ellipse ring the test built has no joins between faces any more.
The test now extrudes a circle drawn as eight cubic splines: eight walls
meeting with one curvature along eight upright edges (24 edges in all).
It still checks that those joins are left out of the shaded view, drawn
in the wireframe and selectable as edges.

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

* Kernel ladder: 009 and 012 record the one-wall extrude (an extruded spline or ellipse sweeps one wall)

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

---------

Co-authored-by: jyviko <2501648+jyviko@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 21:27:14 +00:00
bherbruck 1e0bfca819 Release 0.40.0-rc.2 (#11751)
Bumps the version to 0.40.0-rc.2 and folds the pending format changes into format 50. A published pre-release no longer reaches the Flatpak feed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 15:35:34 -05:00
CamWight fd4b7a7bc0 Seg2: one rule for an arc under a shape-keeping map (#4816) (#7316)
`Seg2::mapped(f, arc)` is a segment under a map of the plane that keeps shapes. Points go
through `f` itself (an arc's centre, every control point). `ArcMap { angle, mirrored, scale }`
says how the map moves an arc: it turns it by `angle`, after a mirror across the x axis when
`mirrored` (start `angle - start`, sweep reversed), and scales its radius by `scale`.
`ArcMap::turn(angle, mirrored)` keeps lengths; `ArcMap::rigid(f)` measures a map of points that
keeps lengths, from where it puts the origin and the unit axes, with a scale of exactly 1.

`Seg2::similar` and the three arc maps written out by hand use it:
- a sheet-metal region onto a panel's flat (`region_to_flat`, which measured its map by hand and
  now asks `ArcMap::rigid`);
- the mirrored flat (`flip_x_loop`);
- a sketch region mirrored in its x axis (`mirror_y`).

The points go through the same functions as before, so every result is the same, bit for bit.
This comes from the architecture review of the drawing work (2026-10-25): four places wrote the
arc rule out, each its own way, and one measured its map by hand.

`map_controls` is private to geom now: on its own it hands an arc back unmapped, which is what the
three hand-written arms were there to avoid.

Tests: every kind of segment under a mirrored map with a turn, a scale and a shift, point by point,
its length scaled and an arc's sweep reversed; the three old formulas, bit for bit (the sketch
mirror keeps a +0.0 start's -0.0); a rigid map's turn and mirror measured whatever its shift, and
the identity; the sheet-metal and mirror tests unchanged.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 14:59:59 -05:00
bherbruck a5b71ca206 Web app: bring designs saved at the old address over to app.solidcraft.io (#11630)
* Web app: a page that hands designs saved at the old address to app.solidcraft.io

The stable web app moved from docs.solidcraft.io/app/ to app.solidcraft.io, and a browser keeps
storage per address, so designs saved at the old one don't appear at the new one. The docs site
gets app-transfer.html: framed by the move page at app.solidcraft.io it answers that page's
requests, and opened on its own it offers each design as a file. It only reads.

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

* Web app: name the browsers the one-step move was checked in

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 19:02:38 +00:00
bherbruck 381ff99f70 Desktop: one design's long kernel call no longer stops other designs (#11684)
solidcraft-smlib held one process-wide kernel lock for the whole of every
solidcore call, so a long call in one design (a modeled thread, a fillet)
held up every kernel call in every other design, even after its tab was
closed, and a call that never returned held them until restart.

Each call now takes a kernel from a pool and the lock is only held to take
or put one back. Calls one after another get the same kernel as before; one
that starts while another runs gets its own. Each kernel has its own range
of procedural blend serials, since a body keeps the serials of the kernel
that made it.

History commands and delete checks now belong to their design: they wait
with its tab, go when it closes, and can't land in another design. An
agent's command for a design that left the window is refused at once
instead of holding up the requests behind it.

Closes #11681

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 19:00:01 +00:00
kaltenbrunnermarkus 160ee037cc Sketch dimension: typing replaces the proposed value (#11204) (#11214)
* claim: Sketch dimension: typing replaces the proposed value (#11204)

* Sketch dimensions: typing replaces the proposed value until you move the caret (#11204)

The value box selected the proposed value only on its first frame. A frame
in which the field misses the keyboard collapses egui's selection to its
caret, so typing 400 over a proposed 40 mm could give 40040 mm (seen in a
WebView2 host). Now every frame starts with the whole value selected, before
the field reads its state, until the person takes the value over: typing, a
completion or reference, a key in the box, or a press into the box after it
stayed in place. The click that placed or pinned the value does not count.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 18:47:40 +00:00
rlartot 267d8d646e French: catch up with main (125 messages) (#11277)
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-03 18:47:32 +00:00
Nathan Dane 6ff5ca6bc5 Preferences: one heavier, evenly detailed icon set for the page tabs (#11368)
* Preferences: one heavier, evenly detailed icon set for the page tabs

* Build: import traced body records from their new owner

* Build: read exported kernel records from their new crate
2026-11-03 18:47:24 +00:00
github-actions[bot] 556a9c95ef Regenerate generated files and fold pending files [skip ci] 2026-11-03 19:11:45 +00:00
github-actions[bot] 277c112de7 Regenerate generated files and fold pending files [skip ci] 2026-11-03 19:00:00 +00:00
Nathan Dane ec31745f0a Preferences redesign 10/11: Navigation and 3D mouse (#9882) (#10739)
* Preferences: preserve Navigation and 3D mouse alongside other panels

* Tests: check preference search visibility after scrolling settles

* Tests: identify preference search results through indexed row handles
2026-11-03 18:34:34 +00:00
CamWight 664c9812d2 Paper scene: a sheet's pieces on paper are capped, refused by name past 4,194,304 (#4890 A1) (#11709)
A run's glyphs count once in a scene's records, for a writer that embeds its font, but the
walk onto a writer's grid copies every placed glyph's whole outline, so a sheet dense with
text could reach any size. The walk now counts the pieces it places and refuses a scene past
MAX_GRID_PIECES (1 << 22), saying to move some text to another sheet. place() is public so
a writer that draws text as outlines (PDF) counts with the same rule.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 13:20:44 -05:00
Nathan Dane 5e2a1460ff Preferences redesign 9/11: Sketch (#9881) (#10720)
* Preferences: preserve Sketch redesign alongside Graphics

* Preferences: share hinted and unhinted blocks across independent panels

* Tests: wait for preference search scrolling before checking visible targets

* Tests: locate preference search results by their indexed rows
2026-11-03 18:14:36 +00:00
github-actions[bot] 27808612a1 Regenerate generated files and fold pending files [skip ci] 2026-11-03 18:20:08 +00:00
Nathan Dane 92a9a5e0f0 Preferences redesign 6/11: Layout (#9878) (#10638)
* Preferences: preserve Layout controls alongside the other redesigned panels

* Tests: locate the redesigned Layout checkbox by its visible label
2026-11-03 17:34:24 +00:00
semmelrinde 97cedc77e8 Bosnian: translate the 78 messages main gained after batch 11, the Assistant's model picker and render comparison included (batch 12) (#11007)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 17:20:40 +00:00
semmelrinde bcac191847 Typed schemas for Change Design Units (part of #2829) (#10934)
document.units gets input and output schemas: `units` (mm, cm, m, in or
ft, with the handler's messages for a missing or unknown unit) and its
reply, `units` and `kept`, which the handler now builds from the reply
type. Its line leaves unconverted.txt.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 17:13:37 +00:00
bherbruck c019e2555c Threads: a 12 mm hole now takes an M12 thread, and a modelled M12 builds in it (#11651)
* Threads: a 12 mm hole takes M12, and a modelled thread narrows a bore that is too wide (#11609)

Automatic sizing of a hole now takes the size whose nominal diameter the hole has (a 12 mm hole
is M12, not M14, the size whose tap drill is 12 mm); a hole near no nominal diameter is still
matched by tap drill. A modelled internal thread in a hole as wide as the thread or wider no
longer refuses with "too wide": the bore is filled in to the thread's minor diameter over the
threaded length and the groove cut there, and the feature's warning says so. A size that does
not fit the hole names the size that does.

A saved thread with no designation keeps the old rule through a pending format step, so an
existing design rebuilds with the size it had.

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

* Threads: keep an old automatic size through edits; kernel-rig cases expect the narrowed bore (#11609)

Editing a saved automatic thread keeps `old_auto_size`, so it does not change size. The five
kernel-rig cases that expected a modelled thread in a hole as wide as the thread to be refused
now expect the narrowed bore, with the volume worked out analytically in each case. The thread
dialog's reopen pattern names the new field.

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

* Thread dialog test: a 5 mm hole is suggested M5, its nominal size (#11609)

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 17:09:27 +00:00
semmelrinde 2741df3629 Typed schemas for Appearance, Appearance Library and List Appearances (part of #2829) (#10976)
* Typed schemas for Appearance, Appearance Library and List Appearances (part of #2829)

appearance.assign, appearance.library and appearance.list get contracts
that read what their handlers read, with the handlers' messages, and
reply types the handlers build their replies from. Their lines leave
unconverted.txt.

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

* Appearance library: the opacity is already a number

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 16:52:09 +00:00
Nathan Dane 1094503e97 Preferences: share picture choices without conflicting with panel redesigns (#10937) 2026-11-03 16:37:55 +00:00
github-actions[bot] 9f61296014 Regenerate generated files and fold pending files [skip ci] 2026-11-03 17:06:33 +00:00
Nathan Dane f3977d5c2d Preferences redesign 8/11: Design and Tools (#9880) (#10710)
* Redesign Design and Tools preferences

* Refine Tools: icon rows for Create, dashed Assembly Move ring, gradient cursor trail

* Preserve current behavior when rebasing Design and Tools preferences

* Preferences: keep Design search aliases compatible with queued panels

* Preferences: let Design and Graphics helpers merge independently
2026-11-03 16:16:21 +00:00
Nathan Dane 908fe48c0b Preferences: group Shortcuts and use cards for Assistant and Plugins (#9883) (#10765)
* Preferences: group Shortcuts and refine Assistant and Plugins

* Preferences: keep Assistant search additions compatible with other panels
2026-11-03 16:09:05 +00:00
CamWight 08cd8d6442 Drawings: title blocks on sheets, and a drawing can start from or be restyled by a template (#3875) (#7208)
* Drawing sheets: a copy of a title block on the drawing border, saved with the sheet (#3875)

`Sheet.title_block` holds a copy of its definition, as `border` does. Each sheet's check lays it
out on the drawing border, so a sheet without a border, a block wider or taller than the border,
or a change of paper that leaves no room is refused. `drawing.sheet.add` and `drawing.sheet.set`
take `title_block` (a definition, or null for none), a new sheet takes the block of the sheet it
follows, and `drawing.inspect` lists it. A sheet without a block saves no `title_block`, so an
older drawing reads as having none (pending format file `sheet-title-block.rs`).

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

* Drawing sheets: the built-in title block, on every new drawing (#3875)

`title_block::default_block()` is the 180 x 54 mm block of twelve cells approved on #3875, with
the legal owner read from the drawing's `organization` and the general tolerances as text to
change. `drawing.create` starts its sheet with it. The geometry tests read the built-in block
instead of their own copy of it.

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

* Drawing sheets: a title block's lines on paper, drawn after the views (#3875)

`TitleBlockLayout::paper` draws the outline, then every cell edge inside it, each once:
neighbours' shared edges, and edges that meet along one line, are one stroke. The sheet scene
paints the block after the views, and counts its records with the border's, so no view takes
its room. The view tests' drawing leaves the block off, so their scene is still the border and
the views.

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

* Drawing sheets: the title block's projection symbol, in the drawing's projection (#3875)

ISO 5456-2 (Annex A): a truncated cone and its end view, h across the cone's
small end from table A.1's sizes (3.5 to 20 mm), the largest that fits the
cell, lines h/10 wide. The end view is on the right in first angle and on
the left in third. The block only says a cell holds the symbol; the angle
comes from drawing.projection, and the sheet scene redraws when it changes.

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

* Drawing sheets: a title block's fields, and text typed over its cells (#3875)

A title block cell reads its fields from the component the sheet documents (its
name, part properties, material and mass from the bill of materials), the
drawing, the sheet (number and count among the counted sheets) and the newest
revision row; a field with no value shows "-". A key its source doesn't have is
refused when a block is given or read. Sheets save text typed over their cells by
cell name; drawing.inspect lists each text cell with its value beside an override.

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

* Drawing templates as data: a drawing with no model, and ISO built-ins (#3875)

A template is a drawing with no sources, views, dimensions, balloons, revision
rows or documented components, so it is saved and read as a drawing is. A new
drawing is a copy with ids of its own. The built-ins, ISO A4 to A0 for parts and
for assemblies, are made in code when asked for and differ only in their general
tolerances.

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

* Drawing templates: drawing.create from a template, and the preferred ones (#3875)

drawing.create takes template? (default ISO A3, for a part or, for a design with
components, an assembly) and projection? over the template's. The new
drawing.template.list lists the built-ins and marks the one a new drawing starts
from. Preferences add a template for part drawings and one for assembly drawings;
the app fills in the preferred template and projection before drawing.create
runs, so a template named keeps its own projection, instead of changing the
projection after the drawing is made. A saved template this version doesn't know
reads as the default.

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

* Drawing templates: apply one to the drawing open, moving nothing (#3875)

drawing.template.apply {template, dry_run?} gives each sheet the paper, drawing
border and title block of the template's sheet in its place (its last for the
sheets past its own). Text typed over a cell stays while the new block has a
text cell of that name; the cells whose text is dropped are listed. Nothing
moves: the views a new paper leaves outside the drawing space are listed. A dry
run answers the same and changes nothing; applying is one undo step, none when
nothing changes.

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

* Drawing sheets: views a title block covers are listed, never moved (#3875)

A view lying over its sheet's title block is listed with the ones the drawing
space leaves out, after a change of paper or block (drawing.sheet.set) and when
a template restyles a sheet. Where a block sits is one rule, the one a sheet is
checked by (Sheet::title_block_outline), and the overlap of two boxes is one
rule in Aabb2::overlaps, which the title block's own cell check now uses too.

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

* Drawing templates: save one as a file, and start or restyle drawings from it (#3875)

drawing.template.save {path} writes the drawing open as a template: its format,
and nothing it draws or documents. drawing.create and drawing.template.apply take
a .json template file as well as a built-in's name. A file is read as a drawing
is, then held to the template rules; files are read and written through the same
file check and atomic write as the sheet export. A drawing made from a file keeps
its own copy, so a later save changes no drawing.

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

* Drawing sheets: the built-in title block is 180 x 49 mm (#3875)

The top row is 15 mm, Sheet is 30 mm wide, lined up with Mass, and Date of
issue is 32 mm. The top row now holds one line of the legal owner, two of the
general tolerances and the 3.5 mm projection symbol.

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

* Drawing sheets: one operation sets a sheet's title block, and drawing.sheet.set names the typed text it drops (#3875)

A sheet's block was set in two places: drawing.sheet.set kept the typed
text of the text cells the new block still has, and drawing.template.apply
did the same and listed what it dropped. Sheet::set_title_block is now the
one rule both use. drawing.sheet.set's reply gains `lost`, the cells whose
typed text a new block drops, as drawing.template.apply's reply has it, and
its help says that a view over the title block is listed too.

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

* Drawing templates: the built-ins and a new drawing start with the border only; the built-in title block is added by name (#3875)

A template now holds only the ISO border by default. A title block,
tables, references and text are added to a drawing, and a template saved
from it keeps them.

- The ten built-ins (iso-a4-part ... iso-a0-assembly) become five,
  iso-a4 to iso-a0: one sheet with the ISO border. Without the block, the
  part and assembly versions were the same.
- drawing.create with no template starts from iso-a3. The two
  preferences for parts and assemblies become one, drawing_template,
  "Template for new drawings".
- drawing.sheet.add and drawing.sheet.set take title_block: "built-in".
  The built-in block's general tolerances are a part's or, for a design
  with components, an assembly's, through Sheet::set_title_block.
- An old name such as iso-a3-part is refused with the list of names;
  drawings haven't shipped.

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

* Drawing title blocks: a drawing with no number of its own is numbered by the component its sheet documents (#3875)

The identification number cell read the drawing's part number property,
and showed "-" for a drawing that has none, which is every new one. Now
such a drawing shows the name of the component the sheet documents, so a
new drawing of a part is numbered by the part until it is given a number.

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

* Drawing templates: a template saved from a drawing keeps typed text only over plain text cells (#3875)

A template saved from a drawing kept all the text typed over its title
block's cells, including text over a cell that shows a field, such as
the material or the title, which belongs to the design the drawing
documents. Now it keeps the text typed over cells of plain text, such as
the general tolerances, and leaves the rest behind.

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

* Drawing title blocks and tables: a field without a value shows nothing (#3875)

A title block cell or a table row whose field has no value (a property
nobody gave, a row field its row doesn't have, an uncounted sheet's
number) now shows nothing instead of "-".

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

* Drawing title blocks: the built-in block's general tolerances start empty (#3875)

The general tolerances cell of the built-in title block now starts empty for
parts and assemblies alike, rather than ISO 2768-m in the block and a part's
or an assembly's text added by drawing.sheet.set. A shop types its own, or
its template carries them, and an empty cell shows nothing.

- template::PART_TOLERANCES, ASSEMBLY_TOLERANCES, for_assembly and
  built_in_block go: "built-in" is title_block::default_block.
- drawing.template.list's help no longer speaks of part and assembly
  built-ins.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 11:01:13 -05:00
bherbruck 38ca98f88b Meshing curved faces: evaluate spline basis tables in place (#11648) (#11649)
Every point on a spline surface returned its basis tables by value, up to
272 bytes several times over. Filling them in place takes about a quarter off
meshing a round extrude's wall, with the same numbers.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 15:50:59 +00:00
Mateusz Sych bfb32cd6e1 Align: move bodies by frames that follow sketch and model geometry (#6257) (#7123)
* claim: #6257

* Align: move bodies by frames that follow sketch and model geometry (#6257)

solid.align takes from_frame and to_frame: a joint origin on the model
(a face's or round end's centre, an edge's middle, a corner, found again
by name) or a sketch point or a sketch circle's, arc's or ellipse's
centre. Both are resolved on every evaluation, so the bodies follow
their target when it changes. An optional angle turns the bodies about
the target's direction after the move.

The Align dialog takes sketch points and curves as From and To, offers
the Joint Origin snap and an Angle row.

* Align frames: box the joint origin (clippy large_enum_variant)

* Align frames: every moved body on the frame's component, placed once

* Align frames: a sketch id's length in characters

* Align frames: the dialog test's sketch curve hit carries its point

* Align frames: the dialog test takes Vec2 from solidcore-record

---------

Co-authored-by: MateuszSychXpl <151755914+MateuszSychXpl@users.noreply.github.com>
2026-11-03 15:47:52 +00:00
Hainlein4711 a8bc6b2945 Construct: allow axes in Point At Edge And Plane (#11141) (#11152)
* claim: #11141

* fix(ui): accept axes for point at edge and plane (#11141)
2026-11-03 15:46:17 +00:00
jyviko 81e55b5443 New kernel cases go to solidcore's corpus; the rig's case list only shrinks (#11646)
* New kernel cases go to solidcore's corpus; tests/kernel-cases/ only shrinks

tests/kernel-cases/CASES lists every rig and ladder case there is today. `cargo xtask lists`
fails on a case directory it does not list, saying where a kernel case goes instead
(solidcore's crates/solidcore/tests/kernel_cases/, exported with `cargo xtask kernel-cases
--export` or written by hand), and on a listed case that is gone. AGENTS.md, CONTRIBUTING.md,
the tests skill, the rig README and EXEMPT say the same; a new command gets an EXEMPT line
naming where it is tested.

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

* CONTRIBUTING: the kernel-cases paragraph ends before the naming corpus one

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

---------

Co-authored-by: jyviko <2501648+jyviko@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 15:42:09 +00:00
github-actions[bot] 138e3478e6 Regenerate generated files and fold pending files [skip ci] 2026-11-03 15:58:19 +00:00
CamWight c5dab0b9a8 Paper scene: ellipses, hyperbolas and higher Béziers as cubics on the grid (#4890 A3b) (#9846)
A conic of weight other than 1 and a Bézier above degree 3 become cubic Béziers within
1e-4 mm of the curve (`paper::cubic_pieces`), so the walk takes them and every writer
draws them with the cubics it already writes. Each piece's cubic has the curve's ends
and end tangents; its distance is bounded from the Bernstein form, not sampled: for a
Bézier, by the control points of its difference with the cubic raised to its degree;
for a conic, by Q·D − N over the denominator's least value. A piece over the error is
halved exactly and tried again, up to 4096 a curve and 1,000,000 a scene; past that the
curve is refused, never drawn coarser. The walk counts the cubics it made. Circular arcs
are left to the shared `paper::cubics` helper.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 09:27:36 -05:00
Nathan Dane fbd30b0bd0 Panels: inspect and change layouts through commands (#11304) (#11454)
* claim: #11218

* Panels: add shared layout rules and retained content runtime

* claim: #11304

* Panels: inspect and change layouts through shared commands (#11304)

* Panels: account for layout commands in geometry coverage
2026-11-03 13:55:50 +00:00
Nathan Dane 372136fa6a Preferences redesign 7/11: Graphics (#9879) (#10662)
* Preferences: keep Graphics redesign independent on current main

* Preferences: repair Graphics search targets and CI regressions
2026-11-03 13:27:19 +00:00
CamWight cca77b176f Drawings: a shaded view is drawn on the sheet as a picture under its lines (#5624) (#10926)
* A drawing's list of one kind of record: ById, with balloons first (#5426)

Each kind on a sheet writes out the same add, find by halves, cap, order and check loop. ById holds
those, so a kind's own file holds only its own rules, and the file's bytes don't change: it saves as
the plain list. Views and dimensions stay on their own lists for now, since other work is on them.

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

* A drawing keeps an image's file once, however many places it sits (#5426 W38e-1)

images holds the files and sheet_images each place one sits, so a logo on every sheet costs what one
does. drawing.image.add reads the file (bytes and pixels checked from its header before anything
decodes it), reuses a file with the same bytes, and saves the bytes without the path. A file goes
with the last thing that uses it, which Drawing::images_in_use alone says, and reading a drawing
refuses a file nothing uses or a record its file disagrees with. A sheet's list and delete take the
placements on it. Deleting one placement waits for selection.delete to take a drawing's objects. Its
pixels on paper are W38e-2.

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

* Sheet images: an image file's pixels on any paper grid (#5425, W38e-2)

A PNG or JPEG decoded once, then stretched over its paper rectangle onto the pixel grid a writer
asks for: a box filter in linear light with alpha premultiplied, so a shrunk logo keeps its edges
and gains no dark fringe. The size is read from the header and refused over 64 megapixels before
anything is decoded. An EXIF orientation is applied; colour profiles are not read.

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

* Sheet images: a placement in the sheet's scene and its SVG (#5425, W38e-2)

Each image placed on a sheet is a group of the new kind `sheet_image`, between the drawing border
and the views, so a view's lines draw over a photo. `drawing_pictures` holds both that group and
the provider a writer asks for its pixels, so the two read the same placement.

The provider holds what one sheet uses, decodes a file when it is first asked, once however many
placements show it, and refuses a sheet whose files hold more than 64 million pixels, from their
headers, before anything decodes. `drawing.export` passes it to the SVG writer in place of
`NoPictures`, and a file that won't decode refuses its sheet by name.

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

* Render: pin the product image's output before it is drawn in tiles (#5624, W17b S1)

The decoded rows of five product renders (orthographic on a transparent background, perspective on a
solid one, a PBR surface, two glass layers sorted back to front, triangles crossing every edge of the
picture), taken on this tree. The next change reshapes ImageTask into a prepare stage, tiles and a PNG
sink; they must not move. The PNG bytes are not pinned: their compression depends on the deflate
backend the build unifies (flate2's zlib-rs when the UI crate is built alongside, miniz_oxide
without), not on this code.

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

* Render: pictures drawn tile by tile into any pixel window (#5624, W17b S1)

The CPU product renderer, reshaped so drawings can use it: one rasteriser (`Raster`) prepares a
scene once (appearances, projected triangles, glass sorted back to front), then draws each tile of
the picture on a canvas of its own. A sample's position comes from its whole-picture index, so tiles
put side by side are the picture drawn whole, to the byte.

- `ImageTask` (render.image) is that rasteriser over one whole-picture tile from the product camera,
  then its PNG: the same output, pinned by the previous commit's rows.
- `PictureTask` draws a `PictureRequest`: a `Projection` (the product camera, or an `OrthoFrame`,
  W17a's view frame as plain data: a `Plane` basis, a scale and a paper shift, on a
  `geom::pixel_grid::PixelGrid`), any list of `PixelWindow` tiles, `Supersample::One` (pixel centres)
  or `Two` (i + 1/4 and i + 3/4), a `BodyRole` per body (`DepthOnly` hides what lies behind it and
  draws nothing itself) and an optional background (none: transparent). Each tile comes back as
  straight-alpha sRGB RGBA rows, blended in linear light.
- The orthographic map is the frame's paper map, onto the grid, so lines drawn from the same frame
  register with the pixels (to 1e-9 mm in the test).
- Limits: 65,536 px a side, no total cap; each tile under the resolver's `RenderLimits`, as the
  product image is; at most 65,536 tiles; the prepared lists as a product image's; pixel tests up to
  8 per sample of the picture (128 M for a product image, as before), counted at admission by the
  one projection the raster uses, as main counts a product image's.

Not here: the drawing light, stored per-tile bins (S2, measured), the paper scene's picture item
(S3), 4x sampling, and `raster::render`, the snapshot rasteriser, which stays as it is.

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

* Render: time the tile renderer (#5624, W17b S2)

An ignored timing test, as `simplify_timing` is: a 65,548-triangle front view drawn as the
product image, as one 300 dpi picture, and in 64-row strips at 300 and 1200 dpi, each the median
of three runs. It is the measure S2's speed-ups are judged by.

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

* Render: a tile passes over a triangle outside it before painting it (#5624, W17b S2)

Each tile placed and painted every triangle of the picture, then dropped those outside it: the
appearance lookup, the PBR normals and the colour decode ran for every triangle in every tile. A
triangle's work is now three steps, `corners`, `look` and `place`. Preparation runs them in
today's order, so its checks and errors are the same. A tile places a triangle first and paints
only those inside it. What is drawn doesn't change: the pin and the tile tests stay as they were.

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

* Render: one light per picture (#5624, W17b S2)

`raster::shade` normalised its key light and highlight directions again for every sample,
although both depend only on the view. `raster::Light` works them out once per view and shades
with them. The tile renderer and the snapshot rasteriser (`raster::render`) both use it, so the
product light is written in one place. The arithmetic is the same, so every pixel is too.

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

* Render: sRGB codes from tables (#5624, W17b S2)

Each shaded sample was stored as sRGB through a power (three per sample), and each output pixel
was averaged from its samples through twelve more and written back through three: the powers
were most of a sample's arithmetic. Decoding is now a 256-entry table of `geom::srgb`'s own
values (#7162), which also ends this file's own copy of the formula. Encoding counts the code
starts (the linear values halfway, in sRGB, between two codes) at or below the value. Within a
billionth of a start, where the formula's rounding could fall either way, `srgb::encode8`
decides, so every code is the one it gives. A test checks both against `geom::srgb` at every
start, the numbers around it, the band's edges and a million values in and outside 0 to 1.

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

* Render: a tile's passes run in loops (#5624, W17b S2)

The raster went back through its step loop for every sample: the cancel check, the stage match,
the tile's window and its canvas bounds, once per sample cleared, tested or averaged. Clearing,
the opaque and glass passes and the downsample now each loop on their own up to the step's budget,
counting a unit as before; preparation and the glass sort still take one unit a call. A step
still checks for cancellation before each stage's run and still does at most 16384 units, so the
host's slices and the progress counts are the same.

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

* Render: tests for a picture's light, glass order and test budget (#5624, W17b S2)

Three planted faults went through the image tests; each now has one
that fails:
- an orthographic picture lit from another direction than its eye: a
  face square-on to the view is the colour the product image gives it;
- glass placed by a triangle's first corner, not its middle: a tilted
  square whose first corners lie in front of the near one, its middles
  behind;
- a picture held to the product image's pixel-test budget: a face
  across most of an A0 sheet at 1200 dpi is prepared.

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

* Drawing sheets: a shaded view's picture on paper (#5624, W17 V9b)

A saved view in a shaded style draws on its sheet and in drawing.export:
draw puts one picture per entry of the view's parts under its lines, and
ViewPictures renders each on the writer's grid through the frame that
placed the lines. SheetPictures answers for a sheet's images and views.
The styles with lines stay refused until W06's exact lines.

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

* Render: a picture's count of its tiles and its fixed limit, as #6483 has them (#5624)

A triangle counts only where it meets the tiles asked for, and a picture has a fixed 400 M pixel tests,
as #6483 at bf93e890e. V9b's stack holds an older copy of that slice; this drops away when drawings
takes main. PictureTask says what admission counted, for the view's running sum.

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

* Drawing views: a shaded view's pictures add up to one limit (#5624, W17 V9b)

A writer asks for a picture a band at a time, and each request is limited on its own, so ViewPictures
keeps what each view's requests have counted and refuses the next one past the limit, by name, before it
paints. That makes 400 M a bound on a whole view.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 08:21:51 -05:00
Frank 2aa45ef3ee GPU path tracer: compile the path pipeline one at a time per process (#6706) (#6813)
Each path test opens its own device and compiles the path pipeline. On a
cold driver cache that takes minutes and gigabytes per compile, and libtest
ran one per CPU thread side by side: on NVIDIA the solidcraft-gpu tests ran
for hours and committed ~80 GB. Executor::new now holds a process-wide lock
while it compiles; the first compile fills the driver's cache and the
waiting ones get the pipeline from there.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 13:04:23 +00:00
jyviko 41d6a9244a Merge pull request #11640 from tangency-labs/jyviko/kernel-cases-export-record-import
Build fix: the kernel-cases export takes the record type from solidcore-record (rest of #11626)
2026-11-03 10:08:12 -05:00
github-actions[bot] 8affa93051 Regenerate generated files and fold pending files [skip ci] 2026-11-03 15:03:56 +00:00
bherbruck 603c974f37 Fix main's build: the CLI imports the B-rep type from its new home (#11634)
The CLI's kernel_cases_export.rs still imported Brep from solidcraft_geom::brep after the layer split moved it to solidcore_record::brep. Changes this one import.
2026-11-03 09:52:19 -05:00
jyviko 98480ac83d Kernel-cases export: the record type comes from solidcore-record
The second half of #11626: apps/solidcraft-cli/src/kernel_cases_export.rs (#11568) imported
solidcraft_engine::geom::brep::Brep, which #11496 moved to solidcore_record::brep::Brep.
cargo clippy --workspace --all-targets -D warnings is clean with it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtL5M41G8Nua7HvuFPZkmJ
2026-11-03 09:35:51 -05:00
jyviko c553365a21 Merge pull request #11626 from tangency-labs/jyviko/trace-record-import
Build fix: the kernel trace takes the record type from solidcore-record (main red since #11496 + #11568)
2026-11-03 09:27:31 -05:00
jyviko 541e743d7c Kernel trace: the record type comes from solidcore-record
#11568's trace (crates/smlib/src/trace.rs) named the record as solidcraft_geom::brep::Brep,
and #11496 moved that type to solidcore_record::brep. Each merged green on its own; together
main does not compile (error[E0425]: cannot find type Brep in module b).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtL5M41G8Nua7HvuFPZkmJ
2026-11-03 09:18:31 -05:00
jyviko dc38349cbf Layer split stage 1: the B-rep record, native body file and Vec/Plane come from solidcore-record (#11496)
* Kernel: the B-rep record and the native body file come from solidcore-record

SolidCraft's portable B-rep record (`solidcraft_geom::brep`: brep.rs, topology, history,
envelope, part, curve2) and the native body file (`solidcraft_kernel::native`: the tagged binary
codec and the container) move to solidcore's `solidcore-record` crate: geometry as data, with
structural validation and no maths (solidcore's docs/layers.md, stage 1). Every caller uses
`solidcore_record::brep` and `solidcore_record::native`; the old copies are deleted.

What computes from coordinates stays with the kernel, in geom's `brep`:
- `brep::check`: the record's geometric checks (orthonormal frames, non-degenerate planes,
  unit revolution axes, non-singular placements). `check::validate` and
  `check::validate_built` are the record's validation and these together, and every caller of
  `Brep::validate` uses them, so what is accepted is unchanged;
- `brep::label`: a surface's natural ranges (a cone's apex), `curve_interval`,
  `edge_curve_interval` and `form_turns`;
- `brep::pcurve::eval`: a pcurve's point;
- `geometry::check`, `placement::moved` and `split_edges` become functions on the record.

The kernel's native module keeps the conversion between a body and its saved record
(metadata.rs) and checks the decoded record's geometry. `native/analytic.rs`, which no module
declared, is deleted.

solidcore-record is pinned to its rev on solidcore's `layer-record` branch.

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

* Geometry: Vec2, Vec3, Plane and the profile types come from solidcore-record

The plain data of vectors and points, sketch planes and planar profiles (`Seg2`, `Loop2`,
`Region2`) moves to `solidcore-record`, with its constants and component-wise operators only
(owner decision, solidcore layer split). Their geometry stays in geom as traits:
`Vec2Geom`, `Vec3Geom`, `PlaneGeom`, `Seg2Geom`, `Loop2Geom`, `Region2Geom`, `Seg2ArcFit`,
`Loop2ArcFit` and `Region2ArcFit`, in scope with `use solidcraft_geom::prelude::*`. Every caller
names the types from `solidcore_record`; geom no longer defines or re-exports them.

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

* Kernel: pin solidcore-record to solidcore main f5e8e9a29

solidcore-record is merged on solidcore main (tangency-labs/solidcore#2519); its crate there is
identical to the rev pinned before but for its clippy configuration. The lock is regenerated
against the merged rev.

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

---------

Co-authored-by: jyviko <2501648+jyviko@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 09:04:26 -05:00
jyviko bd2dc3fade Merge pull request #11611 from tangency-labs/jyviko/oracle-77-known-failure
Reference tests: the variable round on a box edge (case 77) is a known failure at solidcore 8ddce9016
2026-11-03 08:51:11 -05:00
jyviko d9afdc33ae Oracle: case 77 (variable round r 2 to 5 on a box edge) is a known failure at solidcore 8ddce9016
It fails with status 1001 since #11360 moved SolidCraft onto solidcore alone, as the
kernel test variable_radius_on_a_box_edge already listed in tests/KNOWN_FAILING_TESTS.
The oracle keeps its own list, so main's Reference tests have been red since.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtL5M41G8Nua7HvuFPZkmJ
2026-11-03 08:49:19 -05:00
jyviko 1b51a0678b Merge pull request #11568 from tangency-labs/jyviko/rig-export
Kernel rig: export each case's kernel operations as a corpus for solidcore's rig
2026-11-03 08:00:07 -05:00
CamWight 1bd0040d25 Drawing export: SVG and PNG cut a picture to a detail view's boundary (#4890) (#10969)
* Paper scene: clips of lines and arcs, cut once before every writer (#4890 A8a)

Scene::prepare_clipped(clips) cuts each group of an object to that
object's Clip, a rectangle or a circle (a detail view's), and keeps what
lies inside; prepare() is prepare_clipped with none. Prepared holds a
Cow: a scene without clips is borrowed as it is, and Group and Scene are
unchanged, so today's scenes are the same bytes.

- Lines and arcs are cut where they cross the edge; a part is kept when
  its middle is inside, the edge included. Parts no longer than EPS go.
- Kept parts that follow one another stay one open stroke. A stroke
  wholly inside is kept whole, closed if it was; a closed one cut open
  rejoins across its seam.
- A dashed stroke keeps its pattern: each part's phase is where it
  starts along the stroke. Across a closed one's seam the pattern runs
  on only when its length is whole patterns, so the parts rejoin only
  then.
- Checked: one clip an object, a rectangle from its lower-left to its
  upper-right corner or a circle with a radius above 0, within 1e5 mm;
  a clipped object's items strokes of lines and arcs. What the cuts
  made is validated again.

SVG and PNG draw the cut scene through Prepared as they are.

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

* Paper scene: clips of every curve, and fills kept or dropped whole (#4890 A8b)

A clipped object's strokes may now hold any curve, not only lines and
arcs, and its fills are checked against the clip.

- A line or an arc is still cut where its equation says. Any other
  curve is halved while its control points lie either side of the edge
  (widened by EPS), until they span at most CUT_MM (1e-9 mm), and is cut
  there. A curve that needs more than MAX_HALVINGS halvings (one that
  runs this close along the edge) is refused as not supported yet.
  Each part is the same kind of curve as the whole.
- Seg2::part gives the piece between two parameters, starting and
  ending exactly where split_at puts those points; a conic's tail is
  re-normalised, so its second parameter is carried into it.
  Seg2::MIN_SPLIT names split_at's distance from the ends.
- A line or a circle within EPS of touching a circle only touches it:
  one cut there, never a sliver of chord.
- A fill wholly inside its clip is kept and one wholly outside is
  dropped. One that crosses the edge, or lies round the whole clip, is
  refused as not supported yet.

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

* Paper scene: pictures in a clipped object, cut by the SVG and sheet PNG writers (#4890 A8c)

A picture wholly outside its object's clip is dropped when the scene is prepared. One crossing the clip's edge reaches its writer with the clip, on the grid item, since pixels can't be cut as geometry: SVG cuts it with a clipPath, the sheet PNG by whether each pixel's middle is inside. Both draw only the pixels that meet the clip's box. Text in a clipped object stays refused.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 07:58:27 -05:00
bherbruck e438a9bbea Web app: choose the release, the release candidate or the edge build (#11419)
The web app is now offered at /app/ (latest release), /app-rc/ (newest release candidate) and /app-edge/ (current main), each with its own browser storage. /app/ is built from the latest release's web zip instead of main.
2026-11-03 07:23:17 -05:00
CamWight 49a3ba1c03 Paper scene: dashed lines, in SVG and PNG (#4890 A5) (#7430)
A stroke can carry a pattern, `Dashes { lengths_mm, phase_mm }`: dash and
gap lengths in turn, in paper millimetres, and how far into the pattern
the stroke starts. Without one a stroke is solid, and a solid stroke
reads and writes as before.

- Checks: 2 to 12 lengths, an even count, each above 0 and at most
  1000 mm; the phase from 0 up to the pattern's length; at most a
  million dashes along the stroke, counted from its pieces' own lengths.
- The walk puts the pattern on the writer's grid: a dash or gap under
  half a step is one step, counted, as widths are.
- SVG: stroke-dasharray, and stroke-dashoffset when the phase isn't 0.
- PNG: tiny-skia's dashing on the flattened line; its points count
  against the sheet's, a line in a gap draws nothing, and one tiny-skia
  won't cut is refused rather than drawn solid.
- The paper fuzz cases carry a dashed arc.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 06:52:45 -05:00
jyviko 7b0906b918 Kernel rig export: review fixes
- Picks run on a copy of the operand: a pick builds query caches on the body it
  reads, so the traced body stays as an untraced run leaves it.
- An untrim and an extension record their picks with the pickers they run
  (`face_at`, `nearest_edge`).
- A final body matches the newest export of its record that an operation made.
- A stopped run is the kernel's when the last failed operation is followed by
  copies only.
- A re-export removes each case's old entry first; fixes.json is written whole.
- The manifest names no commit for a copy without git, never an empty one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtL5M41G8Nua7HvuFPZkmJ
2026-11-03 06:37:58 -05:00
jyviko 7e3824c0bc Kernel rig export: operand records in a gzip file beside each entry
An entry keeps what each operand body is and names <case>.bodies.json.gz, which
holds the records (gzip with no timestamp, so the same records give the same
bytes); the entry itself is pretty JSON. The corpus of 1202 cases is 33 MB.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtL5M41G8Nua7HvuFPZkmJ
2026-11-03 06:24:51 -05:00
jyviko 5fd0ac98a9 Kernel rig export: a failure is the kernel's when the last traced operation failed; grades SolidCraft computes are listed apart
An entry's operation is expressible when the kernel accounts for its result; the
values only SolidCraft grades (continuity, meshes, files, command answers, merged
counts, SolidCraft's wording of a refusal) are listed under `ungraded` instead of
making the whole case inexpressible.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtL5M41G8Nua7HvuFPZkmJ
2026-11-03 06:10:34 -05:00
jyviko 9c65a30265 Kernel rig: export each case's kernel operations as a corpus for solidcore's rig
`cargo xtask kernel-cases [--ladder] --export DIR` runs the rig with the kernel's
operations traced (`smlib::trace`) and writes one entry per case: the operation, or
chain of operations, that made the bodies the case ends with; each operand that came
in as a record, as that record; the parameters, with every pick by points also given
as the topology index it picked; the fix set; the case's expected values and the
rig's result. A case no kernel operation alone accounts for is written too, marked
not expressible with its reasons. DIR/manifest.json counts them per area.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtL5M41G8Nua7HvuFPZkmJ
2026-11-03 06:05:18 -05:00
CamWight d78895de4d Where a drawing stands: drawing.status in one read (#4894 WE2) (#11352)
drawing.status says each view's status (current, out of date, placeholder or
failed, with why) from what the sheet page last drew, and each dimension's in the
words inspect uses, reading each body once for the call. Nothing is drawn.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 04:41:33 -05:00
CamWight 112e0a3d9c Paper text in SVG: a run's glyph outlines on the writer's grid, filled nonzero (#4890 B2a) (#9708)
The walk puts a text run on the grid as its glyphs' outlines, placed by the run
(`TextRun::outlines`), each loop walked as a fill's: `GridItem::Text`, with the run's
index among its group's items, so a writer that sets real text (PDF) finds it in paint
order. SVG writes one path a run, filled black with its default nonzero rule, so text
looks the same without the font and never reflows. The sheet PNG refuses text by name
until B2b, and its refusal test says so.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 02:09:25 -05:00
CamWight eb0b427a36 Drawings WE3-WE5: commands report where each object sits on a sheet, and select and delete views, dimensions and balloons (#4894) (#11047)
* Where a sheet's objects sit: bounds beside the scene (#4894 WE3)

The sheet scene now says where its border and each view sit on paper: the box
round what each draws, and a view's boundary, so one that can't be drawn can
still be picked. It is worked out once per assembly, beside the scene rather
than in it, so no writer sees it and the page needn't inspect each view.

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

* Selected drawing objects: a view, dimension or balloon in the selection (#4894 WE4)

select.set takes {type: drawing, drawing, object} for the drawing open. It adds no
undo step and changes nothing saved, and a selected object leaves the selection
when an undo takes it or its drawing closes.

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

* Delete on a sheet: views, dimensions and balloons in one step (#4894 WE5)

selection.delete now works with a drawing open: a view goes with its dimensions
and balloons as one undo step, and one with views projected from it is refused,
naming them, unless they are selected too or contents: delete takes them. A sheet's
delete and this one share the record's one removal rule.

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

* Tests: a view's box holds what it draws, and what's chosen goes in any order (#4894 WE3, WE5)

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 00:59:41 -05:00
CamWight ba69e5488c Drawings WE1: Inspect Drawing lists each sheet's views, with their names, dimensions and balloons (#4894) (#11350)
* Where a sheet's objects sit: bounds beside the scene (#4894 WE3)

The sheet scene now says where its border and each view sit on paper: the box
round what each draws, and a view's boundary, so one that can't be drawn can
still be picked. It is worked out once per assembly, beside the scene rather
than in it, so no writer sees it and the page needn't inspect each view.

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

* Selected drawing objects: a view, dimension or balloon in the selection (#4894 WE4)

select.set takes {type: drawing, drawing, object} for the drawing open. It adds no
undo step and changes nothing saved, and a selected object leaves the selection
when an undo takes it or its drawing closes.

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

* Delete on a sheet: views, dimensions and balloons in one step (#4894 WE5)

selection.delete now works with a drawing open: a view goes with its dimensions
and balloons as one undo step, and one with views projected from it is refused,
naming them, unless they are selected too or contents: delete takes them. A sheet's
delete and this one share the record's one removal rule.

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

* Tests: a view's box holds what it draws, and what's chosen goes in any order (#4894 WE3, WE5)

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

* What's on each sheet: drawing.inspect lists its views (#4894 WE1)

Each sheet in drawing.inspect now lists its views: each base view by id, followed
by those projected from it, with what it is called and its dimensions and balloons.
It is one pass over the record with no model read, so the Browser can read it once
per revision.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 00:59:36 -05:00
CamWight 9308095744 Paper text in the browser: Japanese through the font the web app fetches (#4890 F6) (#10516)
The browser has no font folders, so `font_dirs` is empty there. The first
kana or Han a text's font lacks asks for the font the web app already
fetches on demand (BIZ UDPGothic); no new file. The text is pending until
it comes, then sets whatever it has a glyph for. If the fetch fails, or
the font lacks a character, that character is refused by name, and the
refusal says the web covers Japanese for now.

The web app starts the fetch through its existing loader, once, when a
text waits for the font, and hands the font over when it arrives.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 21:59:34 -05:00
CamWight 5bf3940c04 Sheet tables on paper: a table's lines (#3874, #4890 W10.7, D3a) (#7745)
TableLayout::paper gives a placed table's lines as one paper group of
the kind it is (parts_list or revision_table), so both sheet tables reach
paper through one function:

- the outline as one closed stroke, outline_line_mm wide;
- a stroke cell_line_mm wide across the whole table at each row boundary
  inside the outline, the heading's too, from the top whichever side the
  heading is on;
- a stroke down the whole table at each column boundary inside it.

Each line is drawn once, centred on its edge, on the layout's own
corners. The cells' text follows once the paper scene has text (D3b,
after #4813); hatch clearance waits for hatching.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 21:53:06 -05:00
CamWight 2f483a00c0 Drawing text F4: a character missing from the chosen font is drawn with another installed font (#4890) (#10515)
* Drawings: the font a drawing's text is set in, by family (#4890 F1)

A drawing names the font family its text is set in (`font`, saved only when
set; a pending drawing-format file). `drawing.create` and `drawing.set` take
it, and `drawing.inspect` reports it, with `font_note` when this computer sets
the text in the built-in font instead ("Futura isn't on this computer: set in
Liberation Sans"). Drawings name fonts by family, so they travel: a font file
is refused.

Paper text finds the family among the installed fonts as sketch text does
(`paper_text_in` for given folders), admits each face once a process, and
sets a family that is missing or can't be set on paper in the built-in font.
Bold takes the family's bold face; the serif flag comes from the face's OS/2
family class. The fixed list of drawing fonts is gone.

New drawings made in the app take the interface font when it is a family.

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

* Paper text: any face with outlines is set, and says how a PDF may carry it (#4890 F2)

A face is admitted for paper whatever its tables lack:
- a variable face is set at its default instance, which is what a PDF draws;
- its cap height is the one sketch text scales it by (OS/2, else the top of
  its `H`, else 70 % of the em);
- its PostScript name is what a PDF name can hold of its own, else of its
  family and style, else `Font-` and the start of its id;
- without a `post` table it has no italic angle, and a fixed pitch when every
  glyph advances alike;
- its stem width is measured across its `l`, else taken from its weight.
A bitmap or colour-only face, with no outlines to fill, is refused by name.

`Font.embed` says how a PDF may carry the face, from its embedding permissions:
a subset, the whole face (no subsetting), or not at all (restricted, bitmap
embedding only, or CFF2 outlines), when the PDF draws the runs' outlines with
their text beside them. Which characters a PDF name may hold is now one rule,
`is_postscript_char`, that the record's check and sketch share.

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

* Paper text: a font's family, for readers that pick fonts by family (#4890 F2)

`Font.family` is the face's own family name: its typographic family (name
ID 16), else its family (ID 1), read from the face itself (in a collection,
not the first face), without control characters and at most 255 characters;
empty when it has neither. A DXF text style picks a font by it; `Font.id`
stays the identity, so two faces of one family are still two fonts
(@0xlillith on #4890). The name lookup PostScript names use is one function
now, shared by both.

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

* Fonts: one list of fallback fonts per script, in sketch (#4890 F3)

The interface's lists of fonts to try for a script move down to
`sketch::fonts::fallback`, so drawings can use the same ones. Each row is
a family and the files it's known to be in. The interface reads the files
in the order it always did, so what it finds is unchanged.

- `CRAFT_FONTS_DIR` joins `font_dirs`, as the interface already has it:
  `SOLIDCRAFT_FONT_DIRS`, else the pack, then the system's folders.
- `font_files` sorts the files within each folder, so a search doesn't
  depend on how the system lists a folder.
- The Windows folder search takes its names from the Japanese list
  instead of its own copy of them.

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

* Paper text: a character its font lacks falls back to a font that has it (#4890 F4)

A text on paper was refused for any character its font lacks. Now each
such character falls back to an installed font that has it: the script's
list in `sketch::fonts::fallback` first, Han by the drawing's language,
then the first face in sorted order with outlines and the character.

- Runs split by direction, then by font. Every font takes the same em
  size, on the line's baseline and pitch. A space, a digit or punctuation
  stays with the run it follows when that font has it; a mark stays with
  its letter. A font fallen back to is shaped, with the drawing's
  language. Its nearest style is used: bold or italic isn't refused.
- A line its own font has all of is set as it was.
- A character no font has is still refused by name.
- The last-resort search reads each file's character map alone, stays
  within `MAX_FONT_FILES`, and keeps its answers, misses too, for the
  process.
- `paper_text_now` is for the window's thread: the search runs on a thread
  of its own (`font-search`) and it refuses with `FontPending` until the
  answers are in. `paper_text` searches where it's called.
- `paper_fallback_notes` names what fell back, for a drawing's status,
  from the answers so far: it looks for no font.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 21:27:16 -05:00
Nathan Dane d5fd14135f Add the native Drawing workspace UI scaffold (#5349)
* docs: plan drawing workspace UI implementation

* assets: add drawing icon drafts for owner review

* feat(ui): add drawing workspace scaffold with shared ribbon and docked settings

Expose planned Drawing tools without introducing drawing documents, paper rendering or CAD behavior. Reuse existing source BOM, properties and flat-pattern services; retain placeholders for unapproved icon drafts.

Validation: full UI, kernel, icon, clippy, wasm and layout checks passed. Full cargo xtask ci reports three geometry failures that also reproduce on unchanged drawings base 3b982ca: counterbored chamfered-disc ring cut, robustness seed 36 and seed 71. No geometry source or golden fixtures changed.

* Preserve default icon palette in Drawing scaffold tests

* Preserve Drawing providers and complete scaffold translations
2026-11-01 21:45:05 -05:00
CamWight 85be50df0d Reference parts: a view shows them by percent, not by a margin (#5866 V5c) (#10713)
reference_margin_mm becomes reference_shown_percent, 0 to 100. At 0 the
boundary is the model's box, as before, and no reference part is measured.
At 100 it holds the model and all the view's reference parts. Between, each
side moves on its own, so a part on one side grows only that side.

The model's middle now places a view, not the middle of what it shows, so a
view stays lined up with its projected views as its box grows. With nothing
grown the two are the same point and the picture is the same.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 18:36:53 -05:00
CamWight 71edae80df Drawing views made by command: drawing.view.base and drawing.view.projected (#4816) (#9254)
* Drawing views made by command: drawing.view.base and drawing.view.projected (#4816)

drawing.view.base {sheet, view?, scale?, position_mm?, the view settings?} adds a base view of the
drawing's design to a sheet. Without a scale it takes the largest ISO 5455 preset at which it lies
inside the sheet's drawing space, centred where it goes, its margin included, as the snapshot fits
it; that scale is saved as the view's own (1:1 with nothing to measure).

drawing.view.projected {parent, side, place?} adds a view projected from a saved view, on its
parent's sheet, following its parent's scale and display settings. Without a place it is lined up
with its parent, its boundary 20 mm past the parent's (DEFAULT_GAP_MM, until a drawing style
gives it).

Each is one undo step through edit_drawing, refused with nothing changed when the record's rules
refuse it; the reply gives the view, its sheet, its scale, its boundary and, when it leaves the
sheet's drawing space, itself as outside (it isn't moved back in). Unknown fields are refused.

One rule, one place: whether a view leaves its drawing space is drawing_view::leaves, and the sheet
of an id is drawing_view::sheet, used by outside() and both commands.

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

* Drawing views made by command: a build fix, and each view one undo step, tested (#4816)

From the review of the view create commands:
- drawing.view.base measures the shown bodies (`measure` takes them, not the session), so it builds.
- The nothing-to-measure case is a design with one sketch and no bodies, as the other view tests
  make it: drawing.create refuses an empty design.
- The schema test gives drawing.view.base its own key list (it takes no snapshot-only keys).
- Every view a test makes is checked to be one undo step, not only the first: a second step made
  under the first (a nested edit) still let one undo take the view away.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 17:12:38 -05:00
CamWight 93baf88e2d Paper scene: pictures in the sheet PNG and SVG (#5624, W17b S5) (#7591)
* Paper scene: one walk onto a writer's number grid, for every writer (#4890, C4)

The SVG writer kept its own number grid: each coordinate whole nanometres, every joint written once,
a closed path ending exactly on its first point, slivers merged into the next piece and a path's
last piece kept. PDF, PNG and the Drawing workspace's canvas need the same rules, so they move into
`geom::paper::grid`, and the SVG writer only formats what the walk gives it.

- `Grid`: `per_mm` steps a millimetre. `Grid::NANOMETRE` (six decimals of a millimetre) is SVG's.
  `Grid::new` takes 1 to 1e9; a power of two keeps every number exact as a binary float, which a
  writer of 32-bit numbers such as PDF can use.
- `Prepared::on_grid(grid) -> OnGrid`: each group's items as paths of `Piece`s (lines, arcs of at
  most a quarter turn, cubics, parabolas), y up as in the scene, a closed path's closing line left
  to the writer's close, and a report of widths snapped and pieces merged. Slivers are measured in
  the grid's own steps, so a coarser grid merges more.
- The SVG writer flips y on the whole numbers it's given and writes the same bytes as before.
  `SvgReport` is the grid's report.
- Ellipses, hyperbolas and Béziers above degree 3 are still refused (A3b), now by the walk.

DXF stays on its own number rule for now: it writes exact entities (ELLIPSE, SPLINE) and is being
reworked under #5318 to #5321; whether its polylines take a grid is decided there.

Tests: every SVG test unchanged, bytes included; the grid's rounding and its bounds; the A3 border
on the grid (y up, closing line left out, fill loops closed); slivers in the grid's own steps, with
the merge boundary both ways (10 and 11 steps, a 6-8 and a 7-8 step); an arc's quarters with their
radius and turn in steps; a width under half a step made one step; an ellipse refused.

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

* Paper scene: a sheet PNG at a chosen dpi and colour (#4890 A4)

A new writer, io::paper::paper_png, that reads the shared walk on the
nanometre grid, the one the SVG writer uses, so a PNG draws the same joints,
closures and merges. Neither the walk nor the SVG writer changes.

- Curves are flattened within 1/8 px by geom::paper::flatten, which turns
  each walked piece back into its Seg2 (an arc's centre from its ends, radius
  and direction) and takes Seg2::chords' steps. chords is the bound
  Seg2::polyline already used (the chord's bulge for arcs, Wang's bound for
  the rest), moved out of polyline unchanged so both read one rule.
  polyline itself doesn't fit: it floors an arc's tolerance at a millionth of
  its radius and caps a piece at 4096 steps, coarser than asked without a
  word (0.1 mm, 9 px, on a 100 m arc at 2400 dpi). Over 2^20 points a path,
  or 2^24 a sheet, is refused instead. tiny-skia only gets lines: its own
  flattening stops at 64 lines a curve, more than a pixel off on a large arc
  at a high dpi.
- 25 to 2400 dpi (300 by default). 8-bit greyscale (the default), 1-bit
  black and white (no anti-aliasing, every line at least a pixel wide), 24-bit
  colour, or 32-bit colour with transparent paper.
- The file records its dpi (pHYs) and sRGB, so it prints at the sheet's size.
- Each line is stroked once, then the sheet is drawn in bands of 16 MiB of
  pixels streamed into the file, so memory doesn't grow with the sheet. Each
  band also draws a row above and below that isn't written: tiny-skia starts
  an edge a little differently where a clip cuts it (one subsample, 16
  levels, in a few pixels), so no written row sits on a cut and the bytes are
  the same whatever the band.
- A line under a pixel wide (in greyscale and colour) is drawn as tiny-skia's
  hairline, its coverage scaled by its width: its stroked outline would fall
  between the rows the rasteriser samples and vanish.

Tests: the pixel size and pHYs of A4 at 300 and 25 dpi; a line's and a fill's
pixels at 254 dpi (butt ends, the fill's hole); each colour's PNG type, ink
and paper; a hairline between two rows' centres that is light grey in
greyscale and stays one pixel in black and white, and a bar a fifth of a pixel
wide that black and white inks by its centre; a closed outline joined at its
start; a 40 mm circle round to the pixel at 600 dpi; the same bytes drawn 1, 3
or 7 rows at a time or all at once, and again; the refusals; the flattener's
bounds and counts, and its point cap at the exact boundary; the scene-JSON
fuzz through the PNG writer.

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

* Geom: the pixel grid of a paper rectangle (#5624, W17b S1)

One rule for where a picture's pixels lie on paper, shared by the picture renderer and the writers
that place it: anchored at the rectangle's lower-left corner, rounded outward (W = ceil(w / p - 1e-9)
with p = 25.4 / dpi), so the overhang is at the top and the right; rows run top-down. At most
65,536 px a side; nothing caps the total, as pictures are drawn in tiles and strips.

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

* Paper scene: pictures, and a sheet's pixel grid and windows (#5624, W17b S3; #4890 A9)

A picture on a sheet is `Item::Picture`: the rectangle it sits in, the name of its pixels
(`source`, opaque as a group's object id is) and its opacity. The pixels stay out of the scene: a
writer asks a `Pictures` provider for them on the grid it draws on. A PNG at 1200 dpi then gets a
shaded view drawn at 1200 dpi, not one stretched, and a scene stays small, plain data.

The pixel grid gains the two rules a sheet needs, beside the rectangle's own grid:
- `PixelGrid::sheet`: the whole sheet's grid, anchored at its top-left corner, as an image of
  the sheet is drawn.
- `PixelGrid::window`: the columns and rows a rectangle meets; a side on a pixel edge takes no
  sliver beyond it. A picture drawn into its window of the sheet grid is placed by copying whole
  pixels.

`Picture::grid` is a picture's own grid at a dpi, for SVG and PDF to embed. `Scene::validate`
counts a picture as one record. It refuses a rectangle outside the coordinate bound or without
area, and an opacity that isn't above 0 and at most 1. `Aabb2` is read and written as
`{min, max}`.

On the walk (C4), which S3 follows here: `GridItem::Picture` passes a picture through with its rectangle in
millimetres, since the pixel grid places its pixels, and the SVG and PNG writers refuse it ("not supported yet:
pictures (A9)") until their own slices. `GridItem`, `GridGroup` and `OnGrid` are `PartialEq` only, as a
picture holds numbers that aren't `Eq`.

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

* Paper scene: pictures in a sheet PNG (#5624, W17b S5)

The sheet PNG writer draws the scene's pictures (S3's Item::Picture) where
the scene orders them, between its lines and fills, from a Pictures provider.

- The PNG is now the sheet's pixel grid (PixelGrid::sheet, S1): anchored at
  the sheet's top-left corner and rounded outward, so nothing is clipped.
  210 mm at 300 dpi is 2481 px (it was 2480, rounded to the nearest). A side
  over 65,536 px is refused and the refusal names the limit ("a sheet PNG
  side of ... px at ... dpi is over the limit of 65536 px"); an A0 sheet
  fits up to about 1400 dpi.
- A picture fills exactly its window of that grid, so it registers with the
  lines drawn over the same rectangle. Each band asks the provider for just
  its own rows, as straight-alpha sRGB RGBA, and the bytes are checked: too
  few or too many, or the provider's error, refuse the PNG with the picture's
  source named. paper::NoPictures refuses every picture.
- Pictures are asked for at most MAX_PICTURE_DPI (1200): above it, on the
  grid k = ceil(dpi / 1200) times coarser, whose pixels each fill exactly
  k x k of the sheet's (both grids share the top-left anchor), so a 2400 dpi
  export doesn't ask a renderer for four times the pixels.
- Source-over in sRGB, the picture's alpha times its opacity; transparent
  paper keeps the alpha. In black and white each picture pixel is dithered
  with an 8 x 8 ordered (Bayer) matrix at its place on the sheet, so bands
  don't show; lines stay solid. PngReport says whether anything was
  dithered.
- In the writer a picture is one more shape a band draws, beside A4's
  outlines, fills and hairlines; the walk's picture arm passes pictures
  through, and the SVG writer still refuses them (A9).

Tests: a picture's exact window and its register with a fill over the same
rectangle, also on a sheet 100.8 px high (the grid anchored at its top);
pictures keep their place in the paint order; opacity scales the alpha (on
white and transparent paper, and the picture's own half alpha); above 1200
dpi a coarser picture's pixels are each copied 2 x 2; black and white
dithers half of mid grey and keeps a line over it solid, whatever the band;
a provider's short rows, its error and no provider are refused, and a
picture off the sheet asks for nothing; the bands test draws three pictures
1, 3 or 7 rows at a time, the same bytes and each picture row asked for
once; the sheet sizes on the outward grid and the 65,536 px refusal; the PNG
fuzz with a picture.

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

* Paper scene: pictures in a sheet SVG (#5624, W17b S5)

A picture is an <image> holding a PNG as a data URI, on the picture's own
pixel grid (Picture::grid) at the picture resolution the caller gives,
25 to 1200 dpi: paper_svg(scene, pictures, picture_dpi), the same shape as
paper_png. Only its window over the sheet is embedded and asked for, so it
never costs more pixels than the sheet holds; one off the sheet is left out.
Its x, y, width and height are the grid's own pixel edges put on the
nanometre grid every line is on, so a line on its edge has its number.
Opacity below 1 is written, in millionths as lengths are.

The PNG writer gives the embed its encoder settings (sRGB, the dpi
recorded) and its provider check, now one function each, and streams the
picture a band of rows at a time, refused once it passes the room left
under the file's 64 MiB.

Tests: the 1182 x 591 px grid at 300 dpi and its edges, a line on the
edge, paint order and opacity, the crop and the picture off the sheet, the
bands, and each refusal; the SVG fuzz case gains a picture.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 14:01:08 -05:00
CamWight 4060069fcd Drawing views: shaded snapshots and their pictures' parts (#5624, W17b S4) (#9798)
* Paper scene: one walk onto a writer's number grid, for every writer (#4890, C4)

The SVG writer kept its own number grid: each coordinate whole nanometres, every joint written once,
a closed path ending exactly on its first point, slivers merged into the next piece and a path's
last piece kept. PDF, PNG and the Drawing workspace's canvas need the same rules, so they move into
`geom::paper::grid`, and the SVG writer only formats what the walk gives it.

- `Grid`: `per_mm` steps a millimetre. `Grid::NANOMETRE` (six decimals of a millimetre) is SVG's.
  `Grid::new` takes 1 to 1e9; a power of two keeps every number exact as a binary float, which a
  writer of 32-bit numbers such as PDF can use.
- `Prepared::on_grid(grid) -> OnGrid`: each group's items as paths of `Piece`s (lines, arcs of at
  most a quarter turn, cubics, parabolas), y up as in the scene, a closed path's closing line left
  to the writer's close, and a report of widths snapped and pieces merged. Slivers are measured in
  the grid's own steps, so a coarser grid merges more.
- The SVG writer flips y on the whole numbers it's given and writes the same bytes as before.
  `SvgReport` is the grid's report.
- Ellipses, hyperbolas and Béziers above degree 3 are still refused (A3b), now by the walk.

DXF stays on its own number rule for now: it writes exact entities (ELLIPSE, SPLINE) and is being
reworked under #5318 to #5321; whether its polylines take a grid is decided there.

Tests: every SVG test unchanged, bytes included; the grid's rounding and its bounds; the A3 border
on the grid (y up, closing line left out, fill loops closed); slivers in the grid's own steps, with
the merge boundary both ways (10 and 11 steps, a 6-8 and a 7-8 step); an arc's quarters with their
radius and turn in steps; a width under half a step made one step; an ellipse refused.

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

* Paper scene: a sheet PNG at a chosen dpi and colour (#4890 A4)

A new writer, io::paper::paper_png, that reads the shared walk on the
nanometre grid, the one the SVG writer uses, so a PNG draws the same joints,
closures and merges. Neither the walk nor the SVG writer changes.

- Curves are flattened within 1/8 px by geom::paper::flatten, which turns
  each walked piece back into its Seg2 (an arc's centre from its ends, radius
  and direction) and takes Seg2::chords' steps. chords is the bound
  Seg2::polyline already used (the chord's bulge for arcs, Wang's bound for
  the rest), moved out of polyline unchanged so both read one rule.
  polyline itself doesn't fit: it floors an arc's tolerance at a millionth of
  its radius and caps a piece at 4096 steps, coarser than asked without a
  word (0.1 mm, 9 px, on a 100 m arc at 2400 dpi). Over 2^20 points a path,
  or 2^24 a sheet, is refused instead. tiny-skia only gets lines: its own
  flattening stops at 64 lines a curve, more than a pixel off on a large arc
  at a high dpi.
- 25 to 2400 dpi (300 by default). 8-bit greyscale (the default), 1-bit
  black and white (no anti-aliasing, every line at least a pixel wide), 24-bit
  colour, or 32-bit colour with transparent paper.
- The file records its dpi (pHYs) and sRGB, so it prints at the sheet's size.
- Each line is stroked once, then the sheet is drawn in bands of 16 MiB of
  pixels streamed into the file, so memory doesn't grow with the sheet. Each
  band also draws a row above and below that isn't written: tiny-skia starts
  an edge a little differently where a clip cuts it (one subsample, 16
  levels, in a few pixels), so no written row sits on a cut and the bytes are
  the same whatever the band.
- A line under a pixel wide (in greyscale and colour) is drawn as tiny-skia's
  hairline, its coverage scaled by its width: its stroked outline would fall
  between the rows the rasteriser samples and vanish.

Tests: the pixel size and pHYs of A4 at 300 and 25 dpi; a line's and a fill's
pixels at 254 dpi (butt ends, the fill's hole); each colour's PNG type, ink
and paper; a hairline between two rows' centres that is light grey in
greyscale and stays one pixel in black and white, and a bar a fifth of a pixel
wide that black and white inks by its centre; a closed outline joined at its
start; a 40 mm circle round to the pixel at 600 dpi; the same bytes drawn 1, 3
or 7 rows at a time or all at once, and again; the refusals; the flattener's
bounds and counts, and its point cap at the exact boundary; the scene-JSON
fuzz through the PNG writer.

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

* Geom: the pixel grid of a paper rectangle (#5624, W17b S1)

One rule for where a picture's pixels lie on paper, shared by the picture renderer and the writers
that place it: anchored at the rectangle's lower-left corner, rounded outward (W = ceil(w / p - 1e-9)
with p = 25.4 / dpi), so the overhang is at the top and the right; rows run top-down. At most
65,536 px a side; nothing caps the total, as pictures are drawn in tiles and strips.

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

* Paper scene: pictures, and a sheet's pixel grid and windows (#5624, W17b S3; #4890 A9)

A picture on a sheet is `Item::Picture`: the rectangle it sits in, the name of its pixels
(`source`, opaque as a group's object id is) and its opacity. The pixels stay out of the scene: a
writer asks a `Pictures` provider for them on the grid it draws on. A PNG at 1200 dpi then gets a
shaded view drawn at 1200 dpi, not one stretched, and a scene stays small, plain data.

The pixel grid gains the two rules a sheet needs, beside the rectangle's own grid:
- `PixelGrid::sheet`: the whole sheet's grid, anchored at its top-left corner, as an image of
  the sheet is drawn.
- `PixelGrid::window`: the columns and rows a rectangle meets; a side on a pixel edge takes no
  sliver beyond it. A picture drawn into its window of the sheet grid is placed by copying whole
  pixels.

`Picture::grid` is a picture's own grid at a dpi, for SVG and PDF to embed. `Scene::validate`
counts a picture as one record. It refuses a rectangle outside the coordinate bound or without
area, and an opacity that isn't above 0 and at most 1. `Aabb2` is read and written as
`{min, max}`.

On the walk (C4), which S3 follows here: `GridItem::Picture` passes a picture through with its rectangle in
millimetres, since the pixel grid places its pixels, and the SVG and PNG writers refuse it ("not supported yet:
pictures (A9)") until their own slices. `GridItem`, `GridGroup` and `OnGrid` are `PartialEq` only, as a
picture holds numbers that aren't `Eq`.

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

* Paper scene: pictures in a sheet PNG (#5624, W17b S5)

The sheet PNG writer draws the scene's pictures (S3's Item::Picture) where
the scene orders them, between its lines and fills, from a Pictures provider.

- The PNG is now the sheet's pixel grid (PixelGrid::sheet, S1): anchored at
  the sheet's top-left corner and rounded outward, so nothing is clipped.
  210 mm at 300 dpi is 2481 px (it was 2480, rounded to the nearest). A side
  over 65,536 px is refused and the refusal names the limit ("a sheet PNG
  side of ... px at ... dpi is over the limit of 65536 px"); an A0 sheet
  fits up to about 1400 dpi.
- A picture fills exactly its window of that grid, so it registers with the
  lines drawn over the same rectangle. Each band asks the provider for just
  its own rows, as straight-alpha sRGB RGBA, and the bytes are checked: too
  few or too many, or the provider's error, refuse the PNG with the picture's
  source named. paper::NoPictures refuses every picture.
- Pictures are asked for at most MAX_PICTURE_DPI (1200): above it, on the
  grid k = ceil(dpi / 1200) times coarser, whose pixels each fill exactly
  k x k of the sheet's (both grids share the top-left anchor), so a 2400 dpi
  export doesn't ask a renderer for four times the pixels.
- Source-over in sRGB, the picture's alpha times its opacity; transparent
  paper keeps the alpha. In black and white each picture pixel is dithered
  with an 8 x 8 ordered (Bayer) matrix at its place on the sheet, so bands
  don't show; lines stay solid. PngReport says whether anything was
  dithered.
- In the writer a picture is one more shape a band draws, beside A4's
  outlines, fills and hairlines; the walk's picture arm passes pictures
  through, and the SVG writer still refuses them (A9).

Tests: a picture's exact window and its register with a fill over the same
rectangle, also on a sheet 100.8 px high (the grid anchored at its top);
pictures keep their place in the paint order; opacity scales the alpha (on
white and transparent paper, and the picture's own half alpha); above 1200
dpi a coarser picture's pixels are each copied 2 x 2; black and white
dithers half of mid grey and keeps a line over it solid, whatever the band;
a provider's short rows, its error and no provider are refused, and a
picture off the sheet asks for nothing; the bands test draws three pictures
1, 3 or 7 rows at a time, the same bytes and each picture row asked for
once; the sheet sizes on the outward grid and the 65,536 px refusal; the PNG
fuzz with a picture.

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

* Paper scene: pictures in a sheet SVG (#5624, W17b S5)

A picture is an <image> holding a PNG as a data URI, on the picture's own
pixel grid (Picture::grid) at the picture resolution the caller gives,
25 to 1200 dpi: paper_svg(scene, pictures, picture_dpi), the same shape as
paper_png. Only its window over the sheet is embedded and asked for, so it
never costs more pixels than the sheet holds; one off the sheet is left out.
Its x, y, width and height are the grid's own pixel edges put on the
nanometre grid every line is on, so a line on its edge has its number.
Opacity below 1 is written, in millionths as lengths are.

The PNG writer gives the embed its encoder settings (sRGB, the dpi
recorded) and its provider check, now one function each, and streams the
picture a band of rows at a time, refused once it passes the room left
under the file's 64 MiB.

Tests: the 1182 x 591 px grid at 300 dpi and its edges, a line on the
edge, paint order and opacity, the crop and the picture off the sheet, the
bands, and each refusal; the SVG fuzz case gains a picture.

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

* Drawing views: shaded snapshots and their pictures' parts (#5624, W17b S4)

drawing.view.snapshot takes `shaded` as a display or reference style and,
when a picture is drawn, says which: picture {pixels, dpi, rect_mm}, the
boundary's pixel grid at the sheet's dpi or 1200, the lower, since lines
may export finer. It renders nothing. A shaded part's own lines are left
out of the reply, as its picture draws it; it still hides what lies behind
it. shaded_visible and shaded_visible_hidden stay refused until the view's
exact lines (W06), and shaded with `scene` (and on a sheet) until the
paper scene draws pictures.

- engine::drawing_picture: the pictures a view draws, bottom first, and how
  each body takes part in each (painted, depth only, left out). The model
  always hides reference parts behind it; reference parts hide the model
  only when reference_hides_model is on; shaded reference parts that don't
  get a picture of their own under the model's, so they never cover it.
- product_scene::extract_where: the scene for the bodies a caller picks;
  extract keeps the browser's eye, a drawing draws every body.

Tests: every style and hide case, reference parts through a subassembly
and a part, the picture's size at 300, 1200 and 2400 dpi, a view with
nothing shaded, the hidden body in the filtered scene, and which lines a
shaded model or shaded reference parts leave.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 14:01:04 -05:00
CamWight a4c9bc380f Paper scene: text runs and their fonts, as records with their rules (#4890 B1a) (#9512)
`Item::Text` is a run of placed glyphs in one font, with its logical text and the clusters that
tie the two together, and `Scene.fonts` the table of the fonts the runs use (as posted on #4890,
with each glyph's `hmtx` advance). `Scene::validate` holds them to the posted rules. Writers never
parse a font.
- A run names its font by the font's id (a hash of its face's bytes and index), so groups laid
  out apart name a font alike whichever machine's font it is; `Scene::add_fonts` joins their
  tables at assembly, kept in order of id, so the same input gives the same bytes.
- A font's bytes never go through serde: a scene read from JSON is refused until they come from
  the font of the id it names.
- `TextRun::outlines` places the glyph outlines on the sheet, for the writers to fill nonzero.
The SVG writer refuses text by name until B2. `Scene::new` builds a scene without text, so the
next field doesn't touch every caller again.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 13:15:32 -05:00
CamWight f219d9b795 Paper text: a text laid out by sketch's rule, as the scene's runs and their drawing font (#4890 B1b) (#9513)
* Paper scene: text runs and their fonts, as records with their rules (#4890 B1a)

`Item::Text` is a run of placed glyphs in one font, with its logical text and the clusters that
tie the two together, and `Scene.fonts` the table of the fonts the runs use (as posted on #4890,
with each glyph's `hmtx` advance). `Scene::validate` holds them to the posted rules. Writers never
parse a font.
- A run names its font by the font's id (a hash of its face's bytes and index), so groups laid
  out apart name a font alike whichever machine's font it is; `Scene::add_fonts` joins their
  tables at assembly, kept in order of id, so the same input gives the same bytes.
- A font's bytes never go through serde: a scene read from JSON is refused until they come from
  the font of the id it names.
- `TextRun::outlines` places the glyph outlines on the sheet, for the writers to fill nonzero.
The SVG writer refuses text by name until B2. `Scene::new` builds a scene without text, so the
next field doesn't touch every caller again.

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

* Paper text: a text laid out by sketch's rule, as the scene's runs and their drawing font (#4890 B1b)

`paper_text` lays a text out with `TextMetrics`, the rule sketch text draws and measures with, and
returns the paper scene's runs of placed glyphs (B1a) with the font they are set in, holding the
glyphs they place: each one's `hmtx` advance and its outline in font units.
- Drawings are set in bundled fonts only (`DRAWING_FONTS`: Liberation Sans for now). A font is
  admitted once: a static face with a PostScript name, a cap height and a `post` table, its stem
  width measured across its `l`, and its id hashed then. A run names its font by that id.
- A character the font has no glyph for is refused by name; bold is refused; an italic leans as
  sketch text's made-up italic does; text on paper follows no path.
- Lines break a pitch apart and align as sketch text's do. A line with right-to-left characters
  is a run per direction, shaped, with clusters from the shaper; any other is one run, a
  character to a glyph.
`TextMetrics` keeps one layout: it now reports each glyph's cluster and the line's runs, and the
glyph outline walk gives loops of `Seg2`, which sketch text turns into the geometry it drew before.

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

* Paper text: the fonts a text uses, each once (#4890 F0)

`paper_text` returns its runs with the list of fonts they use, each once in order of id (so the
same text gives the same list) and each holding exactly the glyphs its runs place, where it
returned one font. Still one drawing font today; the list is the shape a text set in several
fonts (a Latin, Arabic or CJK line) needs, and `Scene::add_fonts` already takes it as it is.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 13:15:28 -05:00
CamWight 21888e8000 Drawing views: reference parts in a view's lines and thin on paper (#5866, V5) (#10520)
* Reference parts in a drawing view's lines (V5a, #5866)

A body placed through an occurrence that counts as reference is a reference
part: one rule, Document::reference_occurrences. Its lines say so in replies.
With reference_hides_model off (the default) the model's lines skip reference
parts at the one occlusion test, and reference lines test against everything.
The two roles merge apart, so a reference line never takes a model line's
place on one stretch. Extents come from the model parts when there are any. A
sketch curve is the model's: a reference part hides it only as it hides the model.

One thing the plan didn't name: reference lines stop at the view's boundary
(extents and reference margin) when model parts are there too, which is what
the margin is for; a part much bigger than the model would otherwise run off
the sheet.

Hidden reference lines in a scene are refused until dashed lines (V7b), as the
model's are.

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

* Reference parts on paper: thin, under the model's lines (V5b, #5866)

Saved views and the snapshot's scene alike, 0.25 mm until the drawing styles
slice sets widths by kind of line.

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

* Reference parts: tests the planted faults asked for (#5866 V5)

A reference line against a model line on one stretch, and against another reference part; one part
placed through a reference assembly and on its own; a dimension on a reference part's edge; undoing
and redoing a change of type, set on the occurrence or its component; and a saved view's setting.

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

* A test reads any other paper item as no stroke (#5866 V5)

Naming Fill alone would stop compiling when #7590 adds a kind of item, with
no conflict for git to show.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 11:51:20 -05:00
CamWight 9b73ec8d9d Display units: every length unit of the expression table shows its own numbers (#4896) (#7809)
A drawing's fixed unit may be any length unit of the expression unit
table (expr::UNITS: um, inch, yd, mil and the rest besides a design's
five), but the display functions knew only the five and showed the
others' measurements as millimetres. unit_scale now reads the same
table, so the two can't drift apart again; the five keep exactly the
scales they had (1 over 10, 1000, 25.4 and 304.8). The inch rule (a
fourth digit when it means something) follows an inch's size, so
`inch` reads as `in` does.

A design's own units are only the five, so nothing it shows changes.
The test that pinned `inch` to millimetres becomes one that checks
every length unit of the table shows its own numbers; angle names
(`deg`, `rad`) and other text keep the millimetre fallback.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 05:38:22 -04:00
CamWight 0d4739fffb Drawing dimensions: between two cylinders' silhouettes (#4896, W20b) (#9251)
* Drawing dimensions: a hole's or an arc's centre as a point to measure from (#4896, W20c)

A linear dimension's point can be the centre of a circle or an arc, saved as its one edge's name
with `centre`. It is found again as construction geometry finds a round edge's centre
(`construct::as_circle`), in 3D, so any view can measure it; a split edge is ambiguous, and an edge
that is no longer round can't be measured. `drawing.dimension.add` takes `{centre: edge}` wherever
a corner goes, and refuses a straight edge.

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

* Drawing dimensions: a chain of distances along one line (#4896, W20c)

A chain dimension runs through 3 to 64 points in order (corners or hole centres) and reads the
distance from each to the next along one line as the view shows it: horizontal, vertical, along a
direction, or aligned from the first point to the last. The line runs the way the first point to
the last goes, and a point out of order is refused, never moved, so the members add up to the
overall from the first point to the last (`drawing_measure::chain`). It reads projected only (true
lengths of points off one line don't add up) and shows its members' numbers, never a text instead.
`drawing.dimension.add` takes `{chain: {points, direction?}}` and counts the points before looking
any up; `drawing.dimension.edit` changes its direction; inspect lists its members, its value being
the overall.

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

* Drawing dimensions: a whole cylinder's diameter between its silhouettes (#4896, W20b)

A cylinder dimension saves a face by name (ModelFace, as ModelEdge names an edge) and reads Ø
between the cylinder's two silhouettes, which any view but one along its axis shows 2r apart
(`drawing_measure::silhouette_side`). The face, or the pieces it was cut into, must lie on one
cylinder (the kernel's own condition for faces on a common cylinder); every face of the body on
it counts, since the kernel builds a whole cylinder as two half faces; and both silhouettes must
lie on those faces, so a fillet's quarter round is refused. `Document::face_reference` picks a
face by a point on it or by its index; `drawing.dimension.add` takes `{cylinder: {face}}`, edit
refuses its settings, and inspect can list face candidates.

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

* Drawing dimensions: between two cylinders' silhouettes (#4896, W20b)

A dimension between a silhouette of one cylinder and a silhouette of another, as the view shows
them. Each is named by a face of its cylinder and by its side: the one nearer the other cylinder
on the view, or the one farther (SilhouetteSide). So it keeps to them whichever way round the
view shows the cylinders.

- geom: drawing_measure::between_silhouettes takes the two cylinders (Cylinder, now geom's: the
  type doc kept privately), their sides and a direction as a linear dimension takes it (aligned:
  square to the silhouettes). The silhouettes must be parallel on the view and the axes apart
  there. Foreshortened when the axes aren't parallel in the model, or the silhouettes lie at
  different depths.
- doc: DimensionKind::Silhouettes { a, b: Silhouette { face, side }, direction }, read as the
  view shows it. Each silhouette must lie on its cylinder's faces (Lookup::on_cylinder, now shared
  with the cylinder kind). Document::silhouettes_kind makes two silhouettes of one cylinder of
  one body that cylinder's diameter (which takes no direction), else the distance between them.
- engine: drawing.dimension.add takes {silhouettes: {a, b: {face, side}, direction?}};
  drawing.dimension.edit changes its direction only.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 04:49:53 -04:00
CamWight 693cf911f1 Recovery: unsaved drawing work survives a crash (#3890, K9′) (#9289)
* Recovery: a change to a drawing alone is autosaved and recovered (#3890, K9')

A design's drawings are part of its document (K3b), so the autosave already sees a drawing's
change as unsaved work and writes it with the design, and recovery reads it back. This test
holds that: with the model saved, a change to a drawing's properties alone is autosaved, and the
recovered design has the drawing as changed.

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

* Recovery: a drawing that breaks a rule is left out of the autosave and the crash copy, named (#3890)

Reading a design refuses it whole for one drawing that breaks a rule of the record, so an autosave
or a crash report's design holding one never opened again: all the unsaved work was lost. Both
copies now keep the rest of the design (drawing::openable) and name each drawing left out and the
rule: the autosave as its error, the crash report in its notes. A design's save error names the
drawing it couldn't save too (Drawing::label).

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

* Recovery: a copy that must open keeps a design's drawing rules too (#3890)

drawing::openable now leaves out what a read refuses at the design's level as well: the later of
two drawings with one id, and the drawings past MAX_DRAWINGS, each named with its rule. So an
autosave never keeps a stale copy over a drawing problem. The id rule is take_id, which
check_in_design uses too.

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

* Recovery: a drawing too large to save is left out too, and the status line names it (#3890)

The autosave and the crash copy already leave out a drawing that breaks a rule of the record.
A drawing too large to save failed the whole autosave instead; `openable` now leaves it out the
same way, before its id, so the rest of the design is kept. Nothing is skipped silently: the
autosave's error names each drawing left out, and the status line shows it.

Tests: a drawing left out takes no id from a later one; an autosave leaves out one too large to
save; an autosave keeps what is hidden; the status line names a drawing the autosave left out.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 00:56:45 -04:00
CamWight b21c377296 A lost dimension keeps its last value after the design is reopened (#4896 W19d) (#9764)
Dimension::last_good saves the value the session last read, written only when
there is one, and a save takes it from the session's reads as it takes what is
hidden: no read touches the document, makes an undo step or asks to be saved.
drawing.dimension.inspect answers this session's read first, else the file's.
resolve_dimensions never sees it, so a lost dimension stays lost. A value past
the drawing format's limit is left out of the save, not written into a file that
then can't open.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 18:59:15 -04:00
CamWight 878bc1b421 A point on a curved edge finds it, between the edge's chords (#4896) (#9765)
An edge given as a point looked along the edge's mesh lines, within a point's tolerance, so a point on a
circle or an arc that fell between two chords found no edge: a radial dimension on most points of a rim
said "no edge at". A point off every chord now takes the edge whose chords are within what its body was
meshed to, and only when no edge passes through the point itself, so a point on a straight edge beside
a curve is still that edge.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 18:59:11 -04:00
CamWight 562bb379a5 Dimension layout: sizes for paper, and text above the line (#4896, W19c) (#9193)
* Dimension layout: sizes for paper, and text above the line (#4896, W19c)

A dimension's sizes become a parameter, so a drawing can lay its
dimensions out in paper millimetres with the same code as a sketch:
DimSizes { arrow, gap, overshoot, offset, above } and
dim_layout_sized. dim_layout and dim_layout_reaching keep their
signatures and use DimSizes::screen(px), today's 9, 3, 4 and 22 px;
the other default text places stay the same fractions of the offset
(18, 48 and 20 to 22), and default_text takes its 22 from the same
place. A size that isn't a finite number above 0 (or is beyond MAX_COORD,
the largest length a sketch takes) falls back to its 1 px value, as a
bad px does today.

`above` puts a linear dimension's text above an unbroken dimension
line (ISO 129-1), running along the line and reading from the bottom
or the right; DimLayout::text_dir gives its direction (level for text
in the line). The arrows go inside or out by the same test, with the
text's length along the line; a leader to text outside runs on under
it.

On main, #7056 moved the four default places into
default_text_offset(frame, px). This leaves those lines as they are,
so when main next comes into drawings the layout's arms call it with
this px (the offset's 22nd), and lib.rs's export line takes both lists.

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

* Format the dimension layout's re-exports (#4896, W19c)

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 13:59:49 -04:00
bherbruck 1a47ac13de Merge pull request #9467 from tangency-labs/sync/main-into-drawings-1030
Sync main into drawings (Oct 30)
2026-10-30 10:29:35 -04:00
bherbruck 1c957c105d Translate the drawings' new messages into the complete catalogs (ar, he, vi)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 10:15:28 -04:00
CamWight ec527a32ab geom: arrowheads and leaders on paper (#4890 D1a) (#9466)
`paper::annotate` places the marks that point at things on a sheet as plain
strokes and fills in sheet millimetres: open, closed and filled arrowheads,
the oblique stroke and the dot, and leaders (points, a landing line in the
reading direction, a terminator at the first point). Every size is the
caller's; ISO 128-2's dot and landing ratios and ISO 129-1's 15° to 90°
arrowhead angle are constants beside them. A closed or filled arrowhead stops
its line at the base. Dimension layout stays W19c's (#4896) and the datum
triangle the datum symbol's (#7257).

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 09:59:02 -04:00
bherbruck d01de87552 Merge main into drawings (Oct 30)
Brings the 1,270 commits on main since the drawings branch last met it,
including the first drawings landing (#8249), into drawings.

Merged with the drawings snapshot that #8249 landed (ebafe0748) as the
merge base, so only the drawings work after that landing is replayed on
top of main. Main's structure wins; drawings' additions are kept and
adapted to main's APIs (metadata Owner/KEYS table, checked! without a
contract argument, Snapshot visibility, crash journal outcome, the
part-properties normalisation, model_body_components, io helpers).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 09:54:08 -04:00
CamWight fefca6bfa0 Drawing export: a sheet of the open drawing as SVG, from its scene (#4890 A7) (#9137)
* Drawing sheets on paper: the drawing border, grid and marks (#4890 A6, #3875)

SheetLayout::paper gives what a sheet's drawing border draws as paper
groups of kind drawing_border, in order, each left out when empty:

- the drawing border, one closed stroke drawing_border_line_mm wide
  (0.7 mm in the built-in profile, ISO 5457);
- the grid: the grid reference border, closed, and a mark at each edge
  between two fields from the drawing border out to it, on all four
  sides, GRID_LINE_MM (0.35 mm, ISO 5457's grid line width as the
  drawings study's section 11.3 gives it) wide; none without fields or
  with the grid reference border on the drawing border;
- the centring marks, as the layout places them, in the drawing
  border's width, as the profile says;
- the trimming marks, each rectangle filled.

The grid's labels are text and come once the paper scene has text
(#4813). Only the function, its constant and its tests are added.

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

* Drawing sheets on paper: one sheet's scene, its border then its views by id (#4890 W10, #3890 W15)

`drawing_scene::build` draws one sheet of a drawing as one paper scene, the
one the sheet page and every export will draw, and `drawing.scene {sheet}`
answers it (in_drawing, noundo, doc_only, typed contract and output schema).
In paint order: the sheet's drawing border groups as `SheetLayout::paper`
gives them (object: the sheet), then each view on the sheet by id (object:
the view), its visible lines as `paper_group` makes them.

- A view's lines are worked out centred on the view and then moved to its
  centre, exactly (each point plus the centre), so the same lines on the same
  centre are the same bytes. They match the snapshot's within rounding.
- The shown set is worked out once per sheet. No model is evaluated.
- A view with nothing to measure is a placeholder, and one that can't be
  drawn yet (shaded, or hidden lines) fails with the snapshot's reason. Each
  draws nothing, and the sheet's other views are still drawn.
- A view is placed only while the sheet stays within the paper scene's
  100,000 records. One that would pass the limit fails and says so, and later
  views that fit are still drawn. Each view's lines are placed as they are
  drawn, so at most one view's lines are held beside the scene.
- A view above 100:1 takes a coarser chord rather than asking the mesh for
  chords below its floor. A saved view has no chord setting to give.

One rule, one place:
- the snapshot's two style refusals are `drawing_view::not_drawable`;
- a group's record count is `paper::Group::records`, which `Scene::validate`
  now counts with;
- the stats reply is `From<Stats>`;
- both paper scene schemas come from `paper_scene_schema`.

Tests: the border then the views by id, byte for byte against the layout,
with hand-worked outlines and lengths; the command's reply and its schema; the
SVG's group order; each view against the snapshot, and one of many lines (a
block with eight holes, from above); an empty sheet; a failed and a
placeholder view; no evaluation; refusals; the record limit at exactly 100,000
and one past it; a 200:1 view.

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

* Drawing sheets on paper: the provider the sheet page draws through (#4890 W10, #3890 W15)

The sheet page asks for a sheet every frame, so it draws through a provider
that keeps each view's lines between frames, instead of through `build`.

- `drawing_scene::extract(session, sheet)` returns the last scene at once,
  with each view as it was last drawn and the ones out of date. Nothing is
  drawn there. When the drawing, the world state and the views drawn are all
  as they were, it returns the same answer without looking at the sheet.
- `drawing_scene::step(session)` draws the first out-of-date view of the
  sheet last extracted and says whether more wait: a short piece of work for
  the UI's tick, one view's projection.
- `generation` comes from one counter for every session. It goes up only
  when a sheet's scene is assembled again, so a page never sees one number for
  two scenes, even across closing and opening its drawing.

The cache is the session's view state, beside the record: never saved, never
an undo step. `Session::execute` drops it when a command closes the drawing or
opens another. One entry per view, keyed by the world state it was drawn from
(held in the entry and compared with `Arc::ptr_eq`) and the view as it resolves.
A drawing-only edit keeps the world state, so a move, and its undo, project
nothing: the same centred lines are moved to the new centre. The shown set is
worked out once per world state. Views no longer in the drawing are forgotten
when a scene is assembled again.

Tests:
- a new cache gives the border with both views out of date;
- each step draws one view, in id order, until the scene is `build`'s, byte for
  byte;
- the generation stays put while nothing changes;
- a move and its undo project nothing, and the views move by exactly the move;
- after a model change (a checkpoint restored) the last scene shows, and a view
  listed as up to date always draws as `build` does;
- closing drops the cache, and the scene after reopening has a new generation;
- seeded mixes of moves, setting edits, undos, redos, extracts and steps
  always end at `build`'s scene.

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

* Drawing export: a sheet of the open drawing as SVG, from its scene (#4890 A7)

drawing.export {sheet, format?, path?} writes one sheet of the open
drawing from drawing_scene::build's scene, the scene the sheet page
draws, so a file shows what the page does: its drawing border, then
each view's visible lines, through the SVG writer. With a path (a .svg
file, checked by vfs::check before and again just before writing, as
the view DXF is) the file is written atomically; without one the reply
holds the SVG. The reply gives its size, what the writer snapped or
merged, and each view's status as drawing.scene reports it.

A sheet with a view that failed isn't written: the refusal names each
such view with why. A placeholder view (nothing to measure) draws
nothing and is exported all the same. PNG and DXF are "not supported
yet": they come with the sheet PNG writer (#4890 A4) and the shared DXF
writer (#5319). No undo step; no model is evaluated.

One rule, one place: the scene's sheet look-up is
drawing_scene::sheet_id, and a view's reply entry is
From<ViewState> for SheetSceneView, both used by drawing.scene and
drawing.export. The scene tests' drawing fixture is open to the crate's
tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 01:20:10 -04:00
CamWight 6a2bdbf144 Bill of materials: virtual components and a typed quantity (#3874, P6) (#9226)
`bom_quantity` is a part property of any component: what one placement of it means, an expression
evaluated with the design's parameters (pieces without a unit, else a length). A row's `quantity`
is what its placements add up to, with `quantity_unit`; pieces beside a length, or a quantity that
no longer evaluates, give none and say why. A quantity is a parameter user: a rename follows it, and
a parameter it uses can't be deleted. The CSV gains a `quantity` column at the end.

`virtual` marks a component with no geometry (grease, paint, a label): listed with its quantity,
weighing nothing. It can't be the active component, and one check after each undoable outermost
command refuses any change that puts a body, a sketch or a placed component into one, whichever
command made it. A file a damaged virtual component already fills opens, and other changes go on.

Mass, volume and area quantities wait for the expression units of #4811.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 01:15:51 -04:00
CamWight 413c145c56 Bill of materials: stable item numbers, saved with the design (#3874) (#7779)
* Bill of materials types on one occurrence (#3874)

An occurrence can count in the bill of materials as another type than its
component, for that occurrence only: `bom_type` on the occurrence, set with
occurrence.bom_type (one undo step; it changes no geometry, so nothing is
evaluated). An occurrence without it counts as its component, so older
designs need no step; the format change is one pending file
(occurrence-bom-type.rs).

assembly.bom counts each placement by its own type: a purchased placement of
a kit part is one row without the kit's contents, a phantom placement lists
what it holds where it sits, and one component placed as two types gives a
row for each. Paste New keeps the types; Paste Instance counts as the
component. The compact list and the locales (en, de, ja, ko, zh) carry it.

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

* Bill of materials rows: material, and mass when asked (#3874)

Every row of assembly.bom (and every part folded into a row) names the
physical materials of its definitions' own bodies, each once, with "; "
between several. An unassigned body's is Default. A subassembly or an
empty component has none of its own.

With mass: true each row also carries mass_g, what one of its parts
weighs as the row stands for it, so the rows of any list add up to the
assembly once:
- at the top level, all it holds;
- in parts only, its own bodies, because what it holds is listed beside
  it; but a folded part's row is the kit (all it holds), a purchased part
  is all it holds, and an inseparable one all but its purchased parts;
- a reference placement adds nothing, nor does anything inside it.
It is null when nothing in the part has a body, a body can't be
measured or measures a negative volume (inside out, such as an open mesh
facing the origin), or the definitions sharing the row's part number
weigh differently.

Measuring runs the kernel, so it is asked for: the Bill of Materials
window re-runs assembly.bom after every change and doesn't ask (0.2 ms
without mass, 135 ms with it on a bolted bracket). Placements are rigid,
so each definition is weighed once however often it is placed, and each
body is measured once with measure_mass (the same volume as measure,
without the topology counts' tessellation).

A body's material and its density now follow one rule:
Document::body_material (new, beside Document::density) is the material
its mass is worked out with, named as the library or the design spells
it: the assigned one when it is known and has a usable density, else
DEFAULT_MATERIAL (new, beside DEFAULT_DENSITY). Document::density is
that material's density, with the same values as before. So a damaged
file's unknown or broken material reads as Default, as it weighs, and a
hand-edited "steel" reads as Steel. The bill of materials uses both; the
other places that read a body's material or spell "Default"
(component.properties, inspect and others) move to them in a separate
cleanup PR for #4783, which brings the same two functions to main.

Document::body_owners returns each body's owner in order, so the rows
find their bodies in one pass. It no longer repeats the owner rule:
model_body_component, legacy_body_component and body_owners go through
the rule written once, given how to find a feature's component (the
timeline for one body; body_owners still builds its map once). The CSV
columns are unchanged.

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

* Physical properties: a reference placement adds nothing (#3874)

component.properties and occurrence.properties now leave out a placement
that counts as a reference in the bill of materials (its occurrence's
bom_type, else its component's: a fixture shown for context), and
everything inside it: no body entry, mass, volume, area, centre of mass
or bounding box, and it isn't in placement_count. That is the bill of
materials' rule, so a reference part is in neither the parts list nor
the assembly's mass, and every bill of materials list still adds up to
the assembly's Properties. The placement asked for counts whatever its
type: a reference fixture's own Properties still measure it and what it
holds, as a component's own bill of materials lists what it holds.

Only the totals change. The placements walk that face picking, sketches
on placed faces and the occurrence path lookup use stays complete, so a
reference fixture's faces can still be picked.

The type an occurrence counts as is now written once:
Document::occurrence_bom_types (new, beside BomType) gives each
occurrence's type in their order, looking each component up once. The
bill of materials, occurrence.bom_type's reply and the physical
properties use it, in place of the two copies in the bill of materials.

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

* Bill of materials: one module of typed rows in doc (#3874)

The bill of materials now lives in solidcraft_doc::bom as the typed rows
of a bill of materials view. assembly.bom's JSON and CSV and the Bill of
Materials window present those rows; parts lists, item numbers and
balloons are to read them too.

- list(tree, request) walks the placements below the assembly once. One
  rule (classify) decides for each placement whether it is counted in a
  row, how what it holds is walked and what its row stands for when
  weighed, from the type it counts as, the view and the component's
  shape. "An inseparable part's purchased parts are listed beside it" is
  written there once, and the mass walk reads it.
- A row carries its identity (RowKey: the rows it is listed under and
  its lowest member, a component and the type it counts as), its item,
  count, each, the placements it counts by occurrence path, what it
  stands for (Holds), its part properties, materials, mass and children.
- Mass is a Mass: not asked, grams, or unknown with the reason (nothing
  to weigh, unmeasurable, the definitions differ). Each definition is
  weighed once for each kind of row and kept in the Tree. The assembly's
  total is weighed from the design the same way, not added up from rows,
  so it is known even when a row's definitions weigh differently.
- Bom::row_of(tree, path) finds the row a placement is counted in (a nut
  inside a purchased bolt is in the bolt's), or says why there is none:
  no such placement, a reference, or no row of its own.
- rows_deep() lists each row followed by its children (1, 1.1, 1.2, 2);
  Request::default() is assembly.bom {}; View and Group carry the
  commands' words.

What changes, beside that the output is otherwise the same:
- One placement budget (100,000) and one depth limit (64 levels) cover
  the whole list. A folded part's contents used to be walked under a
  budget of their own, so a design past the limit was listed with
  group_children; it is refused now, as it already was without it.
- Kits are compared by what they hold (part, type and count), not as
  text: a part without a part number whose id is 12 no longer reads as a
  part numbered "#12", nor a part numbered "P×1\nQ" as a P and a Q. Kits
  holding the same parts through different definitions (two bolt
  definitions, each with its own nut definition of one part number) are
  one row, and its children count every bolt's placements, where they
  showed the first bolt's definitions only.
- A folded part's row always stands for all it holds. It used to stand
  for its own bodies when nothing inside it was listed, which differs
  only for a phantom with bodies of its own inside it.
- An inseparable part's row in parts only names the material of what it
  stands for (its steel blocks read Steel) where it was blank, and a
  subassembly whose parts share one material names it at the top level.
- A damaged file's component placed inside itself is refused at the first
  level too, since naming a row's material walks inside it, as weighing
  it already did.
- A row's body count saturates, so a compact assembly with more placed
  bodies than a count holds (a block placed 2^64 times) is weighed
  instead of overflowing.

Materials follow Cameron's rule for a row that stands for an assembly
(C3, 2026-10-23): a part's row lists its own bodies' materials, as before;
a row that weighs what is placed inside it (a subassembly at the top level,
a purchased or inseparable part with parts inside it, a folded part) names
a material only when everything it stands for shares it, else none. A frame
of steel and aluminium parts reads blank, and a bought bearing of steel
parts reads steel. One walk (adding) decides what is placed inside a row's
part for both its mass and its materials, and each definition's materials
are read once.

Components are looked up by id once per list; they were searched for
each row and each kit. The window reads the rows directly instead of
running assembly.bom after every change, so it no longer adds an entry
to the crash journal each time it refreshes. The engine keeps the
command, its parameters and the files.

Tests check rows. The module's own: an empty assembly, one part, a row
named by its lowest member through part-number edits, one part number
of two types, a phantom in a phantom, a reference at the root and its
own list, the 64-level limit, the budget hit exactly with and without
folding, kits told apart by what they hold and the types they hold it as, a folded part's contents not
folded again, row_of for every type, every placement in one row or
none, a damaged file's missing component and cycle, the mass agreement
rule (moved from the engine with row_mass) and what adds to a row. In
the engine: the material rule for every kind of row, the JSON against the
rows for every option, why a mass is
unknown, the total against component.properties in every view, what a
row stands for and its placements, and the overflow. The assembly.bom
tests from #1888 on pass unchanged.

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

* Bill of materials: structured, all levels (#3874)

P4: `assembly.bom {levels: "all"}` lists the assembly's direct contents, each subassembly a row with
its own rows listed under it, to any depth (items 1, 1.1, 1.1.1), each child counted `each` per one
of its parent and `count` in all.

- `doc::bom::View::AllLevels` goes through the one rule (`classify`) the other views use: a
  normal subassembly is a row holding what it holds, listed under it as all levels again; a
  phantom has no row and what it holds joins the level it sits in, merged with the same parts
  there; a purchased part is a row with nothing under it unless `show_purchased_contents`
  (Request::purchased_contents) asks for its contents; an inseparable part is a row with nothing
  under it; a reference counts nowhere.
- The rows under a row are the folded-part rows already built (kits), so subassemblies with one
  part number and different contents are different rows, and no second nested builder is written.
  All levels gathers every level as it gathers the first; a folded part's contents are still
  gathered by part number in the other views.
- The JSON and CSV already nest to any depth; `show_purchased_contents` is echoed with the other
  options. Each row's mass still adds up to the assembly at the top.

Tests, worked by hand: four bolts and a two-bolt subassembly placed twice (8 bolts in parts only;
in all levels the subassembly twice with 2 bolts each, 4 in all, beside the top's 4); three levels
deep (2 x 3 x 2: counts 2, 6, 12, items 1, 1.1, 1.1.1, the CSV in that order); each type (a
phantom's bolt merged into its subassembly's, purchased with and without its contents,
inseparable, reference); two subassemblies of one part number with different contents as two
rows; `levels: "deep"`, a flag that isn't one, and an assembly over 100,000 placements refused.
D's test that a folded part's contents are listed as parts only lists them now names the two views
it holds for, and checks all levels' nested rows (the subassembly inside the folded part a row with
its own rows).

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

* Bill of materials: parts with one part number are one row only when they hold the same to any depth (#3874)

The kit compare looked one level down: two parts with one part number whose contents differed two
levels below merged into one row, and the children that only one of them held came out at each 0.

Each placement now has a kit, a number interned from what it holds (each child's part, type and
own kit), built in one pass from the last entry back: an entry's children come after it in the
walk, so their kits are known first. Rows are told apart by that number.

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

* Bill of materials: a phantom's own bodies are a row of their own (#3874)

A phantom had no row, and its own bodies were weighed in the assembly's total but in no row, so
the rows didn't add up. Its own bodies, when it has any, are now a row of the phantom's type that
stands for them alone, as a subassembly's own bodies are in parts only; what it holds still counts
where it is placed. Inside an inseparable part in parts only, nothing changes: the part's row
weighs them.

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

* Bill of materials: the assembly's total mass in assembly.bom (#3874)

`list` weighed the assembly as a whole (from the design, not added up from rows) and the command
dropped it. `assembly.bom` now gives it as `total_mass_g`, and so does a JSON file it writes, only
when `mass: true` (null when it isn't known).

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

* Bill of materials: all levels as JSON lists its rows one after another (#3874)

All levels nested each level's rows in the row above it: two JSON containers a level, so the
64 levels a list may hold came to 129, past the 128 that serde_json (and so the MCP server and
the control channel) reads. In all levels `assembly.bom` now gives the rows one after another,
each followed by the rows under it (items 1, 1.1, 1.1.1), as its CSV does; a row listed under
another still reads its item as text and gives `each`. A folded part's contents, one level, stay
inside its row as `children`.

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

* Bill of materials: a row whose definitions weigh something and nothing differs (#3874)

A row gathering a definition with bodies and one with none (one part number on a steel block and
on an empty assembly) said there was nothing to weigh. Their masses differ, so it says so; only a
row where none of them has a body has nothing to weigh.

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

* Bill of materials: definitions agree on a mass within the measurement accuracy (#3874)

A row's definitions had to weigh the same to 1e-9, but the kernel measures a volume from
tessellations, and one part modelled another way is tessellated differently: a pin made as a
cylinder and as a revolved rectangle measured 1.05e-5 apart, so it read as two masses. The bill of
materials now has its own `bom::MEASURE_ACCURACY` (1e-4: ten times that gap, with its doc on what
it follows: the kernel's `measure` gives one solid's volume to about that, relative, however the
solid was modelled), and a row's definitions agree within it. A pin 0.1% wider (0.2% heavier) still
differs. The kernel is unchanged: its work is paused while it is being replaced.

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

* Bill of materials guide: all levels, purchased contents and this round's changes (#3874)

The guide had no `levels: "all"` or `show_purchased_contents`. It now has both, with how all
levels tells subassemblies apart and lists its rows as JSON, a phantom's own bodies as a row, the
assembly's `total_mass_g`, and definitions that agree on a mass within the measurement.

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

* Bill of materials: materials are bodies', as the code says (#3874)

The rule stays as built (a row that stands for an assembly names a material only when everything
in it shares one), but its comment and the guide said a material set on a component is set on
all its bodies. `material.assign` takes bodies only: a component has no material of its own yet.
The module notes now say so, and where component materials will join the rule.

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

* Bill of materials: a body's volume measured once with the evaluated model (#3874)

`assembly.bom` measured each definition's bodies anew for every list, and `component.properties`
measured each placed body again. A body's measure depends on nothing but the body, so the evaluated
model keeps its volume (`ModelBody::volume`, shared by clones, as its outline box is), and both
commands weigh that one measurement: the same grams to the bit. A body offset scales it by the size
of its determinant (`mat_volume_scale`) instead of copying the B-rep to measure it. The physical
properties still measure the placed body for its area, centre, box and counts.

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

* Bill of materials: row keys, what rows stand for, members and row_of stay in doc (#3874)

Nothing outside `doc` reads them yet: `RowKey`, `Unlisted`, `Holds`, a row's `key`, `stands_for`
and `members`, and `Bom::row_of` are crate-private until item numbers and balloons need them, so
they can still change without breaking another crate. The engine test that read them is covered
by doc's own (`row_of_finds_the_row_a_placement_is_counted_in`, `every_placement_is_in_one_row_or_none`).
`Unknown` stays public: `Mass::Unknown` carries it, and a public enum can't carry a private type.

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

* Assemblies: one fallible placement walk for totals that cover the whole assembly (#3874)

The physical properties walked the placements twice in their own code, with the 64 levels and
100,000 placements written as literals, and the bill of materials gave the same refusal in words
of its own. `Document::placements_below` is that walk once, from any placement, refusing what is
past `MAX_PLACEMENT_DEPTH` or `MAX_PLACEMENTS` (never walking it in part), and both use its
refusal: "the assembly is deeper than 64 levels or has more than 100000 placements". The physical
properties now allow 100,000 placements below the start, as the bill of materials does (they
stopped at 99,999).

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

* Bill of materials: the same rows for less work (#3874)

- Part numbers are borrowed from their definitions (`Part<'a>::Number(&'a str)`) instead of copied
  for every placement grouped and every kit.
- A row's members, the placements' paths, are built only when the list is asked for them
  (`list_with`, for item numbers, balloons and `row_of`): the walk then keeps paths and moves each
  into its one row instead of copying it. A list made for its rows alone builds no paths at all.
- `row_of` reads an index of member paths, built once with the list, instead of scanning every
  row's members for each placement asked about.

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

* occurrence.bom_type: input and output schemas (#3874, #4035)

`OccurrenceBomTypeParams` takes the occurrence (an id or a name), its path when given and the
type (one of the five, `same_as_component` or null), each read by the handler's own reader: the
path through `cmd::path_ids`, split out of `occurrence_path`, the type through
`cmd::occurrence_bom_type`, split out of the handler. The handler builds its reply from
`OccurrenceBomTypeResult` ({occurrence, bom_type, effective, changed}).

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

* Bill of materials tests: all levels presented at every depth, and its caps (#3874)

- `assembly_bom_presents_the_typed_rows` now covers all levels (with mass, purchased contents and
  by definition) and the total mass, on a three-level design with a purchased gearbox holding a
  gear, comparing every field of every row down to 1.1.1.1.
- All levels lists 100,000 placements and 64 levels, and refuses 100,001 and 65.

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

* Bill of materials: static materials, typed in for a component (#3874)

A value the bill of materials works out can now be typed in by hand for a component (a static
value, Cameron's choice of word). Material comes first:
`component.metadata.set {component, material}` takes any text up to MAX_TEXT_BYTES, and
`component.metadata` reads it back as `material`. It is shown wherever the component is listed,
at every level and in every view, over the materials worked out from its own bodies or an
assembly's blank, and the row carries `static: ["material"]`. Clearing it (empty text or null)
brings the worked-out material back.

It is kept the way `bom_type` is: a part property in the component's metadata (`material`), so
there is no format change and older releases keep it. `PART_ONLY` names both keys in one place:
Design Properties doesn't list them and `document.metadata.set` refuses them. Another tool's
non-text value is kept and reads as no static material.

The bill of materials says one thing about the part everywhere: a static material also stands for
the part in the one material an assembly around it shares (two parts typed in as S355 make their
frame read S355; S355 beside plain steel reads blank). A phantom's row is its own bodies, so its
static material names those only, and what it holds keeps its own. A row gathering several
definitions takes the first one's that has a static material, as its description does. It only
names: mass still comes from the bodies.

The CSV gets a `material` column at the end, so the other columns keep their places: what the row
shows, the static material or the worked-out ones joined with "; ".

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

* Bill of materials: stable item numbers, saved with the design (#3874, P5a)

A numbered parts list keeps each row's item number under the row's key in
`assembly.item_numbers`, so a row keeps its number when others are added,
removed or reordered. New rows take the next number after the highest at
their level; Renumber restarts a view in list or item order and leaves
locked rows alone; duplicates are marked; numbers change no geometry and
are part properties in the undo history.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 00:29:25 -04:00
CamWight b708170028 Drawing W07: a view's lines, on paper, for the occurrences it shows (#5866) (#8970)
* View DXF: its lines come from one module both view commands share (#5866, W07 V1)

The view DXF (file.export_view_dxf) finds its lines in four files private to the command: the
export meshes, the projection, the hidden-line scene and the progress stages. Drawing views
(W07's drawing.view) need the same lines, so the four move to engine/src/mesh_lines, with
three changes that let a caller other than the DXF use them:
- prepare takes the bodies to draw from its caller (the DXF passes the shown ones), and
  names the caller's command in its errors;
- the lines are seen from a View (an origin, a right/up/back basis and, for perspective, the
  eye's distance) instead of a viewport Camera; the DXF builds one from its camera, with the
  same arithmetic as before;
- a sketch's polyline (Part) belongs to the module, so the sketch code no longer defines what
  the scene draws.

The DXF writing stays in cmd/view_dxf.rs, with connect (the dashed-line joining), and the stage
names and cancellation text stay the DXF's. Nothing else changes: the module is the DXF's own
code, moved, and the DXF writes the same bytes as before for every case checked (see the PR).

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

* View DXF: the sketch runs go once the lines are made, and a box's outline pinned (#5866, review)

From the review of #6482:
- The sketch runs (up to 4,000,000 points) are dropped once the lines are made, as they were
  before the move, instead of living through the DXF writing.
- `mesh_lines::prepare`'s doc says which errors name the command: those about the placements.
  A body with no exportable surface, the memory budget and cancellation come back as the meshing
  stage's own.
- A new test pins the lines themselves by hand: a plain 40 x 30 x 10 box seen from the front is
  its 40 x 10 outline. Every line lies along a side, they add up to the 100 mm perimeter, and
  nothing is hidden.
- The shared-geometry test hides the definition's first placement: the other two are still
  drawn from the one shared mesh.

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

* Drawing views: the bodies a view shows, worked out once per request (#5866, W07 V3)

A view's extents and, next, its lines read one list: each body the design places, with the
occurrences that place it and its definition's body name, in the world state's order. Choosing
occurrences and hiding parts (V4) and the reference split (V5) become filters on it.

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

* Drawing views: a view's lines from the mesh projection, each named by what drew it (#5866, W07 V3)

drawing.view.snapshot now returns its lines: every shown body's edges and silhouettes from the
view DXF's mesh projection, seen orthographically on the view's basis, in pieces of one visibility
on the sheet. Each line names its occurrence path, its body and its source (an edge, as model.edges
numbers it, with whether it is a tangent edge, or a silhouette across a face). They are marked
approximate and sampled (Cameron's choice, 2026-10-27): the exact lines (W06) take over per body
after the kernel swap (V8).

- display_style visible or visible_hidden; tangent_edges hide or show; chord_mm, the paper chord
  the meshes are asked for (0.001 to 1, 0.01 by default). The model chord is chord_mm over the
  scale: the view DXF's 1 mm cap doesn't apply, so a view below 1:100 keeps its chord on paper;
  its 0.0001 mm floor does, and a chord under it is refused with the chord_mm that would do.
- max_error_mm is a check, never a setting (the accuracy rule on #5866): refused while any piece
  is sampled, with the count of pieces short of it.
- The three view DXF changes moved here from V1: each segment keeps its owner (its placement and
  its edge or silhouette face); a stretch two sources share belongs to the nearest visible one,
  else the nearest hidden one (level ones in owner order), ends where that changes, and counts as
  covered; the triangles keep their faces for that. The view DXF asks for neither, so it merges and
  writes as before. (Nearest, not first: a hole seen end on has its bottom rim under its top one,
  and by owner order the bottom rim took the stretches it shows through, cutting the top rim up.)
- A mesh side between two faces every edge between which is drawn is no silhouette: it lies on one
  of those edges, already drawn from its own curve. (Seen end on, a hole's wall faces neither way,
  and the side it shares with the top face drew stray chords just inside the rim.) The triangles
  keep their faces for it in both commands (4 bytes each, in the memory budget); without them the
  view DXF drew a hole's rim twice, and a view DXF test now finds every line drawn once.
- A triangle seen edge on, its normal at most 1e-3 (cosine) toward the view, faces away whatever
  sign the mesh's own error gives it, so no silhouette lies between two of them. (Seen end on, a
  bend's surface drew its rows as chords beside its arcs: a 60° bend's outer arc 1.76 times over.
  A view DXF test now finds each of its arcs drawn once.)
- Mesh positions within the tolerance weld: a position takes the key of the first one within it,
  in its own rounding cell or a neighbouring one. (The faces round a hole's rim are meshed apart,
  their copies of a rim vertex up to about 1e-7 mm apart, and copies either side of a cell
  boundary never met: each copy's side was a boundary, drawn as a silhouette.) The view DXF takes
  these three rules too; they change its lines only where a side lies along a drawn edge, a
  triangle is seen edge on or a weld was missed. The sample designs' view DXFs lose lines, and
  each line they gain lies within 0.0011 mm of an old one.
- One source's stretches join into runs within twice the tolerance the lines were merged at: the
  merge drops spans up to that long, so a stretch can stop that short of a corner on either side.
- The tangent test is a stadium of cubic segments: the kernel marks a join smooth only between
  faces swept from cubic segments, and one between two pieces of one surface is a seam, never
  drawn; a fillet's round keeps its edges, as the viewport draws them.

compile fix by the personal PC

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

* Drawing views: a circle facing the view is drawn as its exact arc (#5866, W07 V3)

An edge that is a circle, or part of one, in a plane parallel to the view
is drawn as its exact arc from Body::planar_edges instead of the mesh's
chords. Where the mesh projection says its visibility changes, the arc is
cut there: a piece that runs along all of the edge is analytic, a cut
piece keeps the mesh's sampled accuracy; its ends may stop within the
stretches' join tolerance of the edge's own, as the merge leaves them.
planar_edges is asked once per shared mesh and placement turn, for the
curved edges only; a part placed again the same way takes the arcs
shifted on the view's plane.

compile fix by the personal PC

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

* Drawing views: a view's visible lines as a paper scene group (#5866, W07 V7a)

The paper scene gains a kind for views (ObjectKind::View, layer VIEW).
drawing_view::paper_group turns a view's lines into its group: each
visible piece one stroke, 0.5 mm wide until D2 sets widths by kind of
line, closed when it ends where it starts; a line with no length is left
out and an arc too short to sweep is its chord. W14 calls it for saved
views.

drawing.view.snapshot takes scene: true and replies with the lines as a
paper scene of its sheet, checked by Scene::validate before it is
returned. Hidden lines need dashed strokes (A5, #7430), so scene with
visible_hidden is refused until V7b.

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

* Drawing views: a view draws the occurrences its source names and hide leaves out (#5866, W07 V4)

drawing.view.snapshot takes source: {occurrences: [[ids]]}, the occurrence
subtrees it draws ([] the design's own bodies; every body when not given),
and hide: [[ids]], subtrees left out. A body is drawn when its path is under
a source path and under no hide path, so a hidden path wins over a source
path above it. The rule is one filter on the shown set (ShownSet::keep), so
the extents, the lines and the paper scene follow it; each body costs one
look-up per head of its path, at most 64.

Each path is checked by Session::occurrence_placement as it is (each id in
its parent, no id twice, 1 to 64 ids), with at most 1000 paths in each list
and 4096 ids in all. [] in hide and a choice that leaves nothing are
refused. M2's occurrence tree will make the check one step per id later,
inside occurrence_placement.

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

* Drawing views: a bend seen end on draws its arcs once at any angle (#5866, W07 V4b)

A triangle whose surface is seen edge on at each of its corners (the mesh's own normals, at most
1e-3 toward the view) now faces away, whatever lean its facet has. A thin triangle's facet leans by
the mesh's chord error over its own width, which can be far past the 1e-3 a facet may lean and
still count as edge on. Seen end on, a 90° bend's surface is meshed with its edges' samples at its
ends and its rows at even angles inside, 1.667 mm in; the triangles that join the two lean 1.1e-3
to 5.8e-3, so each one facing the view among its neighbours facing away drew silhouettes along its
sides: chords beside the arcs, 1.14 times the inner arc and 1.69 times the outer. The view DXF
takes the rule too.

The export meshes keep their normals for it: 24 bytes a vertex, a position's size again, counted
in the export's 512 MB mesh budget, so the largest view that budget takes is about 30%
smaller. A mesh without normals keeps its facets' facing.

Tests: the bend test takes a 90° bend beside the 60° one; each bend circle's lines add up to its
arc, less only what sampling takes off.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 00:25:05 -04:00
CamWight 8679063629 Drawing revisions: rows added, edited and deleted by command, and listed by drawing.inspect (#3874) (#9150)
* cell: rules both kinds of table share (#3874)

Sheet tables (parts lists first) use the title blocks' cell content, so
the rules both read live in cell.rs:

- Source::Row: the row of a sheet table a cell is on. A title block has
  no rows, so its validation refuses a cell that reads one.
- Warning::Width: a word wider than its value box, shown whole, never
  cut: the width warning that measured text brings.
- is_snake_case moves here from title_block.rs: table columns are named
  by the same rule as cells. check_text is shared the same way.

Tests: a title block cell reading a row is refused, beside other parts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 640cc3e0f5)

* Sheet tables: the parts-list table model and layout (#3874, P7a)

A Table is a sheet table's definition: its columns, the side its heading
is on (top, or at the bottom with the first row just above it, ISO
7573), its row height (7 mm by default), text height, line widths, a
filter and overrides. A column's cells are the title blocks' cell
content read against each row: row fields and text. Rows are input, in
their listed order, with their values worked out and formatted, so a
filter (parts, assemblies, purchased, text per column ignoring case)
hides rows and never changes a count or a quantity.

Table::layout places it with a text measurer, from the right end of the
heading's outer edge:
- a column without a set width is as wide as its widest line, padding and
  line insets added; a set width is kept;
- every cell in a set-width column breaks between words to fit, and its
  row grows to hold its lines, 10/7 cap heights apart (ISO 3098-0); a
  word wider than its column is shown whole with Warning::Width;
- a row field the row doesn't have shows "-"; an override shows its text
  and keeps the value it replaces, an empty one is a blank cell, and one
  for a row that has gone is kept;
- cells sit inside their lines as a title block's do.

Limits, refused and never truncated: 10,000 rows and overrides, 64
columns, 1 MiB of text in all (each cell's text counted before it is
built), 256-byte keys, a measured width that isn't finite and at least
0, a table off the paper's coordinates.

Nothing is saved or drawn yet: the real measurer, DXF and CSV output and
the command come with P7b.

Tests: hand-worked cell corners and text anchors with a fixed-width
measurer, the heading at the top and at the bottom; wrapping and the
row it grows; a word too wide; overrides, blanks, "-"; each filter
leaving every shown value as given; JSON round trip, defaults and
unknown fields; every limit and one past it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 0c5180267d)

* Revision record and table (#3874, W31, P14a)

A drawing's revisions as data (geom::revision): one RevisionRecord for
the whole drawing, or per sheet with each sheet numbered apart.

- A row holds what people type: revision, description, designer,
  checker and date. Adding a row fills in only the next revision;
  nothing fills in a date, a checker or an approval.
- Numbers run from 1, or 0; letters run A to Y without I, O, Q, S, X
  and Z, then AA, AB. The next revision follows the highest in its scope
  among the rows and the issued history, so an issued revision is never
  given out again. A typed revision is kept as typed; in letters one
  with a skipped letter is accepted with a SkippedLetter warning.
- Rows are added, hidden and deleted; issue() appends a copy of a row to
  the issued history, which nothing removes or changes (private, read
  through issued()). Row and tag ids come from the caller (the
  design's random ids), never from a counter, so two people adding rows
  never take the same id; an id in use is refused.
- Triangular tags bind to a row id and show its revision or their own
  text; deleting a row reports its tags, which keep the revision they
  showed, marked lost.
- The revision table is the sheet table (table()): Revision,
  Description, Designer, Checker, Date, heading at the top; hidden rows
  are left out. The table's rows and a title block's revision fields
  (newest(), hidden rows included) read RevisionRow::field, one path.

This is the drawing's revision only: the model's revision and the
export time are kept apart. Release, approval and frozen snapshots
(G16/G22) are not in this slice. Nothing is placed or saved on a sheet
yet (W12, W15).

Tests: numbering both ways with the skips and past Y and YY, typed
revisions and the warning, the next after issued and deleted rows, per
sheet; add fills in nothing else; history kept through hide and
delete; tags reported and lost; the title block's fields and the table
rows from one function, laid out by the sheet table; JSON round trip,
unknown fields and damaged records refused; the caps; an id in use refused.

- An issued copy keeps the model version it was issued against
  (model_version, given to issue(); none until version control gives
  versions, #2869), so the release step has its field here. It is read
  by the label rule, like the revision.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit fa7849ada7)

* Drawing revisions: rows added, edited, deleted and listed by command (#3874, W31)

A drawing saves one revision record for all its sheets (Drawing::revisions, behind an Arc as the
sheets are, so an undo step shares it; validated with the drawing; its ids among the drawing's ids
in use), in the order its rows were added, the newest last. Pending drawing format: revisions.

- drawing.revision.add {set?}: a row with the next revision, after the highest the drawing has
  given, and any texts given: one undo step.
- drawing.revision.edit {row, set: {revision?, description?, designer?, checker?, date?,
  hidden?}}: the texts as typed (a revision is one line) and what changed; a revision in letters
  with a letter the lettering skips is kept and warned about.
- drawing.revision.delete {row}: the row, and the tags it leaves showing the revision they had.
- drawing.revision.list: every row, the oldest first, and the revision a new row gets.

An id's text form (13 characters of lowercase Crockford base32) moves down to solidcraft_geom::ids,
with an option helper, so the revision record (geom) writes its row and tag ids as every drawing id
is written; solidcraft_doc::ids gives them on, beside IdSource.

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

* Drawing revisions: listed by drawing.inspect, a table row keyed by its id's text, and a record per sheet tested (#3874, W31)

The rows show in drawing.inspect beside the sheets, as `revisions: {rows, next}`, from the same
function the revision commands answer with; drawing.revision.list goes, so a drawing's rows have one
place to be read. A revision table's row is keyed by its id as a file writes it, the one spelling
every drawing id has (a table's cell overrides name a row by its key). A record kept per sheet whose
rows keep its own rules is refused by the drawing, so that rule has a test of its own.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 00:22:51 -04:00
CamWight f87b66d759 Drawing balloons: the record, its rules and its commands (#3874) (#9126)
* Drawing balloons: the record, its rules and the sheet hooks (#3874)

A balloon points at a part placed in its view's model by an edge of it, saved
by name as a dimension's edge is (ModelEdge), with the occurrence path as the
part it numbers, and sits at a place on paper from its view's centre. The
dimension module's reference checks and strict lookup are shared, not copied.
Balloons go with their sheet, list in on_sheet and take ids from the drawing.
One pending drawing format file; a drawing without balloons saves as before.

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

* Drawing balloons: add, edit and inspect, with their schemas (#3874)

drawing.balloon.add puts a balloon on a saved view, pointing at a placed part
by an edge picked as a dimension's is and saved by name; edit moves it on
paper; inspect says where it stands with the model now, with the arrow's
point on its edge's middle. Add and edit are one undo step each, and none of
them evaluates the model. The status words come from one function shared with
the dimension commands.

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

* Drawing balloons: a copied view takes its balloons, and views and sheets list them (#3874)

A view's balloons are part of what it shows, so copying a view copies them,
with ids of their own, as its dimensions are copied. drawing.view.inspect
lists a view's balloons, and deleting a sheet names the balloons on it.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 23:49:00 -04:00
CamWight a09c24e9bf Drawing dimensions: a circle's or an arc's radius and diameter (#4896, W20a) (#8959)
* Drawings saved inside the design's file, in both forms (#3890)

K3b of the drawing storage plan, after #7098: a drawing lives in the file of the design it draws
(bherbruck's proposal, Cameron's choice), so the separate drawing file goes.

- `Document.drawings`: the design's drawings, each its own record of the drawing family
  (`solidcraft-drawing/N`), each behind its own `Arc`. Every reader and writer of a design now
  carries them: save, Save As, autosave and recovery, crash copies, the CLI.
- A design container keeps each drawing in its own entry, `drawings/<id>.json` (Deflate), beside
  `document.json`, which holds nothing of them: its bytes, and the model cache keyed to them, are
  the same with drawings or without. A plain JSON design keeps them inline under `drawings`.
- Each drawing is read by its own family's rules, taken out of the design's JSON before the design's
  own checks. One a build can't read (a newer number, a change it doesn't know, damage) refuses the
  whole design, never dropped. So does an entry under `drawings/` that isn't a drawing's, one whose
  record has another id, drawings inline in a container's `document.json`, two drawings with one
  id, or more than 1,000 (counted before any is read). A design is never saved with two drawings of
  one id.
- Drawings are listed by name, then id: no stored order, nothing to merge when two people add
  drawings.
- A drawing has an `id`, unique among its design's drawings (`Drawing::new` takes the ids already
  taken). Its source is the design it is in: at most one, an id and no path.
- One pending design change, `drawings`: the last release refuses a design saved with it, so it
  never opens one and saves it without its drawings. One pending drawing change, `in-design`, for
  the record's new shape.
- Gone: `read_drawing`, `read_drawing_file`, `write_drawing_container`, `file_kind`, `FileKind`,
  `DRAWING_EXT` and `is_drawing_path`. `read_document`, `read_design` and the container writer are
  as they were before #7098, with the drawings added.

Tests: a design's drawings round-trip through both forms byte for byte, with `document.json` and
`container.json` the same with drawings or without; their order; every refusal above; a drawing's
properties kept as other tools wrote them while a sheet's number out of range is refused; the
drawing's id among its objects', a new drawing's id none of the design's; a design with two
drawings of one id not saved; mutation fuzz of a design with drawings in both forms.

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

* Drawing commands on the design's own drawings, each drawing its own undo (#3890)

K4' of the drawing storage plan, on K3b (a drawing lives in its design's file): make, list, open,
close and delete a design's drawings, and read and change the open one's settings and properties.
It replaces #3891's drawing sessions, which held a drawing as a file of its own.

- `drawing.create {name?, language?, units?}` adds a drawing of the design (one ISO A3 landscape
  sheet with the ISO border, the design its one source) and opens it; refused on an empty design.
  `drawing.list`, `drawing.open {drawing}`, `drawing.close`, `drawing.delete {drawing}`.
  `drawing.inspect`, `drawing.set`, `drawing.properties`, `.properties.set`, `.properties.remove`
  work on the open drawing. Labels in en, ja and zh.
- The open drawing is the session's (`Session::open_drawing`, kept in checkpoints), and the part
  gate keeps the parts apart: a command says the part it works on, the model unless it says
  otherwise (`.in_drawing()`, `.any_part()`). While a drawing is open the model's commands wait
  ("a drawing is open: close it to change the model"); the drawing's run only while one is.
  Files, recovery, undo and redo, checkpoints, selection, diagnostics, the command list and
  choosing a drawing work either way. `CommandSpec::allowed` is the one check running a command,
  running it inside another and listing it make.
- `Session::drawing` and `Session::edit_drawing` are the open drawing's one way in: an edit is kept
  when it changed something and still keeps the record's rules, in a new design whose other
  drawings are shared.
- A change that only touches drawings evaluates nothing: `Document::differs_only_in_drawings`
  joins the render-only path, as render settings and part properties do. A model edit shares
  every drawing, so it pays nothing for the check.
- Undo per part: a step records the part it changed (`Snapshot::part`: a drawing, or the model
  and its list of drawings). Undo and redo go through the open part's steps only. A drawing's
  step puts back only that drawing; the model's step puts back the design as it was, keeping each
  drawing it had as it is now. Making and deleting a drawing are the model's steps. A new step
  ends what could be redone in its own part only. `Snapshot::of` is the one place a step is
  taken.
- An agent's message that changed several parts keeps their steps apart, rather than merging them
  into one step that would undo only one part.
- `cmd::metadata`'s rules for reading, setting and removing properties are written once
  (`reading`, `setting`, `removing`) and used by the design's and the drawing's commands.
- `drawing::sort` is the one place the drawings' order (by name, then id) is kept;
  `Document::put_drawing` adds or replaces one in that order.

Tests: making, listing, opening, closing and deleting; what making one refuses, at the cap too; the
gate both ways, inside another command and in the command list; drawing edits as undo steps that
evaluate nothing over 100 rounds; undo and redo per part with model and drawing steps interleaved;
redo ended per part; the order by name, then id, after renames; delete and its undo; refused
changes; checkpoints; save and open in both forms; properties; hostile values for every command
that runs with a drawing open, from the registry; a fuzz of the steps in any order.

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

* Unknown-command tests: the closest id first (#3890)

drawing.open now joins doc.open in the suggestion for `file.open`, as the rule says: both end in
the same word. The engine's and the CLI's tests check that doc.open comes first and let other
close ids follow, as the `solid.extrud` line beside them already does.

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

* Model bodies: their extents seen on a plane, for a drawing view's size (#5866)

ModelBody::mesh_bounds_in(plane) is the box of a body's mesh in a plane's own coordinates
(Plane::to_local), beside mesh_bounds and in the same way: a body meshed already is measured from
its own mesh, and a placement of a part from its definition's shared mesh, with the placement
folded into the plane's two axes once (two dot products a point), so no moved copy is made. W07's drawing.view measures a view's extents with it.

Tests: a 100 x 50 x 20 block seen on the three principal pairs of axes and from an isometric
corner (150/sqrt(2) by 190/sqrt(6), worked by hand), a plane away from the origin, and every
placement in the shared-mesh test against its moved mesh, still without a moved copy, and the
same again from its own mesh once it is meshed.

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

* Drawing views: projected views in first or third angle, lined up with their parent (#4816, W17a)

W17a's second slice: the principal views derived from a base view.

- `ProjectedSide` (left, right, above, below): the side of its parent a
  projected view sits on.
- `projected_basis(parent, side, projection)`: in third angle a view shows
  the side of the model it sits on (one to the right looks from the right), in
  first angle the side away from it (ISO 5456-2). It's the parent's basis
  turned a quarter turn about one of its paper axes, through its origin, with
  no -0. From the front view it gives exactly the named views: right, left, top
  and bottom in third angle; left, right, bottom and top in first.
- `ViewFrame::aligned(side, projection, centre, position_mm)`: a projected
  view at its parent's scale, lined up with it, so it moves only along the free
  axis.
- The projection is S2's `cell::Projection`, the value the title block's
  symbol reads, so a drawing has one first-or-third setting.

Tests: the front view's projections are the named views in both angles, with
no -0; from every named view and a leaning one, a projected view looks from
its side and keeps the parent's other axis and origin, four turns or a turn
and its opposite bring the parent back; a box's corners line up across
aligned views in both angles; hostile parents, centres and positions refused.

Corner (isometric) projected views wait for one convention check on first
angle. The derivation graph (projected from, aligned to, cycles refused) moves
to the view record in the drawing file, where the view ids live.

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

* Drawing dimensions: measurement on view frames (#4896, W19a)

`geom::drawing_measure` is what a dimension on a view reads, in model mm or radians, from model
points and edges and the view's basis: one rule for every dimension. The view's scale and place
on the sheet never enter.

- `linear`: horizontal and vertical along the view's paper x and y, aligned as the view shows
  the segment, or along a direction picked on the view; a true reading is the segment's own
  length (aligned only).
- `angle`: from the view's horizontal or vertical, a model axis as the view shows it, or
  another edge, from 0 to π as each line is oriented; a true reading is between model lines.
- `foreshortened` says a measured line isn't parallel to the view's plane.
- Refused with a reason: points that aren't finite or are past 1e12 mm, an invalid basis, a
  zero-length segment, a line seen end-on, a zero direction, a zero reading, parallel lines,
  and a true reading where there is none. `drawing_view`'s `PARALLEL` and `require` are shared.

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

* drawing.view.snapshot: a view's scale, place and boundary on its sheet (#5866, W07 V2)

The first slice of W07: one orthographic view of the design, worked out and returned as data,
before any line is drawn, so scripts can lay views out by their boundaries.

- `drawing.view.snapshot` takes the view's settings, the sheet it goes on (A3 landscape at
  300 dpi when not given) and where its centre goes (the sheet's centre when not given). With no
  scale it takes the largest ISO 5455 scale at which the view, centred there, lies inside the
  sheet's drawing border. It returns the resolved view, the scale (and whether it was fitted),
  the extents on the view's plane, the boundary on the sheet and what it was drawn from.
  Read-only and not undoable; it opens nothing and saves nothing.
- Every body the design places is in the view, wherever its occurrences place it; the browser's
  eye counts for nothing (`engine::drawing_view::extents`, from P2's `mesh_bounds_in`). A design
  whose assembly walk stops at its limits (deeper than 64 levels, or past 100,000 placements) is
  refused rather than drawn with parts left out: `Document::placements_complete`, beside the walk,
  says whether every placement it holds had its occurrences walked.
- `doc::drawing_view::ViewSettings` is the view's one parameter set: the view (named, or a
  direction and an up), the scale, the two visual styles, the reference settings and tangent
  edges. A saved view (W12) keeps the same fields; no scale is written as none, not null, so
  what it writes reads back as the snapshot's parameters.
- `render::VisualStyle` names the five styles: visible, visible_hidden, shaded, shaded_visible
  and shaded_visible_hidden. The shaded ones are refused as not supported yet until the drawing
  renderer (#5624).
- The command has a typed contract (input and output schemas). Its handler reads the contract's
  own type through `schema::read`, in the contract check's own words, so the check's levels give
  one message for one mistake and an unknown field is refused by name. The reply includes the
  sheet, its defaults filled in.

Tests: the 100 x 50 x 20 block's boundary in the front, top, right and isometric views at 1:2,
worked by hand; the fitted scale on A3 and A4 and near the border; scale, position and margin;
the browser's eye ignored; the design untouched; each hostile setting refused; the walk's
depth limit one level either side; the contract's keys the same as the settings' own.

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

* Drawing views in the drawing record: base and projected views, and the projection (#4816)

E4 of the view-boundary plan, with #4816's derivation graph (plan on #4816).
A drawing now saves its views, as one pending drawing-format change
(`drawing/format/pending/sheet-views.rs`):

- `Drawing.views`: each view an object of the drawing, on one of its sheets.
  A base view draws a source from a direction (`ViewDirection`, as
  `drawing.view.snapshot` takes it); a projected view sits on one side of its
  parent. Its place is a point of the sheet, or, lined up with its parent, only
  how far along the free axis, as the other comes from the parent. A projected
  view's source and basis come from its chain, so neither is saved.
- `ViewOverrides` beside V2's settings: what a view sets itself, each setting
  on its own (scale, display style, reference style, reference parts hide the
  model, tangent edges); none is written when left out. Each view has its own
  reference margin.
- `Drawing.projection`: first or third angle (S2's `cell::Projection`). Third
  angle is the type's own default (Cameron, 2026-10-26), for a drawing that
  doesn't say and for a new one until a template or a preference chooses.
- The rules, checked on reading and after every edit: a view is on one of the
  drawing's sheets; a base view draws one of its sources, has a scale and a
  direction that makes a view; every parent is a view, no chain of parents
  loops, and a chain is at most 32 views; a lined-up view is on its parent's
  sheet; a place is on paper; at most 4,096 views. A view's id is the
  drawing's, never another object's.
- `Drawing::resolve_view`: one rule for every reader. It turns the base view's
  basis down the chain (`projected_basis`, in the drawing's projection) and
  takes each setting from the nearest view that sets it, then the default,
  saying who gave it.
- geom's `is_paper_coordinate`: the one rule for a view's place on paper, now
  used by `ViewFrame::new` and the record.

The record tests' seed drawing has views, so its round trip and mutation tests
cover them; their unknown-field examples no longer use `views`.

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

* Engine tests: one temporary folder helper and the schema check for every test module (#4816)

The view tests use the drawing tests' temporary folder and the schema tests' `conforms`, which were
private to their modules. `Folder` moves to the engine's test module and `conforms` is open to the
crate.

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

* Drawing views: inspect and edit a saved view (#4816, W17c)

E5 of the view-boundary plan (plan on #4816): what the view's window reads and
writes, for the views of a drawing (E4). Both are drawing commands with
input and output schemas, and neither evaluates a model.

- `drawing.view.inspect {view}`: its kind (base or projected), sheet, source,
  parent and side, its direction as resolved (named only when it is exactly a
  named view), its place, and each setting as {value, from, parent?, set}:
  where the value comes from (the view, a parent by id, the default) and what
  the view saves itself. A projected view says whether its display settings
  follow its parent (`linked`: true, false or "partly"). `choices` gives the
  display styles and the preset scales, so a window keeps no list of its own.
- `drawing.view.edit {view, set}`: one undo step. Each setting takes a value,
  or null to follow the parent (a base view keeps a scale); the margin, a base
  view's direction and the place change too. `linked: false` saves the display
  settings at the values they have, `linked: true` clears them so they follow.
  The reply names the fields changed and the views projected from it whose
  settings changed with it. The record's rules hold after every edit; an edit
  that changes nothing is no step.

The contract test keeps the edit's fields and the record's (ViewOverrides) one
list. The view's boundary comes with the next slice, which works out where each
saved view sits on its sheet from its design's extents.

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

* Drawing views: where a saved view sits on its sheet (#4816)

`Drawing::view_centre` gives a view's centre on its sheet: its point, or, lined up with its
parent, its own place along the free axis and the parent's centre across it. The free axis is
one rule on `ProjectedSide` (`along`, `on_free_axis`), which `ViewFrame::aligned` uses too.

`engine::drawing_view::on_sheet` puts a saved view on its sheet: its design's extents on its
plane, grown by its reference margin, at its scale, round its centre. The snapshot and saved
views share `measure` (refusing an assembly past the walk's limit or one with no bodies) and
`boundary`. With nothing to measure (the design has no bodies or places more than a view walks) a
view gets a 20 x 20 mm placeholder round its centre. A view's source is the design the drawing
is in, so the session's own model is what it measures.

`drawing.view.inspect` adds `boundary_mm` and `extents` (status `placeholder` and why, when there
is nothing to measure). It still evaluates no model.

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

* Projection of new drawings: a preference, and drawing.set takes the projection (#4816)

A drawing's views follow its projection, first or third angle (third when nothing chooses).
`drawing.inspect` now shows it and `drawing.set {projection}` changes it as one undo step.
Preferences > Design gets a Drawings section with "Projection of new drawings"
(First angle / Third angle). A new drawing takes it as a new design takes "Units of new
designs": the app sets it after `drawing.create`, with no undo step of its own. A saved
value this version doesn't know reads as third angle; asked for over the control channel,
it is refused.

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

* Drawing sheets: one edit, one lookup, a border that lays out, and schemas (#3875)

The sheet commands change the drawing through `Session::edit_drawing`, so the same settings or
order again leave no undo step, and answer from one reply type. A sheet is looked up by id or
name on `Drawing` (`sheet_index`), where later commands that name sheets find it too.

A sheet's border must lay out on it: the record's check runs the border's own layout, the one
rule drawing uses, so a 15 x 15 mm sheet with the ISO border is refused when it is made, not
when it is drawn. `excluded` must be an object of `count` and `print`: a mistyped key changed
nothing unseen. A new sheet documents nothing and leaves nothing out unless told, and the add's
parameter documentation says it takes `documents` and `excluded` too.

The drawing's properties sit behind an `Arc`, as its sheets do, so an undo step shares them.

Each sheet command has an input and an output schema (#4035). A flattened part of a contract
reports its keys as the parent's own (`schema::flattened`, which `FeatureCommon` now uses).
The tests add the missing cases: 1000 sheets and the 1001st, `after` by id and unknown, the
resolution's bounds, a name two sheets share, the same order and settings again, an undone
delete in place, and the add's reset of what it documents and leaves out.

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

* Drawing ids: one id source per session, its seed kept for replays (#3890)

The drawing commands drew ids from a fresh random source each time, so a crash replay of a drawing
could never find what an earlier command made by its id. A session now has one id source
(`Session::ids`), kept across a file change, from which `drawing.create` draws its drawing's,
sheet's and source's ids: seeded alike and run alike, a session gives the same ids, and a failed
command draws none, so a replay of what succeeded draws the same. The crash journal keeps the seed
at each base and `replay.json` carries it, which `solidcraft-cli crashes replay` seeds the replay
with (`Session::seed_ids`).

Tests: two sessions seeded alike make a drawing with the same ids, another seed other ids.

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

* Drawing sheets: add, change, reorder and delete a drawing's sheets (#3875)

W18 S4: the sheets of a drawing, on the drawing session K4 made (#3890). Each command is one undo
step of the drawing and names a sheet by its id or, when only one has it, its name.

- `drawing.sheet.add {after?, name?, size?, orientation?, dpi?}`: a new sheet after the one named
  (the last when not), taking that sheet's paper, border and resolution, with a name of its own
  ("Sheet 2") and nothing documented yet.
- `drawing.sheet.set {sheet, name?, size?, orientation?, dpi?, documents?, excluded?}`: one
  setting at a time, the rest kept; `documents: null` clears what it documents, and `excluded`
  changes only the flags it gives.
- `drawing.sheet.reorder {sheets}`: every sheet's id once, in the new order.
- `drawing.sheet.delete {sheet}`: a drawing keeps at least one sheet.
- Every change goes through the drawing's own rules (`Drawing::validate`), so what a saved file
  would refuse is refused here. Changing a sheet's paper changes only the paper: regrowing its
  border and moving what is on it is S5's. Labels in en, ja and zh.

Tests: a new sheet copying the paper it follows, after a named sheet too, and undone; each setting
changed with the others kept; nine refusals (resolution, sizes, orientation, name, an unknown
source, a flag, an unknown sheet, no sheet named) with the drawing unchanged; reordering, and four
orders that aren't every id once; a name two sheets share refused in favour of the id; the last
sheet kept.

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

* Drawing sheets: a new sheet's id from the session's id source (#3875)

`drawing.sheet.add` draws its id from `Session::ids` (#3890's one id source per session), so a
crash replay of "add a sheet, then change it by id" finds the sheet: seeded alike and run alike, a
session adds sheets with the same ids, and a failed add draws none.

Tests: two sessions seeded alike add the same sheet ids, another seed others, a failed add none.

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

* Drawing views: move or copy a view to a sheet (#4816)

`drawing.view.move_to_sheet {view, sheet, position_mm?, break_alignment?}` puts a saved view on
another sheet as one undo step, keeping its id, direction, source and settings. The views lined
up with it go with it and keep their places beside it; a view placed apart stays. Without a
position it keeps its place. A view lined up with its parent moves only with `break_alignment`,
and is then placed apart: at the position, else the sheet's centre. Its own sheet changes
nothing.

`drawing.view.copy_to_sheet {view, sheet, position_mm?, with_linked?}` makes new views, each
following none of the views it was copied from (Cameron's rule). A view with views lined up
with it needs `with_linked`: true copies them too, lined up with the copy; false copies it
alone. A projected view copied alone becomes a base view, looking the way it did (by name when
it is exactly a named view) with the settings it had as its own. The copy goes to the position,
else the sheet's centre.

A lined-up view's centre comes from the places alone: its place along the free axis, and its
parent's centre across it. So the views that travel with a view are shifted along their free
axis, and nothing needs the design's extents. `descendants` takes which views it follows, so the
edit's followers and the views lined up with a view are one walk.

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

* Drawing views: move and copy take a view's centre from the record (#4816)

Moving or copying a view to a sheet worked out a view's centre with its own copy of the
lined-up rule. It now uses `Drawing::view_centre`, and a lined-up view's place moves along
its free axis by `ProjectedSide::along`, so the rule is written once.

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

* Drawing sheets: a change of paper lists the views it leaves outside, and moves none (#3875)

W18 S5, its first part. `Sheet::drawing_space` is where a sheet's drawing goes: inside its
drawing border, or the whole paper with none. Nothing on a sheet is saved against the border,
so a change of paper regrows the border and its zones by itself.

`engine::drawing_view::outside` lists the views whose boundary (`on_sheet`) leaves their sheet's
drawing space, sides included. `drawing.sheet.set` lists them after a change of paper (size or
orientation; nothing else moves the space, so nothing else is measured), and moving or copying a
view to a sheet lists those it puts outside there. None is moved: where a view goes stays the
author's choice. A view with nothing to measure is listed by its placeholder, with why.

Tests: the drawing space with the ISO border, a filing margin and no border, and margins that
leave none; A3 to A4 either way up listing the right and top views with their boundaries and
moving nothing; a border through a view's edge leaving it inside, 0.1 mm less paper putting it
outside; a change of name checking nothing; undo; a move to a smaller sheet listed and a copy
that fits not; a placeholder listed as `past_walk_limit`.

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

* Drawing ids and the sheet border: the tests the slices left out (#3890, #4816, #3875)

What the slices' planted faults found untested, now each caught by a test:
- a file change keeps the session's id source: a drawing made after `file.new` draws the same ids
  in two sessions seeded alike;
- a copied view's ids come from the session's id source, seeded alike giving the same copies;
- a crash report's replay carries the session's id seed, from a caught panic (the seed when the
  command started) and from the journal of an unclean exit (the seed at its base);
- a view short of the sheet's border by less than a rounding is still inside it.

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

* Drawing views: listed by id, whatever order they were added in (#4816)

The record's rule says a drawing saves its later records by id, never in
the order they were added in. Views were kept, and saved, in the order they
were added, so the same views added in another order saved as other bytes.

- `Drawing::add_views` adds views where their ids put them. It is the one way
  views are added: `drawing.view.copy` uses it, and so do the test fixtures.
- `Drawing::validate` checks the order, so every edit keeps it.
- Reading puts a file's views in id order before it checks the record. The
  order means nothing, so a file in another order (saved before views went by
  id, or edited by hand) isn't damaged: it opens, and saves by id.
- What a command lists in the drawing's order (the views a change of paper
  leaves outside, an edit's followers) now comes by id.

No format change: no release has saved a view (the views change is still
pending).

Tests:
- views added out of id order are listed and saved by id;
- the same views added the other way round make the same drawing and the same
  bytes, which read back as saved;
- a file that lists them the other way round reads by id, as the same
  drawing, and saves as those bytes;
- an edit that leaves them in another order breaks the record's rule;
- the existing tests now index the file by id order, and the engine's lists
  are by id.

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

* Drawing dimensions: the dimension record, saved by name on a view (#4896, W19b)

A drawing saves its dimensions: each on one of its views, measuring named
corners or edges of the view's model (a linear distance between two corners,
a straight edge's length, or the angle between two edges), with the direction
and reading W19a measures with, how far its line sits from what it measures,
where its text sits on paper, its decimals and any text shown instead of the
number. A reference names its body, the feature that made the body and its
occurrence path, never a picked point. Nothing worked out is saved.

- Dimensions are listed by id, read in that order whatever order a file has,
  as views are: one helper pair (add_by_id, in_id_order) now keeps both lists.
- One cap for the names a saved model reference keeps
  (MAX_REFERENCE_NAME_BYTES, used where 512 was written three times), and the
  corner's edge cap shared with construct's vertex references.
- LinearDirection and Reading save as snake_case words; a direction along a
  line saves as its two coordinates.
- A direction along a line is at most 1e9 each way (format::MAX_REAL, the
  limit the file's number check holds every real number to): a whole number
  past it passes that check, reads as a real, and would save as one the next
  read refuses (found by the mutation test).
- One pending drawing-format file (dimensions.rs).

Tests: what each dimension saves and reads back as the same bytes, a file in
another order reads by id, an older drawing has none; every rule on both sides
of its cap, unknown fields at every level, the id rule, and the count cap; the
plate fixture carries one dimension of each kind, so the existing round-trip
and mutation tests cover them.

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

* Edges and corners by name: the matching step and the shared ends, each in one place (#4896, W19b)

A dimension finds an edge by name the way a fillet does, but never takes
the piece nearest an old pick, and finds a corner where its named edges
meet, refusing to choose between two. Both steps are split out of the code
that already does them, so there is one rule each:

- BodyEdges: a body's edge and face names, worked out once for every name
  looked up on it, and `matching(name)`: the edge with that name, else the
  one edge between the faces it names, else the pieces of it. locate_edge
  now calls it and then takes the piece nearest the pick, as before.
- construct::shared_ends: the ends every named edge has, each once.
  named_vertex keeps the one nearest its point, as before.

No behaviour change: every caller of locate_edge, find_edge and the vertex
references gets the same results. The existing naming, joint and
construction tests cover both paths; three new ones pin what they left
open: a vertex only where every named edge ends, the piece of a split
edge nearest the pick, and a lone match through the stem of a face taken
as a piece, with the warning.

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

* A body's frame through an occurrence path, in one place (#4896, W19b)

Document::body_frame_at(body, path): the placement the path leads to,
then the body's offset, without expanding every assembly instance. It is
what EmbossFace::body_frame composed for its saved path; that now calls
it, and drawing dimensions use it for the occurrence path they save. No
behaviour change.

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

* Drawing dimensions found again in the model, strictly (#4896, W19b)

Document::resolve_dimensions(model, drawing, ids) works out each dimension
as the model has it now:

- An edge by its name, or the one edge between the faces it names
  (BodyEdges::matching), never the piece nearest an old pick. A corner is
  the one end all its named edges, or their pieces, share; two shared ends
  are ambiguous, never "the nearer one".
- The body by its name and the feature that made it, placed through the
  saved occurrence path (Document::body_frame_at). A same-named body made
  by another feature isn't it.
- Status: ok; overridden (the text given is shown, the value still worked
  out); ambiguous (the candidates, at most 16, none chosen); suppressed (its
  feature is suppressed or rolled back, read from the evaluation's own
  skipped mark); lost (why); unmeasurable (found, but the view can't
  measure it so, in W19a's words, such as a line seen end-on).
- The value comes from W19a on the view's basis. An angle is the smaller one
  between the two lines, or pi minus it on its other side, whichever way the
  kernel runs the edges; a split edge's pieces on one line are still that
  line. A split edge's length is ambiguous.
- The text: the override, else length_number in the drawing's unit (or the
  design's) with the dimension's decimals, or degrees for an angle.
- The cost: each body named is looked up once per call, and its edge names
  worked out once, however many dimensions name it. A body none names isn't
  touched.

Not saved: nothing here changes the drawing. The last good value of a lost
dimension needs the session, so it comes with the commands (W19d).

Tests (engine, an 80 x 40 x 30 box, values by hand): front and right view
values, foreshortening and text, the drawing's own unit, several at once
and an unknown id; a change and its undo; a chamfer's 45 and 135 degrees,
and end-on in the front view; lost after a fillet and back on undo,
suppressed and back, a path to no occurrence, a new box with the same body
name; a notched edge's length ambiguous with both pieces while its end
corners still read 80 and its angle to the left edge 90 degrees, neither
exact; an angle on a piece of an edge whose slot is then suppressed still
reads 90 degrees, found through the stem of the face it was cut from, so
not exact; one edge named twice gives two corner candidates;
an override shown while the value follows the model.

Not covered by a test here: a part placed twice through real occurrences
(only a path to no occurrence is); the once-per-body cost (not measured).
A drawing unit outside display_units' five (um, yd, km, mil, nm, inch, µm)
shows millimetre numbers, the formatter's existing fallback; named as its
own follow-up.

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

* Drawing sheets: a sheet with anything on it goes only with it, when asked (#3875)

drawing.sheet.delete refuses a sheet with anything on it, and its
error names what is there: its views and the dimensions in them, the
first few ids of each. With `contents: "delete"` the sheet goes with
everything on it as one undo step, which an undo brings back whole.

What is on a sheet is said once, in the record (Drawing::on_sheet),
and Drawing::remove_sheet takes the sheet out with exactly that; each
later kind of record on a sheet (a sheet's own sketches) adds itself
there, so delete covers it with no change here. drawing.sheet.set's
list of a sheet's views uses the same function.

Nothing on another sheet goes with it: a view there projected from one
of the sheet's views would lose its parent, so the delete is refused
naming those views, to be moved or deleted first.

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

* Drawing views: a view found by id by halves, in one place (#4816)

The views are listed by id, so Drawing::view and Drawing::view_index
find one by halves instead of walking the list. Every lookup by id
uses them: a view's chain of parents (so resolving a view no longer
walks the list once for each parent), the views' rules (which no
longer build a map of every view to check each chain), a dimension's
view, and the engine's view commands and outside check.

The rules check the id order before anything looks a view up, so a
file with its views out of order is still refused as before.

Measured on a drawing of 4096 views (1024 chains of a base view and
three views projected one from another), median of 30 runs: 4096
lookups by id took 6.85 ms by a linear find and take 105 us by
halves; the rules' check took 1.77 ms and takes 1.45 ms.

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

* Drawing views: a damaged chain names the view whose parent is missing (#4816)

A view whose chain of parents breaks further up was refused with "its
parent X isn't one of the drawing's views", naming a view that isn't
its parent. Further up the chain, the refusal now names the view whose
parent is missing: "its chain of parents reaches view Y, whose parent
X isn't one of the drawing's views". A missing parent of the view
itself reads as before.

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

* Drawing dimensions: add one to a view, and read it back (#4896, W19d)

drawing.dimension.add puts a dimension on a saved view of the open
drawing; drawing.dimension.inspect reads one back with what it
measures now. Adding is one undo step of the drawing; neither
evaluates the model.

What a dimension measures is picked and saved by name, never by the
point picked. Document::edge_reference and corner_reference turn a
pick into the record's names: an edge is a point on it in the design's
frame or {body, index} (its index among the body's edges as the model
lists them, as an ambiguous dimension's candidates are given), and a
corner is a point or the edges that meet there, each with the
occurrence path its body is placed through. A pick that doesn't name
one thing is refused, saying what it found: no edge, a point on
several edges, a corner of more than one body, edges that don't meet.
Each body's edges are placed in the design's frame once per pick, and
only the body picked has its edge names worked out.

The kind is given as the record saves it ({linear: {a, b, direction?,
reading?}}, {length: …}, {angle: …}), read by the record's own types,
so a command and a file read the same words. A new dimension's line is
10 mm from what it measures (until a dimension style gives it), shows
2 decimals, and its text is centred on its line; the text's default
place at its centred point (Q58) comes with the layout on paper.

The inspect reply has the record, the status (ok, overridden,
ambiguous, suppressed, lost or unmeasurable, with why), the value
(model mm, or degrees for an angle), whether it is foreshortened, the
text, whether every name was found by exact history, and an ambiguous
dimension's candidates.

One rule, one place: the corner rule's point tolerance is
construct::point_tolerance, shared by the picks; a dimension by id is
Drawing::dimension, which the resolver uses too.

Not here yet: edit, the view's list of its dimensions, the layout on
paper and the last good value (the next slices); delete waits with
view delete for the drawing UI's choice of one delete command or one
for each kind.

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

* Drawing dimensions: change a dimension's settings (#4896, W19d)

drawing.dimension.edit {dimension, set} changes what a dimension
shows and where, as drawing.view.edit changes a view: the offset of
its line, its text's place, its decimals, the text shown instead of
its number (null shows the number again), and its kind's direction,
reading or other side. The reply names the fields that changed. The
same values again change nothing and add no undo step (the drawing is
put back only when it differs). What a dimension measures isn't a
setting: to measure something else, add a new one.

A setting its kind hasn't got is refused (an angle has no direction,
only an angle has an other side); every changed record passes the
check a file read runs, so an edit can't save what opening the file
would refuse. A dimension's place in the list is
Drawing::dimension_index, which Drawing::dimension now uses too, as a
view's is.

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

* Drawing views: inspect lists a view's dimensions (#4896, W19d)

drawing.view.inspect answers `dimensions`: the ids of the view's
dimensions, in id order, so a caller can find them and read each with
drawing.dimension.inspect.

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

* Drawing views: a copied view takes copies of its dimensions (#4896, W19d)

drawing.view.copy_to_sheet copies each copied view's dimensions with
it, each with an id of its own on the copy, in the same undo step:
they are part of what the view shows (the W19d plan's question 1, its
recommended answer). A dimension's text placed `at` a point is from its
view's centre, so it keeps its place on the copy. A moved view keeps
its own dimensions as they are: they name the view by id.

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

* Drawing dimensions: the last good value, in memory (#4896, W19d)

A dimension that can't be measured now (its edge filleted away, its
corner gone) gives `last_good`: the value it last read this session,
as `value` gives it (model mm, or degrees for an angle). Each value a
dimension reads is kept by drawing and dimension in the session, and a
scratch copy keeps the session's. It is never saved (the W19b plan's
question 3), so a design's drawings don't change with every model
edit, and after reopening a lost dimension shows no number.

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

* Drawing dimensions: hostile values anywhere in the commands' params (#4896, W19d)

A hostile value in each place of drawing.dimension.add, .edit and
.inspect's parameters, at every depth (the kind, its picks and paths, a
corner's edges, the edit's settings), with 1 000-item lists, the
largest index and 600-byte text besides the engine's shared hostile
values: no panic, and the drawing keeps its rules. The engine's own
hostile test sends only its list of key names, none of which these
commands read.

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

* Drawing dimensions: a new dimension's defaults, tested (#4896, W19d)

The offset (10 mm), the text's place (centred) and the decimals (2) a
dimension is added with when not told.

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

* A plane carried by a transform, in one place

`assembly::map_plane` is public and gives `None` for a transform that flattens the plane; its two
copies (the occurrence mapping, which keeps the plane as it was, and the face anchor's, which
refuses) now call it and keep their own fallback. W20a's radial dimensions use it to carry a
view's plane into a body's frame.

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

* Drawing dimensions: a circle's or an arc's radius and diameter (#4896, W20a)

A radial dimension saves its edge by name and whether it shows the diameter. It reads the edge's
exact circle in the view's plane through the edge (`Body::planar_edges`, as the view draws it), so
a view that doesn't see the circle face on can't measure it; its text is R or Ø before the number.
`drawing.dimension.add` takes `{radial: {edge, diameter?}}`, a diameter by default for a whole
circle or half a turn or more (a hole's rim is two halves), and refuses a dimension of any kind its
view can't measure; `drawing.dimension.edit` switches `diameter`, keeping the id.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 22:56:06 -04:00
Mateusz Sych ebafe07487 Drawing W06a: exact orthographic hidden lines for the analytic subset (#2455) (#2457)
* Drawing W06a: exact orthographic hidden lines for the analytic subset (#2455)

crates/geom/src/hlr (L0): prepare once per body, then hidden_lines for a
scene of placed bodies. Edges and face silhouettes are projected from their
exact curves, split where projections cross, and each piece is classified by
rays toward the viewer against the trimmed faces: visible, hidden or
explicitly uncertain, never guessed. Planes, cylinders, cones, spheres and
tori are supported whether stored as analytic records, revolutions or spline
patches; freeform faces are reported and make what they could hide
uncertain. Screen geometry is Seg2 with the W01a accuracy vocabulary: exact
lines and conic arcs where the projection is exact, bounded chords
otherwise. docs/drawing-hlr.md holds the contract and its limits.

* geom/hlr: handle the brep records main gained in #3093

Main's exact model storage (#3093) added Curve::SurfaceIntersection,
Surface::PlaneThrough and Surface::PlacedRevolution, and the HLR matches on
the records no longer compiled. A plane through three points is a plane: it
is trimmed and intersected as one, and a box stored that way draws the same
bits as one with axis-stored planes. The procedural intersection curve and
the placed revolution (whose affine placement may scale it unevenly) are
refused by prepare as not supported yet, until the kernel adapter handles
them. The records' positions move with the body's centre like the others.

* geom/hlr: pad plane faces' boxes, so a plane record off by rounding still hides

Plane faces took the box of their boundary chords unpadded, flat along the
normal. When the plane record and its edges disagree by rounding (a unit cube's
top plane one ulp above its edges, a 100 mm cube's 1e-11), the ray toward the
viewer reached the plane just outside that box, the slab test culled the hit,
and the back edges came out Visible with no doubt. Every face's box is now
padded by 1e-6 of its size, as curved faces' boxes already were. Reported by
DevonHope on #2457.

* geom/hlr: limits from review: trim samples, scene extent, crossing pairs, surface groups

From DevonHope's review of #2457:
- Options::trim_samples must be 1 to MAX_TRIM_SAMPLES (4096).
- Placed bodies must stay within MAX_COORDINATE (1e12 mm, as section and brep
  bound stored numbers). Placements 1e200 apart made the scene's size infinite
  and every tolerance taken from it with it, so everything came out Visible.
- The crossing search counts its candidate pairs before it starts and meets
  each pair only in the first grid cell the two segments share, instead of
  keeping a set of the pairs met, which could outgrow wasm's 4 GB before the
  500 million pair budget tripped. The same pairs in the same order.
- prepare groups faces on one quadric or torus by comparing each face with
  the faces of its kind whose size agrees to the tolerance (sorted), not with
  every face; at most 50 million comparisons. prepare_cancellable polls a
  cancel callback between edges, faces and comparisons.

* geom/hlr: boundary chords on a grid, and no fine pass over coincident curved chords

The cost items of DevonHope's review of #2457:
- Every ray hit asked how far it lies from its face's boundary by scanning all
  of the face's chords (4096 per circle; a plate's face with 256 holes has
  about a million). The chords are now bucketed on a uniform grid over the
  boundary's box, cells at least as long as the longest chord, and the test
  looks only at the cells around the point. It finds exactly the chords the
  scan finds.
- Chords of two curved curves that lie on each other end to end (aligned holes
  seen along their axes) were cut into 16 by 16 pieces and intersected, to
  find crossings that are only noise within the curves' tolerance. They are
  now skipped; visibility changes where a coincident run starts or ends, and
  chords that only partly overlap still find those.
- Fixtures: a plate with holes, coaxial cylinders seen along their axis, and
  300 placements.

* geom/hlr: pad boxes by the body's coordinates too; coincident chords only to rounding

Review of the previous commits found two holes:
- The padding grew with the face's size, but rounding grows with where the body
  was modelled: a 0.001 mm cube 1e7 mm out, its top plane one step (1.9e-9 mm)
  above its edges, still had its hit culled. The pad now adds 1e-12 of the
  body's offset.
- Two chords were taken for coincident when their ends lay within each other's
  sag, which in a coarse scene can hide real crossings: an arc can bulge across
  a line 1 mm off its chord twice. Only chords that coincide to rounding are
  skipped now; identical projections, the costly case, still are.

* geom/hlr: coincident curves meeting at shared sample points do not cross

Timing the review's scenes showed that skipping coincident chords was not
enough: chords of two coincident curves also touch end to end at every sample
point they share, and each touch was a crossing to refine and split at. 64
coaxial cylinders seen along their axis gave 8.3 million crossings (19 s), 64
cylinders one behind another along the line of sight 4.2 million (214 s;
before these commits the crossing budget refused it). A touch at a shared
sample point is now dropped when the two curves also coincide, to rounding, on
both sides of it. Where a coincident run starts or ends one side differs, and
the touch is kept.

* geom/hlr: the own-face margin allows for rounding where a body was modelled

The far 0.001 mm cube of the previous commit showed a second effect of records
rounded at large coordinates: its top plane, one ulp (1.9e-9 mm) above its
edges, hid the top edges themselves, because a ray leaving an edge met its own
face 3.2e-9 mm along, past the own-face margin of 1e-6 of the face (1.4e-9).
The margin now also covers 1e-14 of the body's offset (about 45 ulps of its
coordinates), which only matters for parts far smaller than their distance
from the origin.

* geom/hlr: no parity test for a hit on the boundary, where it is uncertain anyway

* docs/drawing-hlr: the fixtures of the review's second round

---------

Co-authored-by: MateuszSychXpl <151755914+MateuszSychXpl@users.noreply.github.com>
2026-10-27 00:20:17 -04:00
CamWight 3575c99750 Drawing W18 S2: title blocks as data (#3875) (#3877)
* Title blocks: what a cell shows and where its text goes (#3875)

geom::cell holds a title-block cell's content: parts of text and fields
(component, drawing, sheet, revision or prompt, by key, with an optional
unit and decimals kept as written for the resolver to check), at most 64
parts and 4096 bytes of text, with no control character but a line break.
Beside it are a resolved value (live, overridden and drifted, or missing,
with "-" for each part that couldn't be read and why), an override with
the value it replaced, and the projection a drawing's views use (first or
third angle), which the title block's symbol will read.

A fit warns and never cuts: a count of characters, not bytes, with its own
count for text in Chinese, Japanese, Korean or Yi characters.

The cell text rule places a label and a value: the label's baseline 1 mm
plus its cap height below the cell's top, the value box under it inset by
1 mm, one line centred in the box, and wrapped or several lines down from
its top at the font's pitch, with a warning when the last falls below the
box. Wrapping by width, width warnings and glyphs a font lacks need the
text measured, so they wait for the paper scene's text.

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

* Title blocks: definitions and their place on a sheet (#3875)

geom::title_block holds a title block's definition: its size (1 to 1000
mm a side), its outline and cell line widths, and up to 64 cells. Each cell
has a snake-case name of its own, a rectangle measured from the block's
lower-right corner, an optional one-line label with its cap height, and a
value: text from cell content, with its cap height, alignment and fit, or
the projection symbol, which reads the drawing's projection and is never
fixed in the block.

validate() checks a definition on its own, as when one is read: cells
inside the block and not overlapping (touching is fine), label and value
cap heights that fit, and the content's own limits. layout() puts the
block on the drawing border's lower-right corner of a sheet, refusing one
that doesn't fit, and gives each cell's rectangle, its label's place and
its value box by the cell text rule. On A3 landscape a 180 x 54 mm block
runs from (230, 10) to (410, 64); it fits A5 landscape and every larger
sheet, but not A5 upright.

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

* Cell text: a value box lies within the paper's coordinates (#3875)

place_lines took any finite, ordered value box, but centring one far out (min x 1e308, max x
1.1e308) overflows: the anchor came back infinite, with no warning. 0xlillith found it in review
of #3877.

The paper scene already bounds every coordinate it holds by MAX_COORDINATE_MM (twenty times
the longest sheet side). That limit is now public, and place_lines keeps its box inside it, so
its anchor is always finite: one rule for what a paper coordinate may be.

Tests: the hostile-box test refuses both overflow cases from the review and a box just past the
limit on each side, and places lines in a box the size of the whole paper limit. The fuzz test's
numbers now include values past the limit (1.1e5, ±1e308).

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

* Title blocks: padding from the lines' inner edges, one label rule, nothing unread (#3875)

A cell's text is padded from the inner edge of its lines: each side of a cell moves in by half
the line drawn on it (the outline's on the block's edges, a cell line's inside), so a wide
outline never covers the text. Before, the padding ran from the line's centre.

The label's one-line rule lives with the cell text rules (`cell::check_label`), where parts
lists will find it. The cell types nothing read yet (`Override`, `Resolved`, `State`,
`Problem`, `Reason` and `MISSING`) wait for the resolver that will read them.

The tests check the exact thresholds inside the lines (a label that leaves no room, a value
that just fits), pick each of the lines, alignment and wrapping on their own so every
combination comes up, and lay fuzzed blocks out on every standard sheet, either way, with and
without the filing margin.

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

* Title blocks: the drawing border's line, descenders, room where the block sits (#3875)

From DevonHope's review of #3877:
- Each cell's lines are clamped to the inner edge of the drawing border's line. That line runs
  along the block's right and bottom edges, and along any other edge it reaches, as when the
  block fills the border. Aabb2::intersection, the 2D twin of Aabb3's, does the clamping.
- layout checks each cell's room again where the block sits (place_cells, which validate runs
  at the origin), so rounding at the real corner can't turn a cell inside out.
- The cell text rule counts descenders, at 3/10 of the cap height (ISO 3098's lettering B).
  They may reach into the padding under a value box, never past it to a line. place_lines
  warns Height when they would, and validate asks it whether one line fits.
- The module doc no longer links to a private function.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 00:18:05 -04:00
CamWight de7bf4fc4d Drawing W17a: view definitions as data (#4816) (#4817)
* claim: #4816

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

* Drawing views as data: named views, scales and the map onto paper (#4816, W17a P1)

What a drawing view is, before anything draws it (geom::drawing_view):
- NamedView: the six principal views and the isometric views from the four upper corners, each
  an exact basis (a Plane whose normal points back at the viewer), never taken from a camera,
  whose pitch clamp would tilt a top view, with no -0 to write out; `iso` reads as the
  front-right one.
- basis_looking: any other view from its direction, an up and an origin (paper x = direction x up,
  paper y = x x direction), refusing a zero direction, an up along it, or an origin that isn't
  finite or within 1e12 mm. W07's
  {direction, up, origin} view reads through it.
- Scale: paper : model as two positive numbers, read and written as "1:2", from 1:100000 to
  1000:1; Scale::fit picks the largest ISO 5455 preset (50:1 to 1:10000) at which a view fits a
  space (W07's "fits the page" default), compared as size x paper <= room x model so an exact
  fit at a reduction isn't lost to rounding.
- ViewFrame: a view's basis, scale and place on paper, the point of its plane at `centre`
  landing at `position_mm`. Its paper map moves the view's curves and their accuracy onto
  paper (paper error = scale x model error); `boundary` maps a box of its plane onto paper from
  its four corners.

In geom, where Seg2 lives:
- Similarity2 and Seg2::similar (new): scale, turn and shift a segment exactly, an arc staying an
  arc. Similarity2::new refuses a scale that isn't above 0 and works the turn out once, not per
  point;
- Seg2::map_controls: a segment's control points mapped by any function. It was doc's
  sheet::map_controls; its four callers in doc use the method now, unchanged.

CurveAccuracy::scaled gives a curve's accuracy at another scale.

Tests: each named view's basis and viewer side, and the same bases from a direction and up; a 50 mm cube in every principal view at 1:1 and
1:2, and in isometric (edges 50 x sqrt(2/3), outline 50 x sqrt(2)); the view's centre on its
position (W07's 100 x 20 front view on A3 at 2:1); scales read, written and refused; the fitted
scale, an exact fit at 1:5 and ISO's smallest; no -0 in any named view's axes; an origin refused by
its own message; hostile bases, centres and positions; every segment kind moved by a similarity,
and the similarities refused; scaled accuracy.

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

* Drawing views: the bottom view pinned, square near parallel, bounded scales and boxes (#4816)

From DevonHope's review of #4817:
- The tests pin all six principal views exactly. The direction-and-up test uses literal ups,
  so a bottom view turned half round now fails it.
- basis_looking squares x to the direction before normalising. Near the 1e-9 threshold the
  normal was up to 8.8e-9 rad off; now it is about 1e-16.
- Scale::fit fits nothing for a size or room that is negative or not finite. A size of 0 still
  sets no limit that way.
- Each side of a scale is 1e-6 to 1e6, so a scale writes at most 49 characters.
- ViewFrame::boundary returns a Result. It refuses a box that is inside out or not finite on
  paper, instead of dropping a corner.
- paper_map, boundary and seg_to_paper say which coordinates they take.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 00:18:01 -04:00
CamWight f18616e19c Paper scene: SVG of a sheet, its lines, arcs, cubics and fills (#4890 A3a) (#7032)
The first export of a paper scene, as SVG first (jyviko and 0xlillith on #4890), from the research
in the plan's §16. A3b adds ellipses, hyperbolas and Béziers above degree 3, which this refuses as
"not supported yet".

- `geom::paper::Prepared`: writers read a scene through `Scene::prepare`, which checks it first, so
  none reads one unchecked. Clipping goes there too, once (A8).
- `geom::paper::arc_pieces`: an arc's ends in pieces of at most a quarter turn, the last its own end.
  A half turn drawn from its ends is ill-conditioned: rounded to the nanometre (six decimals of a millimetre) its
  centre moves up to 1e-2 mm, where a quarter turn's stays under 1e-6 mm.
- `io::paper::paper_svg`: one user unit a millimetre, the size in mm on the root; y flipped by
  writing the height less y, so the file's numbers are page coordinates and an anticlockwise arc
  has sweep flag 0; lines `L`, arcs `A` (never large-arc), cubics `C`, parabolas `Q`; fills
  black and even-odd; one path per item; groups as `data-object` and `data-kind` (object ids can't
  be XML ids). No background, so cutters see only the marks.
- Numbers are whole nanometres (six decimals of a millimetre): no exponent, no "-0", the same bytes every time. They are the
  writer's own state too, so a joint is written once and a closed path ends exactly on its first
  point. Numbers are written straight onto the document, with no text made per number. A width
  under half a nanometre is written as one, and a piece too short for the grid is merged into the
  next, both counted in the report; a path's last piece has no next, so it is kept however short
  (unless it has no length). Output over 64 MiB is refused.

Tests: the A3 drawing border's whole document; quarter, 270°, clockwise and whole-turn arcs, and a
circle of two halves giving the same four quarters; the quarter-turn boundary both sides; cubics and
parabolas, and the conics refused; the number format; a closed path whose start and end round
apart; a sliver merged; an open path's short last line and arc kept, and one of no length dropped; read back through our own SVG reader (lines exactly, the arc as an arc);
the scene's JSON mutated into the writer (never a panic).

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 16:54:42 -04:00
CamWight 657c733166 Drawing files: a drawing saved as a container or as plain JSON, never read as a design (#3890) (#7098)
* File format families: drawings share the design format's versioning (#3890)

The format code works for a family of files: its own tag, its own numbers and
its own folder of change files, with one reading of a file's format, one
refusal of a file a newer build saved and one upgrade for every family. Designs
(solidcraft/N) are one family and keep everything they had: FORMAT and FORMATS
are as they were, and pending_names, format_string, version_of and read mean
what they did. Drawings (solidcraft-drawing/N) are the other, with format 1 as
their first record and each later change one file in
crates/doc/src/drawing/format/pending/, as design changes are. Nothing saved
changes.

A design tag never reads as a drawing's or the other way round, and a file that
names no format is a design from before format strings, never a drawing. Every
refusal names its family; a design's read word for word as before. A design
listing a pending change this build doesn't have is now refused as a newer
build's before its content is checked, as a newer number already was, so a
newer build's fields never read as damage. The list reader is
take_names(v, key, noun), the shape #4811 uses for its own list.

For the maintainer, the release step changes: scripts/fold-format-pending.sh
now folds every family that crates/doc/build.rs lists (FAMILIES), each into
its own next number: designs into crates/doc/src/format/v<N>/ as before,
drawings into crates/doc/src/drawing/format/v<N>/ from 2. With nothing pending
in a family it says so and moves nothing. Its test folds a drawing change into
v2 beside the design changes. docs/releasing.md, CONTRIBUTING.md and AGENTS.md say so.

build.rs lists each family's changes from its own folder, each folder's
versions contiguous from its own base.

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

* Drawing record: format 1 of the drawing family (#3890)

A drawing is now a record of its own (crates/doc/src/drawing.rs): its name,
the designs it draws (sources: an id and the design file's path, relative to
the drawing's folder as a linked design's is), its properties under the design
metadata's rules, its units (each design's own, or one length unit of the
expression unit table), its content language (a BCP 47 tag) and its sheets.
A sheet holds its paper, a copy of its drawing border's profile, the
resolution its pictures are rendered at (300 dpi unless it says, 25 to 2400),
the component it documents and whether it is left out of the sheet count or
of printing. This is format 1 of the drawing family; each later kind of
record adds one file in crates/doc/src/drawing/format/pending/.

The rules: one id counter for every object, raised above the largest id when
a file is read and never rewound; every reference is an id that must resolve,
or the file is damaged and refused, never patched; 1 to 1,000 sheets, at most
64 sources, names of 1 to 256 bytes, paths of 1 to 4,096 bytes without NUL.
The same drawing always saves as the same bytes.

Drawing::from_json reads at most 16 MiB of text, then runs the family's format
check, the number check, the upgrade, the typed record (unknown fields refused
everywhere) and Drawing::validate. The number check is now one function every
family calls, Family::check_numbers, told which field holds other tools' data;
a design's refusals read as before.

Nothing saves or opens a drawing yet: the container is the next step.

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

* Drawing ids: random and collision-free from format 1 (#3890)

A drawing's objects now take random ids, the form jyviko chose on #3890 from
#1017: no counter, so objects added on two branches never share an id and
never edit one line. The allocator is one module for drawings now and designs
later (#626 Phase 3), crates/doc/src/ids.rs: an IdSource gives nonzero u64s by
splitmix64 from a seed, each checked against the ids in use and drawn again
when it is one, or when it is 0. A run never draws a number twice, so at most
one draw per id in use comes before a free one, and a seeded run over a file
the same seed made still gets a new id. IdSource::seeded gives the same ids
for tests and replays; IdSource::from_entropy seeds a session from the
standard library's hash keys, the time and a count, with no new dependency.

In a file an id is 13 characters of Crockford base32, lowercase
(0000000000001 to fzzzzzzzzzzzz): ids::encode writes it and ids::decode reads
only that spelling, case-sensitive, so an id has one form to search for.
Anything else, 0 included, makes the drawing damaged. The drawing loses
next_id (a file that holds one is refused), and Drawing::new and allocate_id
take the IdSource. Messages name ids as the file writes them. The component a
sheet documents stays its design's own number until designs move over.

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

* Drawing record: a drawing too large to reopen isn't saved (#3890)

from_json reads at most MAX_DRAWING_BYTES (16 MiB), but to_json had no limit. Other tools'
properties are kept as they came, and indenting them can grow a drawing read in at about 4 MB
past 16 MiB (two million zeros in one array), so the next read refused what the save had just
written. 0xlillith found it in review of #3891.

to_json now returns a Result and refuses JSON longer than a read takes ("drawing too large to
save (at most 16 MiB)"), so every drawing saved opens again. Nothing writes drawing files yet, so
the error reaches the future file writer before it publishes anything.

Test: that case is refused, and a smaller compact drawing saves and reopens unchanged.

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

* Drawing files: a drawing saved as a container or as plain JSON, never read as a design (#3890)

K3 of the drawing storage plan (W11.2): the file a drawing is saved in. Nothing writes one from a
command yet; opening and saving come with K5.

- A drawing container is a design container holding `drawing.json` (Deflate, as `document.json`)
  in place of `document.json`, and `container.json` says so:
  {"content": "drawing", "format": "solidcraft-container/1"}. A design's `container.json` is as it
  was, with no `content`. The plain JSON form is the drawing's own text. Both give the same bytes
  every save, so a drawing can live in version control as text.
- `io::file_kind` tells a design from a drawing by `container.json` or by the JSON's format. The
  design reader refuses a drawing ("this file is a SolidCraft drawing, not a design: open it as a
  drawing") and the drawing reader a design; plain JSON only asks when it isn't a design, so
  reading a design costs nothing more. A container's content this build doesn't know, or a drawing
  container that also holds a design's `document.json`, is refused.
- `read_drawing` (bytes, either form), `read_drawing_file` (one seekable handle, the design
  reader's checks) and `write_drawing_container`; `DRAWING_EXT` (`scdwg`) and
  `is_drawing_path` beside the design's.
- The container writer's set-up and finish, the file opening and the container sniffing move into
  one function each, which designs and drawings both use. The container's own checks (budgets,
  entries, the ZIP directory) are the design container's, word for word.

Tests: a drawing round-trips through both forms byte for byte, and the container holds exactly
`container.json` and `drawing.json`; `file_kind` on both forms of both kinds; each reader refuses
the other's file; unknown content, a stray `document.json`, a missing drawing, an unknown
container version and a damaged drawing refused; both forms from disk, and a directory refused;
mutation fuzz of both forms (an error or a drawing, never a panic).

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 16:18:29 -04:00
CamWight 4c4f25115b Bill of materials types: phantom, reference, purchased and inseparable (#3874) (#5107)
* Bill of materials types: phantom, reference, purchased and inseparable (#3874)

A component's bill of materials type is a part property (`bom_type` in its metadata, set with
component.metadata.set and read by component.metadata, as part_number is), so older releases
keep it and no format change is needed. It isn't a design property: the Design Properties
dialog doesn't list it and document.metadata.set refuses it. A word this build doesn't know
reads as normal and is kept.

assembly.bom counts by type: a phantom has no row and what it holds counts where it is placed,
in the same rows as the same parts there; a reference counts nowhere, and nothing inside it
does; purchased and inseparable components are one row, and in parts only an inseparable
component's purchased parts are listed too. Rows of one part number but different types are
different rows. Rows and children carry bom_type. With no types set the rows are as before.

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

* Glossary: bill of materials and bill of materials type (#3874)

The bill of materials types are a new idea, so the glossary gets their
term, with the five types, and "normal" gets its fourth meaning in the
table of words with more than one meaning.

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

* Bill of materials types: a plain line for each (#3874)

The guide's table and the glossary entry say what each type is for in
plain words: something you make, something just to group, something to
use as a reference, something you buy, something made as one piece.
The names stay the five standard ones.

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

* Bill of materials: kits fold together only when their contents' types match (#3874)

With group_children, two kits with one part number share a row when what they hold reads the
same. That reading (kit_key) compared each child by part number and count, but not by its bill
of materials type, though rows themselves are told apart by type. So a kit holding a made bolt
and a kit holding a bought one, both KIT, folded into one KIT x2 row listing the first kit's
bolt twice: the other type was lost, and which one depended on creation order. 0xlillith found
it in review of #5107.

kit_key now includes each child's type. Equal part number, type and contents still fold.

Test: two KIT kits each holding a BOLT fold into one row of two; once one bolt is purchased they
are two rows, each listing its own bolt. Planted fault (the type left out of the key): caught.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 11:05:56 -04:00
CamWight 672ef13eb8 Drawing sheets: no centring or trimming mark longer than the largest sheet's side (#3875) (#6410)
A border profile capped every length at 5000 mm (the largest sheet's side) but two: a
centring mark's reach inside the drawing border and a trimming mark's length. A file could
give either a huge whole number, such as 18446744073709551615. The read's number check passes
whole numbers, so the profile took it as 1.8e19 mm, and saving wrote it as a decimal the next
read refused: a drawing that saved but didn't open again. K2's fuzz test of drawing files
(#3890) found it.

Both now take the cap the margins have, `MAX_SIDE_MM`, in `validate`, so the file is refused
when it's read. Layout's own checks against the sheet are unchanged, so a 5000 mm mark still
validates and is refused only on a sheet too small for it.

Tests: the hostile-profile test refuses 5000.000001 mm and 1.8e19 mm for each mark, and takes
5000 mm on its own; 4 planted faults (each cap removed or made one short) are all caught.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 19:59:28 -04:00
CamWight 4c63a88fc3 Drawing sheets: centring marks across the whole margin (#3875) (#5554)
The built-in sheet's centring marks now run from the trimmed sheet's edge to the drawing border.
From the grid reference border, as before, a mark was the same length as a grid field mark, and on
A3 it sat on one (the sheet's centre falls on a field edge), so it no longer read as a mark once the
grid reference border's outline is left out (Cameron, after the prototype on #3875).

- DrawingBorderProfile.centring_mark_start: sheet_edge (the built-in's, and when not given) or
  grid_border (ISO 5457's figure 5); ISO's full border in the tests asks for grid_border.
- A mark still needs a length: one that stops on the drawing border needs room outside it on the
  side it starts from, every margin for sheet_edge, the grid reference border for grid_border.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 11:48:17 -04:00
CamWight 5e23f58f62 Paper scene: plain records of what is drawn on a sheet (#4890 A1) (#5513)
geom::paper holds what a sheet shows as plain data: groups of strokes and fills, each group tagged
with the object that drew it (an opaque id) and its kind, with one validation pass over a bounded
record count. Seg2's checks and the coordinate rule move into profile.rs so section and paper share
them, and drawing_sheet takes its side and line limits from paper, so each limit is written once.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 10:23:18 -04:00
Benny1496 01a542d6e8 Drawing W23a workshop plate oracle fixtures (#4950) (#4962)
* test(oracle): reviewer-aligned parameterized workshop plate fixtures (#4950)

Independent analytic W23 expected values and seven verified models,
including four R10 corners and the 2 mm tangent top chamfer.
Document accepted Drawing states and commit regenerated docs/oracle.md
from successful workflow run #29 (90/90 pass).

* fix(oracle): correct W23 chamfer centroid and clarify drawing session ownership
2026-10-23 07:35:28 -04:00
CamWight f0cc73eb83 Drawing sheets: sizes, the ISO 5457 border and its marks (#3875) (#5108)
* Drawing sheets: sizes and the ISO 5457 border (#3875)

A sheet is a paper size and an orientation: ISO A0 to A5, Letter, Legal,
ANSI A to F, architectural A to E1, or a custom size (finite, above 0 and
at most 5,000 mm a side), landscape by default and either way for every
size.

A border profile is the rule for its border, and layout() works it out
for a sheet: the frame inside the margins, and the reference fields with
their labels. The ISO profile has a 20 mm filing margin on the left and
10 mm elsewhere, a 0.7 mm frame, fields of 50 mm out from the centre
lines with the corner fields taking what's left, rows lettered from the
top without I and O (AA, AB and on past Z), columns numbered from the
left, and an A4 sheet labelled on its top and right only. A profile can
divide the frame equally instead, or have no grid.

The records sit in geom, next to the section records, so saving,
rendering and export can all use them. Nothing is saved or drawn yet:
there is no command and no format change. Margins that leave no drawing
space, values that aren't finite, fields outside 1 to 1000 mm, more than
200 equal fields and unknown JSON fields are refused.

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

* Drawing sheets: ISO 5457's centring and trimming marks (#3875)

A drawing border profile now also holds the grid reference border and the
centring and trimming marks, either of which can be left out. The ISO
profile follows ISO 5457:1999 (read in its identical adoption,
IS 10711:2001):
- the grid reference border 5 mm outside the drawing border on every
  side, with the field labels between the two;
- four centring marks on the sheet's centre lines, from the grid
  reference border to 10 mm inside the drawing border;
- at each corner a trimming mark of two overlapping 10 x 5 mm
  rectangles, in the margin outside the grid reference border.

The labelled sides are now a rule worked out for each sheet: ISO 5457's
(the top and right of A4, all four sides of the others) or fixed sides.
So one profile labels every size correctly. validate() checks what
doesn't depend on the sheet, for when a profile is read; layout() calls
it, then checks the rest against the sheet.

Refused:
- a grid reference border that isn't finite, is negative or is wider
  than a margin;
- a centring mark that isn't above 0, would reach past the sheet's
  centre lines, or is on a drawing border those lines miss;
- a trimming mark that isn't above 0, is wider than it is long, is
  wider than the margin left outside the grid reference border, or is
  longer than half the sheet's shorter side.

The grid rule's JSON is now externally tagged ("none",
{"from_centre": {"field_mm": 50}}), as the sheet size is, so an unknown
field is refused in every form; the internally tagged "none" ignored
one. Nothing is saved yet, so the shape change costs nothing.

The names follow the glossary, and ISO 5457's "drawing sheets": the
module is drawing_sheet (was sheet, which means sheet metal elsewhere
here), DrawingBorderProfile and SheetLayout (were BorderProfile and
BorderLayout), sheet_border and drawing_border (were paper and frame),
and Sheet::size_mm (was paper_mm). The first part of S1 is still a draft,
so the renames are made here rather than in a commit of their own.

The extra centring marks ISO 5457 asks for on sheets larger than A0
aren't here yet. Still nothing is saved or drawn.

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

* Drawing sheets: a lighter built-in border, the filing margin a choice (#3875)

Cameron's decisions after seeing the sheet in the app, 2026-10-23:
- No trimming marks in the built-in profile: the filled corner blocks
  take a lot of ink. trimming_mark_mm stays, so a profile can still ask
  for them.
- Centring marks stop at the drawing border: from the grid reference
  border to the drawing border, never into the drawing area (on A4 they
  crossed the tables). centring_mark_mm keeps its meaning, how far a
  mark reaches inside the drawing border, and now takes 0, the built-in
  value, as grid_border_mm already takes 0 for "on the drawing border".
  A mark of no length (a reach of 0 with no grid reference border) is
  refused.
- Margins of 10 mm all round. ISO 5457's 20 mm filing margin on the
  left, for drawings that get bound, is a named choice: filing_margin,
  off when not given. On, it stands in for margins.left_mm, which is
  kept, and still checked, for when it's off again. The 20 mm is
  FILING_MARGIN_MM, and validate() and layout() both take the margins
  from one helper, laid_out_margins().

A3 landscape is now 400 x 277 at (10, 10), with eight 50 mm columns.
With the filing margin it is 390 x 277 at (20, 10) again, the same as a
20 mm left margin set by hand. A profile with all three choices on
(ISO 5457's full border) lays out exactly as the built-in profile did
before, and the tests keep the old hand values for it.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 03:01:08 -04:00
1500 changed files with 101869 additions and 28381 deletions
+20
View File
@@ -0,0 +1,20 @@
# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
reviews:
auto_review:
# Dependabot bumps are checked by CI; skip CodeRabbit reviews on them.
ignore_usernames:
- "dependabot[bot]"
path_instructions:
- path: "**"
instructions: |
Project rule: don't change anyone's existing defaults unless the change is purely additive and belongs in the application.
Flag any change to something a user already relies on: a default setting or preference value, what a command or tool does when used the usual way, the UI's default layout or behaviour, a keyboard or mouse shortcut, or the result an existing saved design rebuilds to.
Don't flag purely additive changes: a new command, a new setting whose default keeps today's behaviour, or a new option that appears only when someone chooses it. Don't flag bug fixes that only turn a crash or an error into the result the feature always promised.
When you flag one, start the comment with "Changes a default:", say what a user sees before and after, whether a setting keeps the old behaviour, and whether the PR description's `Changes a default:` line says so.
pre_merge_checks:
custom_checks:
- name: "Existing defaults unchanged"
mode: "warning"
instructions: |
Pass if the pull request changes no default a user already relies on (default setting or preference values, what a command or tool does when used the usual way, the UI's default layout or behaviour, shortcuts, or the result an existing saved design rebuilds to), or if its changes are purely additive.
Fail otherwise, naming each changed default and what a user sees before and after. Also fail if the description says `Changes a default: no` while the code changes one.
+2
View File
@@ -21,6 +21,8 @@ concurrency:
jobs:
publish:
name: Request the combined docs, web, and Flatpak deployment
# A pre-release stays off the Flatpak feed: the feed's one branch is what `flatpak update` offers.
if: github.event_name == 'workflow_dispatch' || !github.event.release.prerelease
runs-on: ubuntu-24.04
env:
GH_TOKEN: ${{ github.token }}
+6 -2
View File
@@ -1,5 +1,6 @@
# The documentation site on GitHub Pages: the book (docs/book) at https://docs.solidcraft.io/
# and the bundled web app at /app/. Needs Settings → Pages → Source = GitHub Actions; skipped while the
# and the web app at /app/ (the latest release), /app-rc/ (the newest release candidate) and
# /app-edge/ (this commit). Needs Settings → Pages → Source = GitHub Actions; skipped while the
# repository is private.
name: Pages
@@ -66,7 +67,10 @@ jobs:
mkdir -p site
cp -r target/book/. site/
bash docs/book/seo.sh site # canonical links, robots.txt, sitemap.xml
cp -r dist/web site/app
- name: Add the web app channels (stable and release candidate from their releases, edge from this build)
env:
GH_TOKEN: ${{ github.token }}
run: bash packaging/web-channels.sh site dist/web
- uses: actions/configure-pages@v5
id: pages
- name: Install Flatpak repository tools
+1
View File
@@ -11,6 +11,7 @@ Work: ROADMAP.md, "Where help is wanted most" in CONTRIBUTING.md, issues labelle
- Every asset is original or openly licensed with a row in ATTRIBUTION.md; read `assets/AGENTS.md` before icon work: `cargo xtask assets`, `cargo xtask icons`.
- Never crash: no unwrap/expect/panic/unsafe in non-test code (unsafe allowlist in crash-safety), errors are `Result`, input is hostile: clippy `-D warnings`, `hostile_params_never_panic`.
- Model decisions (what an operation does, which bodies it touches, which face or edge a point or name means, depths, nearness) use exact geometry; a mesh is for display, export, previews and mesh bodies (`xtask/mesh-decisions.txt` only shrinks): `cargo xtask mesh-decisions`.
- New kernel cases go to solidcore: `cargo xtask lists`.
- Everything is a command and programmatic calls never open dialogs: `skills/solidcraft-commands/SKILL.md`.
- Layering (geom, sketch/kernel/render, doc, io, engine, ui-egui, apps); only `crates/kernel` uses truck: `cargo xtask layers`.
- One outer UI container, one camera path (`navigation::handle`), colours from theme tokens: `cargo xtask ui-containers`.
+15
View File
@@ -245,6 +245,18 @@ Editable drawing code, native projects and shipped exports are collected under `
| assets/icons/ui/design/svg/flat/plus.svg | SolidCraft contributors | single-colour application/navigation icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/pan.svg | SolidCraft contributors | single-colour application/navigation icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/perspective.svg | SolidCraft contributors | single-colour application/navigation icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/prefs_appearance.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/prefs_assistant.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/prefs_design.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/prefs_general.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/prefs_graphics.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/prefs_layout.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/prefs_mouse3d.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/prefs_navigation.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/prefs_plugins.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/prefs_shortcuts.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/prefs_sketch.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/prefs_tools.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/redo.svg | SolidCraft contributors | single-colour application/navigation icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/save.svg | SolidCraft contributors | single-colour application/navigation icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/settings.svg | SolidCraft contributors | single-colour application/navigation icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
@@ -772,6 +784,9 @@ Original SolidCraft drawings, Open for owner review; no runtime assignments.
| `assets/locales/pending/sketch-offset-instances.json` | SolidCraft contributors | Original offset instance-count label and translations | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/settings_profile.svg | SolidCraft contributors | single-colour application/navigation icon master (64 grid), original analytic head and shoulder geometry for settings profiles | MIT OR Apache-2.0 |
| tests/kernel-cases/import/4561-convert-box-mesh-with-a-zero-area-triangle/box-degenerate.stl | SolidCraft contributors | synthetic ASCII STL written by hand: a box with one zero-area triangle, for the kernel regression case of tangency-labs/solidcraft#4561 | MIT OR Apache-2.0 |
| assets/locales/pending/theme-blur-ui.json | SolidCraft contributors | Original blur controls, notices and translations | MIT OR Apache-2.0 |
| crates/ui-egui/tests/fixtures/theme-blur.json | SolidCraft contributors | Original theme for blur option scenarios and manager review | MIT OR Apache-2.0 |
| assets/locales/ka.json | David Machakhelidze (devius) | Original Georgian translation of SolidCraft's English catalog, written with Claude Code from the meaning of each English string; terminology and doubtful strings decided by a native Georgian speaker; no commercial CAD text used | MIT OR Apache-2.0 |
## Code adapted from sibling projects
+12
View File
@@ -163,6 +163,18 @@ the most complete description of the house style.
The reference for what the kernel builds is the kernel-cases ladder (`tests/kernel-cases/ladder/`,
`cargo xtask kernel-cases --ladder`, [docs/kernel-ladder.md](docs/kernel-ladder.md)).
**Kernel cases go to solidcore.** A case that checks a kernel operation (a boolean, a blend, a
shell, a sweep, an import's geometry) is a new entry in solidcore's corpus,
`crates/solidcore/tests/kernel_cases/<area>/` in
[tangency-labs/solidcore](https://github.com/tangency-labs/solidcore), with an answer in closed
form or exact. Draft it as a rig case here, export it with
`cargo xtask kernel-cases --area <area> --export DIR`, add `DIR/<area>/<case>.json` and its
`.bodies.json.gz` there, and delete the draft; or write the entry by hand as that folder's README
describes. `tests/kernel-cases/` takes no new cases: its `CASES` list only shrinks
(`cargo xtask lists`). App-level cases (what a command does, the UI, product rules) stay in
SolidCraft, as tests in their area's existing test file or UI scenarios.
The naming corpus checks persistent names: every name a corpus design stores still finds a face or
an edge, and two builds name its faces and edges alike
(`cargo test --profile ci -p solidcraft-cli --test naming_corpus`).
Generated
+49 -21
View File
@@ -4059,9 +4059,19 @@ dependencies = [
"thiserror 2.0.21",
]
[[package]]
name = "solidcore-record"
version = "0.1.0"
source = "git+https://github.com/tangency-labs/solidcore?rev=f5e8e9a29e0c9b7cf6c4040a5bc8878382b3c62f#f5e8e9a29e0c9b7cf6c4040a5bc8878382b3c62f"
dependencies = [
"serde",
"serde_json",
"thiserror 2.0.21",
]
[[package]]
name = "solidcraft"
version = "0.40.0-rc.1"
version = "0.40.0-rc.2"
dependencies = [
"block2 0.6.2",
"eframe",
@@ -4092,21 +4102,23 @@ dependencies = [
"ureq",
"windows-sys 0.61.2",
"winit",
"winreg",
"x11rb",
"zbus 5.19.0",
]
[[package]]
name = "solidcraft-3dx-client"
version = "0.40.0-rc.1"
version = "0.40.0-rc.2"
[[package]]
name = "solidcraft-cli"
version = "0.40.0-rc.1"
version = "0.40.0-rc.2"
dependencies = [
"image",
"log",
"serde_json",
"solidcore-record",
"solidcraft-engine",
"solidcraft-gpu",
"solidcraft-kernel",
@@ -4115,7 +4127,7 @@ dependencies = [
[[package]]
name = "solidcraft-derive"
version = "0.40.0-rc.1"
version = "0.40.0-rc.2"
dependencies = [
"proc-macro2",
"quote",
@@ -4124,12 +4136,13 @@ dependencies = [
[[package]]
name = "solidcraft-doc"
version = "0.40.0-rc.1"
version = "0.40.0-rc.2"
dependencies = [
"log",
"serde",
"serde_json",
"sha2 0.10.9",
"solidcore-record",
"solidcraft-derive",
"solidcraft-geom",
"solidcraft-kernel",
@@ -4142,7 +4155,7 @@ dependencies = [
[[package]]
name = "solidcraft-engine"
version = "0.40.0-rc.1"
version = "0.40.0-rc.2"
dependencies = [
"image",
"log",
@@ -4150,6 +4163,7 @@ dependencies = [
"serde",
"serde_json",
"serde_path_to_error",
"solidcore-record",
"solidcraft-doc",
"solidcraft-geom",
"solidcraft-io",
@@ -4164,16 +4178,17 @@ dependencies = [
[[package]]
name = "solidcraft-geom"
version = "0.40.0-rc.1"
version = "0.40.0-rc.2"
dependencies = [
"serde",
"serde_json",
"solidcore-record",
"thiserror 2.0.21",
]
[[package]]
name = "solidcraft-gpu"
version = "0.40.0-rc.1"
version = "0.40.0-rc.2"
dependencies = [
"image",
"log",
@@ -4182,6 +4197,7 @@ dependencies = [
"robust",
"serde",
"serde_json",
"solidcore-record",
"solidcraft-engine",
"solidcraft-geom",
"solidcraft-io",
@@ -4193,7 +4209,7 @@ dependencies = [
[[package]]
name = "solidcraft-io"
version = "0.40.0-rc.1"
version = "0.40.0-rc.2"
dependencies = [
"cadmpeg-core",
"cadmpeg-ir",
@@ -4203,29 +4219,34 @@ dependencies = [
"gethostname",
"gltf",
"log",
"png",
"quick-xml",
"rustix 1.1.5",
"serde",
"serde_json",
"sha2 0.10.9",
"solidcore-record",
"solidcraft-doc",
"solidcraft-geom",
"solidcraft-kernel",
"solidcraft-profile",
"solidcraft-sketch",
"solidcraft-testkit",
"thiserror 2.0.21",
"tiny-skia",
"zip",
]
[[package]]
name = "solidcraft-kernel"
version = "0.40.0-rc.1"
version = "0.40.0-rc.2"
dependencies = [
"log",
"proptest",
"rayon",
"serde",
"serde_json",
"solidcore-record",
"solidcraft-geom",
"solidcraft-profile",
"solidcraft-smlib",
@@ -4237,7 +4258,7 @@ dependencies = [
[[package]]
name = "solidcraft-mcp"
version = "0.40.0-rc.1"
version = "0.40.0-rc.2"
dependencies = [
"image",
"serde_json",
@@ -4247,7 +4268,7 @@ dependencies = [
[[package]]
name = "solidcraft-profile"
version = "0.40.0-rc.1"
version = "0.40.0-rc.2"
dependencies = [
"serde_json",
"web-time",
@@ -4255,7 +4276,7 @@ dependencies = [
[[package]]
name = "solidcraft-render"
version = "0.40.0-rc.1"
version = "0.40.0-rc.2"
dependencies = [
"image",
"moxcms",
@@ -4263,6 +4284,7 @@ dependencies = [
"serde",
"serde_json",
"sha2 0.10.9",
"solidcore-record",
"solidcraft-geom",
"web-time",
"weezl",
@@ -4270,11 +4292,13 @@ dependencies = [
[[package]]
name = "solidcraft-sketch"
version = "0.40.0-rc.1"
version = "0.40.0-rc.2"
dependencies = [
"harfrust",
"serde",
"serde_json",
"sha2 0.10.9",
"solidcore-record",
"solidcraft-geom",
"thiserror 2.0.21",
"ttf-parser",
@@ -4283,38 +4307,40 @@ dependencies = [
[[package]]
name = "solidcraft-smlib"
version = "0.40.0-rc.1"
version = "0.40.0-rc.2"
dependencies = [
"flate2",
"log",
"serde_json",
"solidcore",
"solidcore-record",
"solidcraft-geom",
"solidcraft-profile",
]
[[package]]
name = "solidcraft-space-mouse"
version = "0.40.0-rc.1"
version = "0.40.0-rc.2"
dependencies = [
"serde",
]
[[package]]
name = "solidcraft-step"
version = "0.40.0-rc.1"
version = "0.40.0-rc.2"
dependencies = [
"solidcore-record",
"solidcraft-geom",
"solidcraft-profile",
]
[[package]]
name = "solidcraft-testkit"
version = "0.40.0-rc.1"
version = "0.40.0-rc.2"
[[package]]
name = "solidcraft-ui-egui"
version = "0.40.0-rc.1"
version = "0.40.0-rc.2"
dependencies = [
"arboard",
"egui",
@@ -4329,6 +4355,7 @@ dependencies = [
"serde",
"serde_json",
"sha2 0.10.9",
"solidcore-record",
"solidcraft-engine",
"solidcraft-gpu",
"solidcraft-space-mouse",
@@ -4345,7 +4372,7 @@ dependencies = [
[[package]]
name = "solidcraft-web"
version = "0.40.0-rc.1"
version = "0.40.0-rc.2"
dependencies = [
"eframe",
"egui",
@@ -5752,8 +5779,9 @@ checksum = "e450f9b2ed1dff33c94c12589a87338689467b9c4f5d8a5710bd09a847d2c8a7"
[[package]]
name = "xtask"
version = "0.40.0-rc.1"
version = "0.40.0-rc.2"
dependencies = [
"flate2",
"proc-macro2",
"quick-xml",
"serde",
+4 -1
View File
@@ -5,7 +5,7 @@ members = ["crates/*", "apps/*", "xtask"]
exclude = ["vendor"]
[workspace.package]
version = "0.40.0-rc.1"
version = "0.40.0-rc.2"
edition = "2024"
license = "MIT OR Apache-2.0"
rust-version = "1.95"
@@ -36,6 +36,9 @@ solidcraft-3dx-client = { path = "crates/threedx-client" }
# local checkout, add `[patch."https://github.com/tangency-labs/solidcore"] solidcore = { path =
# "../solidcore/crates/solidcore" }` to a `.cargo/config.toml` outside the repository.
solidcore = { git = "https://github.com/tangency-labs/solidcore", rev = "8ddce901671f1cebd9214a31d21895d725e886c8" }
# Geometry as data (solidcore's layers §1, "Record"): the B-rep record, its structural validation
# and the native body file. Pinned like solidcore; for a local checkout, patch it as above.
solidcore-record = { git = "https://github.com/tangency-labs/solidcore", rev = "f5e8e9a29e0c9b7cf6c4040a5bc8878382b3c62f" }
# test tooling: a dev-dependency only (xtask/src/layers.rs)
solidcraft-testkit = { path = "crates/testkit" }
+6
View File
@@ -173,6 +173,12 @@ and the [foundation ledger](docs/drawing-foundations.md) track the bounded imple
packages. The first geometry increment defines validated section records in a plane-local
frame; the ledger separates that contract from section producers and the Drawing workspace.
Drawing UI scaffold: the native workspace now supplies the shared four-tab ribbon,
source Browser, compact dockable sheet strip and resizable Sheet Settings/options
panel. Planned tools remain unavailable; only existing source services execute.
Drawing files and paper rendering are still deferred. See the
[UI implementation roadmap](docs/drawing-workspace-ui.md).
Desktop view DXF export runs in the file-operation worker with five-stage progress and
cooperative cancellation. Curve tolerance (0.0001–1 mm, default 0.01 mm) is remembered alongside
the hidden-edge setting. Cancelled exports preserve the model and the previous destination;
+3 -1
View File
@@ -1,6 +1,6 @@
Third-party software in SolidCraft
SolidCraft (the desktop app, solidcraft-cli and the web app) is built from the 536 crates below.
SolidCraft (the desktop app, solidcraft-cli and the web app) is built from the 537 crates below.
Generated by `cargo xtask licences` from Cargo.lock and the crates' own licence files.
SolidCraft's own licence is in LICENSE-MIT and LICENSE-APACHE, and NOTICE.
@@ -379,6 +379,7 @@ smithay-client-toolkit 0.20.0 (MIT)
smithay-clipboard 0.7.3 (MIT)
smol_str 0.2.2 (MIT OR Apache-2.0)
solidcore 1.0.1 (Apache-2.0)
solidcore-record 0.1.0 (Apache-2.0)
spade 2.15.1 (MIT OR Apache-2.0)
spirv 0.4.0+sdk-1.4.341.0 (Apache-2.0)
stable_deref_trait 1.2.1 (MIT OR Apache-2.0)
@@ -16031,6 +16032,7 @@ profiling 1.0.18: MIT OR Apache-2.0 (https://github.com/aclysma/profiling)
r-efi 5.3.0: MIT OR Apache-2.0 OR LGPL-2.1-or-later (https://github.com/r-efi/r-efi)
r-efi 6.0.0: MIT OR Apache-2.0 OR LGPL-2.1-or-later (https://github.com/r-efi/r-efi)
rustls-platform-verifier-android 0.2.0: MIT OR Apache-2.0 (https://github.com/rustls/rustls-platform-verifier)
solidcore-record 0.1.0: Apache-2.0 (https://github.com/tangency-labs/solidcore)
spirv 0.4.0+sdk-1.4.341.0: Apache-2.0 (https://github.com/gfx-rs/rspirv)
================================================================================
+1
View File
@@ -28,6 +28,7 @@ raytrace-cpu = ["solidcraft-engine/raytrace-cpu"]
profile = ["solidcraft-engine/profile"]
[dependencies]
solidcore-record = { workspace = true }
solidcraft-engine = { workspace = true }
solidcraft-gpu = { workspace = true }
solidcraft-mcp = { workspace = true }
+4
View File
@@ -63,6 +63,10 @@ fn replay(report: &Value) -> Result<(), String> {
Some(d) => super::load(d)?,
None => solidcraft_engine::Session::default(),
};
// The ids the session drew from there, so the commands give the same objects the same ids.
if let Some(seed) = script["ids"].as_u64() {
s.seed_ids(seed);
}
std::panic::set_hook(Box::new(|info| {
let loc = info.location().map_or_else(|| "(unknown)".to_string(), |l| format!("{}:{}", l.file(), l.line()));
let msg = info.payload_as_str().unwrap_or("(not a text message)").to_string();
+57 -12
View File
@@ -5,6 +5,7 @@
//! are never compared with each other. See tests/kernel-cases/README.md.
use serde_json::{Value, json};
use solidcraft_engine::geom::prelude::*;
use crate::oracle::{ABS_FRAC, Checks, REL_TOL, totals, v3};
use crate::{read_json, recipe};
@@ -62,10 +63,10 @@ const MESH_TOL: f64 = 0.01;
/// A mesh's edges used by one triangle only (open) and by more than two (over-shared). Vertices
/// closer than `weld` (mm) are one vertex: a mesh repeats a vertex for each face, and faces
/// meeting at an edge may place it a rounding error apart.
fn open_edges(positions: &[solidcraft_engine::geom::Vec3], triangles: &[[u32; 3]], weld: f64) -> (usize, usize) {
fn open_edges(positions: &[solidcore_record::Vec3], triangles: &[[u32; 3]], weld: f64) -> (usize, usize) {
use std::collections::HashMap;
let weld = if weld.is_finite() && weld > 0.0 { weld } else { 1e-4 };
let cell = |p: &solidcraft_engine::geom::Vec3| [p.x, p.y, p.z].map(|x| (x / weld).floor() as i64);
let cell = |p: &solidcore_record::Vec3| [p.x, p.y, p.z].map(|x| (x / weld).floor() as i64);
// Union-find over positions, joining each vertex with any within `weld` in the 27 cells round it.
let mut parent: Vec<usize> = (0..positions.len()).collect();
fn find(parent: &mut [usize], mut i: usize) -> usize {
@@ -125,7 +126,7 @@ const SEAM_ANGLE_DEG: f64 = 1.0;
/// the patch's boundary, where its neighbours' own G1 seams end and no surface is G2 to both),
/// with how many seams and stations clear there were. An error where the seams can't be measured.
fn curvature_jumps(body: &solidcraft_engine::kernel::Body, near: [f64; 3], within: f64, clear: f64) -> Result<Value, String> {
use solidcraft_engine::geom::Vec3;
use solidcore_record::Vec3;
let tol =
solidcraft_engine::kernel::ContinuityTolerances { gap_mm: 1e-3, angle_deg: SEAM_ANGLE_DEG, curvature_per_mm: 0.0, ..Default::default() };
let report = solidcraft_engine::kernel::seam_diagnostics(body, &[], SEAM_SAMPLES, tol).map_err(|e| e.to_string())?;
@@ -182,7 +183,7 @@ fn continuity_seams(b: &solidcraft_engine::doc::ModelBody, edges: &Value) -> Res
if let Some(points) = edges.as_array() {
let picks = points
.iter()
.map(|p| v3(p).map(|[x, y, z]| solidcraft_engine::kernel::EdgePick::Point(solidcraft_engine::geom::Vec3::new(x, y, z))))
.map(|p| v3(p).map(|[x, y, z]| solidcraft_engine::kernel::EdgePick::Point(solidcore_record::Vec3::new(x, y, z))))
.collect::<Option<Vec<_>>>()
.ok_or("continuity `edges` points must each be [x,y,z]")?;
let picked = solidcraft_engine::kernel::pick_edges(&b.body, &picks).map_err(|e| e.to_string())?;
@@ -480,8 +481,8 @@ fn output_checks(c: &mut Checks, want: &Value, out: &str, results: &[Value], rel
/// The word a `surface_types` check names a face's surface by, as the record holds it: an
/// analytic kind, `ball_patch` (a corner's patch on a sphere), `bspline` (a B-spline or NURBS
/// surface) or `revolution` (a revolved curve).
fn surface_word(s: &solidcraft_engine::geom::brep::Surface) -> &'static str {
use solidcraft_engine::geom::brep::Surface as S;
fn surface_word(s: &solidcore_record::brep::Surface) -> &'static str {
use solidcore_record::brep::Surface as S;
match s {
S::Plane { .. } | S::PlaneThrough { .. } => "plane",
S::Cylinder { .. } => "cylinder",
@@ -497,8 +498,8 @@ fn surface_word(s: &solidcraft_engine::geom::brep::Surface) -> &'static str {
/// A B-spline face's two directions as `[degree, spans]` (spans: the distinct knot intervals of
/// the modelled range), the lower first, so a surface parameterised the other way round reads the
/// same. `None` for a surface that is not a B-spline.
fn spline_shape(s: &solidcraft_engine::geom::brep::Surface) -> Option<[[u64; 2]; 2]> {
use solidcraft_engine::geom::brep::Surface as S;
fn spline_shape(s: &solidcore_record::brep::Surface) -> Option<[[u64; 2]; 2]> {
use solidcore_record::brep::Surface as S;
let (u, v, nu, nv) = match s {
S::Spline { u_knots, v_knots, points } => (u_knots, v_knots, points.len(), points.first().map_or(0, Vec::len)),
S::RationalSpline { u_knots, v_knots, homogeneous } => (u_knots, v_knots, homogeneous.len(), homogeneous.first().map_or(0, Vec::len)),
@@ -553,7 +554,7 @@ const CONSTRUCT_DIR_TOL: f64 = 1e-6;
/// parallel to `normal` (either way) and which passes within `tol` of `through`. An axis or plane
/// is graded as the line or plane it is, never by the origin it happens to keep.
fn check_construction(s: &solidcraft_engine::Session, want: &Value, tol: f64, c: &mut Checks) {
use solidcraft_engine::geom::Vec3;
use solidcore_record::Vec3;
use solidcraft_engine::view::{construction_axes, construction_planes, construction_points};
let Some(named) = want.as_object() else {
c.push("construction", json!("`construction` must map names to {point} | {axis} | {plane}"), json!("an object"), false);
@@ -657,7 +658,34 @@ fn flat_pattern_checks(c: &mut Checks, want: &Value, results: &[Value], rel: f64
}
}
fn check_case(dir: &str, dumps: Option<&std::path::Path>, validation: solidcraft_engine::kernel::Validation) -> Value {
/// [`check_case_in`], and with `export` the case's corpus entry written under it
/// ([`crate::kernel_cases_export`]).
fn check_case(
dir: &str,
dumps: Option<&std::path::Path>,
validation: solidcraft_engine::kernel::Validation,
export: Option<&std::path::Path>,
) -> Value {
let mut capture = export.map(|_| crate::kernel_cases_export::Capture::default());
let out = check_case_in(dir, dumps, validation, capture.as_mut());
if let (Some(root), Some(capture)) = (export, capture)
&& let Err(e) = crate::kernel_cases_export::write(root, dir, &out, capture)
{
let mut out = out;
out["export_error"] = json!(e);
return out;
}
out
}
/// One case run and graded. With `capture`, the kernel operations its commands make are traced
/// and kept there with the bodies the case ends with.
fn check_case_in(
dir: &str,
dumps: Option<&std::path::Path>,
validation: solidcraft_engine::kernel::Validation,
mut capture: Option<&mut crate::kernel_cases_export::Capture>,
) -> Value {
let (area, name) = case_name(dir);
let mut out = json!({"case": name, "area": area, "kernel": kernel()});
let want = match read_json(&format!("{dir}/expected.json")) {
@@ -711,7 +739,18 @@ fn check_case(dir: &str, dumps: Option<&std::path::Path>, validation: solidcraft
let mut s = solidcraft_engine::Session::default();
s.set_validation(validation);
let clock = std::time::Instant::now();
if capture.is_some() {
solidcraft_engine::kernel::smlib_trace::start();
}
let ran = s.run_script(&commands);
// The trace ends with the commands: grading reads the bodies through the kernel too.
if let Some(c) = &mut capture {
c.events = solidcraft_engine::kernel::smlib_trace::finish();
c.ran = Some(match &ran {
Ok(_) => s.world_state().map(|w| w.bodies.iter().map(|b| (b.name.clone(), b.body.record().clone())).collect()).map_err(|e| e.to_string()),
Err(e) => Err(e.to_string()),
});
}
// The commands' own time, without process start or grading: the ladder's timing golden.
out["seconds"] = json!(clock.elapsed().as_secs_f64());
let answers = match (ran, may_refuse) {
@@ -976,7 +1015,13 @@ pub fn run(args: &[String]) -> Result<(), String> {
.ok_or_else(|| format!("kernel-cases: --validation= must be each_step, each_feature or off, not `{m}`"))?,
None => solidcraft_engine::kernel::Validation::default(),
};
let results: Vec<Value> = dirs.iter().map(|d| check_case(d, dumps.as_deref(), validation)).collect();
// `--export=DIR`: each case's kernel operations, their input records and its expected values
// go to DIR/<area>/<case>.json, the corpus for solidcore's rig.
let export = args.iter().find_map(|a| a.strip_prefix("--export=")).map(std::path::PathBuf::from);
if export.as_ref().is_some_and(|d| d.as_os_str().is_empty()) {
return Err("kernel-cases: --export= needs a folder".into());
}
let results: Vec<Value> = dirs.iter().map(|d| check_case(d, dumps.as_deref(), validation, export.as_deref())).collect();
if dumps.is_some() {
solidcraft_engine::kernel::smlib_dump_dir(None);
}
@@ -1027,7 +1072,7 @@ mod tests {
/// either order of directions, spans only where given, and every face must be accounted for.
#[test]
fn splines_match_degrees_and_spans_in_either_order() {
use solidcraft_engine::geom::brep::Surface;
use solidcore_record::brep::Surface;
let knots = |p: usize, spans: usize| -> Vec<f64> {
let mut k = vec![0.0; p + 1];
k.extend((1..spans).map(|i| i as f64));
@@ -0,0 +1,299 @@
//! `kernel-cases --export=DIR`: one kernel rig case as a corpus entry for solidcore's rig. The
//! case's commands run as the rig runs them while the kernel's operations are traced
//! (`smlib_trace`); the entry holds the operation (or the chain of operations) that made the
//! bodies the case ends with, each operand that came in as a record given as that record (the
//! B-rep record, solidcore-record's JSON), the fix set, the case's expected values and the rig's
//! own result. A case whose result no kernel operation alone accounts for is written too, with
//! the reasons, so nothing is dropped silently.
//!
//! An entry's operation is expressible (`operation_expressible`) when the run failed in a kernel
//! operation (the last one traced failed), or when every body the case ends with is the record a
//! traced operation's output was written as, or an operand some traced operation took, as its
//! record. The entry is wholly expressible (`expressible`) when, besides, every value it grades is
//! the kernel's: none of `continuity`, `watertight`, `files`, `results`, `construction` or
//! `flat_pattern` (SolidCraft computes them), no face, edge or vertex count that the rig's merging
//! across analytic surfaces (`merged_topology`) changes, and no refusal graded on SolidCraft's
//! wording of the kernel's error. Those are listed in `ungraded`.
use std::collections::{BTreeMap, BTreeSet, HashMap};
use std::path::{Path, PathBuf};
use serde_json::{Value, json};
use solidcore_record::brep::Brep;
use solidcraft_engine::kernel::smlib_trace::{self, Event};
use crate::read_json;
/// What a traced case leaves for its entry: the trace of its commands, and the bodies it ends
/// with by name or the engine's error.
#[derive(Default)]
pub struct Capture {
pub events: Vec<Event>,
pub ran: Option<Result<Vec<(String, Brep)>, String>>,
}
/// The expected values SolidCraft computes, never the kernel.
const GRADER_ONLY: [&str; 6] = ["continuity", "watertight", "files", "results", "construction", "flat_pattern"];
/// The case directory's area (its path under `tests/kernel-cases/`, a ladder case's tier under
/// `ladder/`) and its id.
fn area_and_id(dir: &str) -> Result<(String, String), String> {
let p = Path::new(dir);
let parts: Vec<String> = p.components().map(|c| c.as_os_str().to_string_lossy().to_string()).collect();
let at = parts.iter().rposition(|c| c == "kernel-cases").ok_or_else(|| format!("{dir}: not under tests/kernel-cases"))?;
let rest = parts.get(at + 1..).unwrap_or_default();
match rest.split_last() {
Some((id, area)) if !area.is_empty() => Ok((area.join("/"), id.clone())),
_ => Err(format!("{dir}: no area")),
}
}
/// The case's line in its area's `KNOWN_FAILING`: why it fails today; `None` when not listed.
fn known_failing(dir: &str, id: &str) -> Option<String> {
let list = Path::new(dir).parent()?.join("KNOWN_FAILING");
let text = std::fs::read_to_string(list).ok()?;
text.lines().filter(|l| !l.trim_start().starts_with('#')).find_map(|l| {
let (case, why) = l.split_once(char::is_whitespace).unwrap_or((l, ""));
(case == id).then(|| why.trim().to_string())
})
}
/// The trace read as operations and the records that went in and came out.
struct Trace<'a> {
/// Each operation: its name, operands, parameters and outputs or error.
calls: Vec<(&'static str, &'a [u64], &'a Value, &'a Result<Vec<u64>, String>)>,
/// The record each body made from a record was made from.
imports: HashMap<u64, (&'a Brep, bool, bool)>,
/// The records bodies were written as, in order.
exports: Vec<(u64, &'a Brep)>,
/// The operation that made each body.
producer: HashMap<u64, usize>,
}
impl<'a> Trace<'a> {
fn of(events: &'a [Event]) -> Trace<'a> {
let mut t = Trace { calls: Vec::new(), imports: HashMap::new(), exports: Vec::new(), producer: HashMap::new() };
for e in events {
match e {
Event::Import { id, record, trusted, keeping_splines } => {
t.imports.insert(*id, (record, *trusted, *keeping_splines));
}
Event::Export { id, record } => t.exports.push((*id, record)),
Event::Call { op, inputs, params, outputs } => {
if let Ok(made) = outputs {
for id in made {
t.producer.insert(*id, t.calls.len());
}
}
t.calls.push((*op, inputs, params, outputs));
}
}
}
t
}
/// Bodies some operation took as an operand.
fn operands(&self) -> BTreeSet<u64> {
self.calls.iter().flat_map(|c| c.1.iter().copied()).collect()
}
}
/// The operations `ends` need, in the order they ran: each body's maker, and its operands' makers
/// in turn, back to bodies made from records. Operands made by nothing traced are listed in
/// `unknown`.
fn chain(t: &Trace<'_>, ends: &[u64], unknown: &mut BTreeSet<u64>) -> Vec<usize> {
let mut need = BTreeSet::new();
let mut todo: Vec<u64> = ends.to_vec();
while let Some(id) = todo.pop() {
if t.imports.contains_key(&id) {
continue;
}
match t.producer.get(&id) {
Some(&i) => {
if need.insert(i)
&& let Some(c) = t.calls.get(i)
{
todo.extend(c.1.iter().copied());
}
}
None => {
unknown.insert(id);
}
}
}
need.into_iter().collect()
}
/// The entry of case `dir`, run with result `out`, into `root/<area>/<id>.json`, and the fix set
/// into `root/fixes.json`.
pub fn write(root: &Path, dir: &str, out: &Value, capture: Capture) -> Result<(), String> {
let (area, id) = area_and_id(dir)?;
let want = read_json(&format!("{dir}/expected.json"))?;
let t = Trace::of(&capture.events);
// Why the operation is not the kernel's alone, and what the entry cannot grade without
// SolidCraft.
let mut why_not: Vec<String> = Vec::new();
let mut ungraded: Vec<String> = Vec::new();
let has = |k: &str| want.get(k).is_some_and(|v| !v.is_null());
if let Some(missing) = want["missing"].as_str() {
why_not.push(format!("SolidCraft has no command for it yet: {missing}"));
}
for k in GRADER_ONLY.iter().filter(|k| has(k)) {
ungraded.push(format!("{k}: SolidCraft computes it"));
}
let mut result: Vec<Value> = Vec::new();
let mut ends: Vec<u64> = Vec::new();
let mut failed_call: Option<usize> = None;
let mut outcome = json!({"ran": true});
match (&capture.ran, has("missing")) {
(_, true) => {}
(None, false) => why_not.push("the case did not run".into()),
(Some(Err(e)), false) => {
// The run stopped. It stopped in the kernel when the last operation that failed is
// followed by no operation but copies (a copy or a tagged copy, which build nothing):
// nothing was built after it, so the engine's error reports that failure in its own
// words. An operation that ran after it means the engine went on and failed itself.
outcome = json!({"ran": false, "engine_error": e});
let last_failed = t.calls.iter().rposition(|c| c.3.is_err());
let built_after = |i: usize| t.calls.iter().skip(i + 1).any(|c| !matches!(c.0, "copy" | "tagged"));
match last_failed.filter(|&i| !built_after(i)).and_then(|i| t.calls.get(i).map(|c| (i, c))) {
Some((i, c)) => {
failed_call = Some(i);
ends.extend(c.1.iter().copied());
if let Err(k) = c.3 {
outcome["kernel_error"] = json!(k);
if let Some(why) = want["refusal"].as_str().filter(|w| !k.contains(*w)) {
ungraded.push(format!("refusal {why:?}: SolidCraft's wording of the kernel's error"));
}
}
}
None if t.calls.is_empty() => why_not.push(format!("no kernel operation ran before the failure: {e}")),
None => why_not.push(format!("the failure is SolidCraft's, not a kernel operation's: {e}")),
}
}
(Some(Ok(world)), false) => {
let operands = t.operands();
let mut made = 0;
for (name, record) in world {
let from_op = t.exports.iter().rev().find(|(id, r)| *r == record && t.producer.contains_key(id)).map(|(id, _)| *id);
let operand = t.imports.iter().filter(|(i, (r, _, _))| *r == record && operands.contains(i)).map(|(i, _)| *i).min();
match (from_op, operand) {
(Some(id), _) => {
made += 1;
ends.push(id);
result.push(json!({"body": name, "made": format!("b{id}")}));
}
(None, Some(id)) => {
ends.push(id);
result.push(json!({"body": name, "operand": format!("b{id}")}));
}
(None, None) => why_not.push(format!(
"body {name:?} is no kernel operation's output nor an operand: SolidCraft built it or changed the kernel's record"
)),
}
}
if world.is_empty() {
why_not.push("the case ends with no body".into());
} else if made == 0 && why_not.is_empty() {
why_not.push("no kernel operation made a body the case ends with".into());
}
// Counts graded as SolidCraft merges them, where that differs from the records'.
let raw = |k: &str| -> u64 {
world
.iter()
.map(|(_, r)| match k {
"faces" => r.faces.len(),
"edges" => r.edges.len(),
_ => r.vertices.len(),
} as u64)
.sum()
};
for k in ["faces", "edges", "vertices"] {
if let Some(merged) = out["measured"][k].as_u64().filter(|&m| has(k) && m != raw(k)) {
ungraded.push(format!("{k}: graded merged across analytic surfaces ({merged}), the records have {}", raw(k)));
}
}
outcome["raw_counts"] = json!({"faces": raw("faces"), "edges": raw("edges"), "vertices": raw("vertices")});
}
}
let mut unknown = BTreeSet::new();
let mut order = chain(&t, &ends, &mut unknown);
if !unknown.is_empty() {
why_not.push(format!("operands made by nothing traced: {unknown:?}"));
}
if let Some(i) = failed_call.filter(|i| !order.contains(i)) {
order.push(i);
}
// Operand records: every body the chain takes, or the case ends with, that came in as a record.
let mut bodies = BTreeMap::new();
let given = |id: u64, bodies: &mut BTreeMap<String, Value>| {
if let Some((record, trusted, splines)) = t.imports.get(&id) {
bodies.insert(format!("b{id}"), json!({"trusted": trusted, "keeping_splines": splines, "record": record}));
}
};
let mut calls: Vec<Value> = Vec::new();
for (op, inputs, params, outputs) in order.iter().filter_map(|&i| t.calls.get(i)) {
for &id in inputs.iter() {
given(id, &mut bodies);
}
let mut c = json!({"op": op, "inputs": inputs.iter().map(|i| format!("b{i}")).collect::<Vec<_>>(), "params": params});
match outputs {
Ok(made) => c["outputs"] = json!(made.iter().map(|i| format!("b{i}")).collect::<Vec<_>>()),
Err(e) => c["error"] = json!(e),
}
calls.push(c);
}
for r in &result {
if let Some(id) = r["operand"].as_str().and_then(|b| b.strip_prefix('b')).and_then(|n| n.parse().ok()) {
given(id, &mut bodies);
}
}
let rig = json!({
"status": out["status"], "error": out["error"], "refused_with": out["refused_with"], "measured": out["measured"],
"known_failing": known_failing(dir, &id),
});
let operation_expressible = why_not.is_empty();
let entry = json!({
"case": id,
"area": area,
"sources": want["sources"],
"what": want["what"],
"expressible": operation_expressible && ungraded.is_empty(),
"operation_expressible": operation_expressible,
"why_not": why_not,
"ungraded": ungraded,
"expected": want,
"rig": rig,
"outcome": outcome,
"calls": calls,
"bodies": bodies,
"result": result,
"traced_ops": t.calls.iter().map(|c| c.0).collect::<Vec<_>>(),
});
let folder: PathBuf = root.join(&area);
std::fs::create_dir_all(&folder).map_err(|e| format!("{}: {e}", folder.display()))?;
let path = folder.join(format!("{id}.json"));
let text = serde_json::to_string(&entry).map_err(|e| e.to_string())?;
std::fs::write(&path, text).map_err(|e| format!("{}: {e}", path.display()))?;
// Cases run in parallel processes: each writes its own file and renames it over the shared
// one, so no reader sees a torn file (every process writes the same fix set).
let fixes = serde_json::to_string_pretty(&smlib_trace::fixes()).map_err(|e| e.to_string())?;
let own = root.join(format!("fixes.json.{}", std::process::id()));
std::fs::write(&own, fixes).map_err(|e| format!("{}: {e}", own.display()))?;
std::fs::rename(&own, root.join("fixes.json")).map_err(|e| format!("{}: {e}", root.display()))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_area_is_the_path_under_kernel_cases() {
assert_eq!(area_and_id("tests/kernel-cases/booleans/1894-x").unwrap(), ("booleans".into(), "1894-x".into()));
assert_eq!(area_and_id("/r/tests/kernel-cases/ladder/07-fillets/003-y").unwrap(), ("ladder/07-fillets".into(), "003-y".into()));
assert!(area_and_id("/r/other/x").is_err());
}
}
+2 -1
View File
@@ -35,6 +35,7 @@ mod bench_assistant;
mod crashes;
mod fuzz;
mod kernel_cases;
mod kernel_cases_export;
mod logger;
mod oracle;
mod preview;
@@ -85,7 +86,7 @@ const USAGE_RECIPE: &str = " solidcraft-cli recipe <recipe.json>\n";
/// `oracle`'s lines in the usage.
const USAGE_ORACLE: &str = " solidcraft-cli oracle <case-dir>... [--json]\n";
/// `kernel-cases`'s lines in the usage.
const USAGE_KERNEL_CASES: &str = " solidcraft-cli kernel-cases <case-dir>... [--json] [--dumps=DIR] [--validation=each_step|each_feature|off]\n (the kernel regression rig, tests/kernel-cases, on SMLib; --dumps: each case's\n failing SMLib calls leave their inputs for replay in DIR/<area>-<case>; --validation: when SMLib's\n full check runs (diagnostics.validation), default each_feature)\n";
const USAGE_KERNEL_CASES: &str = " solidcraft-cli kernel-cases <case-dir>... [--json] [--dumps=DIR] [--export=DIR] [--validation=each_step|each_feature|off]\n (the kernel regression rig, tests/kernel-cases, on SMLib; --dumps: each case's\n failing SMLib calls leave their inputs for replay in DIR/<area>-<case>; --export: each case's\n kernel operations, input records and expected values for solidcore's rig, in DIR/<area>/<case>.json; --validation: when SMLib's\n full check runs (diagnostics.validation), default each_feature)\n";
/// `fuzz`'s lines in the usage.
const USAGE_FUZZ: &str =
" solidcraft-cli fuzz [--from N] [--count N] [--jobs J] [--timeout S] [--steps N] [--minimise] [--out DIR] [--report FILE]\n";
+1 -1
View File
@@ -5,7 +5,7 @@
#![allow(clippy::needless_range_loop)]
use serde_json::{Value, json};
use solidcraft_engine::geom::Vec3;
use solidcore_record::Vec3;
use crate::spatial::{self, Tri};
use crate::{read_json, recipe};
+3 -2
View File
@@ -21,6 +21,7 @@
//! Each design runs in a child process (this test binary, [`action_matrix_worker`]) so an action
//! that hangs or aborts is recorded as such and the rest of the design still runs.
use solidcraft_engine::geom::prelude::*;
use std::collections::{BTreeMap, BTreeSet};
use std::io::{BufRead, Write};
use std::path::{Path, PathBuf};
@@ -28,10 +29,10 @@ use std::process::{Command, Stdio};
use std::time::{Duration, Instant};
use serde_json::{Value, json};
use solidcore_record::Vec3;
use solidcraft_engine::Session;
use solidcraft_engine::doc::kernel::{FaceSurface, face_surface};
use solidcraft_engine::doc::{FaceAttachmentStatus, ModelBody, naming};
use solidcraft_engine::geom::Vec3;
/// One in this many actions runs on pull requests (by a stable hash of its key).
const PR_SAMPLE: u64 = 40;
@@ -1656,7 +1657,7 @@ impl TipConstruction {
other => panic!("expected an axis, got {other:?}"),
}
}
fn plane(&self) -> solidcraft_engine::geom::Plane {
fn plane(&self) -> solidcore_record::Plane {
match &self.0 {
solidcraft_engine::doc::construct::ConstructGeom::Plane(p) => *p,
other => panic!("expected a plane, got {other:?}"),
+1 -1
View File
@@ -4,7 +4,7 @@
use std::f64::consts::PI;
use serde_json::Value;
use solidcraft_engine::geom::Vec3;
use solidcore_record::Vec3;
use solidcraft_engine::{Session, io, kernel};
fn assert_two_through_holes(session: &Session, stage: &str) {
@@ -2,6 +2,7 @@
//! the current format and evaluate (what they rebuild to today is today's, not what the build
//! that saved them made); damaged and hostile design files never panic.
use solidcraft_engine::geom::prelude::*;
use std::path::{Path, PathBuf};
use serde_json::{Value, json};
+2 -1
View File
@@ -16,13 +16,14 @@
//! designs, the engine's design fixtures and the older designs kept by the hole matrix, taper
//! and projection tests.
use solidcraft_engine::geom::prelude::*;
use std::path::{Path, PathBuf};
use std::process::Command;
use serde_json::{Value, json};
use solidcore_record::Vec3;
use solidcraft_engine::Session;
use solidcraft_engine::doc::{FaceAttachmentStatus, Feature, FeatureKind, ModelBody, ModelState, PlaneRef, naming};
use solidcraft_engine::geom::Vec3;
fn root() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR")).join("../..").canonicalize().unwrap()
@@ -9,6 +9,7 @@ use std::path::{Path, PathBuf};
use serde_json::{Value, json};
use solidcraft_engine::Session;
use solidcraft_engine::doc::kernel::{FaceSurface, face_surface};
use solidcraft_engine::geom::prelude::*;
fn root() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR")).join("../..").canonicalize().unwrap()
+3 -2
View File
@@ -460,12 +460,13 @@ fn snapshot_runs_its_script_on_the_in_design() {
}
}
/// An unknown command's error suggests the id that was meant.
/// An unknown command's error suggests the id that was meant first; other close ids (such as
/// `drawing.open`) may follow it.
#[test]
fn unknown_command_suggests_the_closest_id() {
let output = cli().args(["exec", "file.open"]).output().unwrap();
assert_eq!(output.status.code(), Some(1));
assert!(String::from_utf8_lossy(&output.stderr).contains("did you mean `doc.open`?"), "{}", String::from_utf8_lossy(&output.stderr));
assert!(String::from_utf8_lossy(&output.stderr).contains("did you mean `doc.open`"), "{}", String::from_utf8_lossy(&output.stderr));
}
/// #1833: `--timings` reports each command's time instead of the measurement, `--profile` adds
+1 -1
View File
@@ -288,7 +288,7 @@ fn a_snapshot_shows_the_part_where_the_camera_puts_it() {
let vp = c.view_proj(f64::from(SIZE.0) / f64::from(SIZE.1), 1000.0);
let (mut elo, mut ehi) = ([f64::MAX; 2], [f64::MIN; 2]);
for k in 0..8 {
let p = solidcraft_engine::geom::Vec3::new(
let p = solidcore_record::Vec3::new(
corner[0] + size[0] * f64::from(k & 1),
corner[1] + size[1] * f64::from((k >> 1) & 1),
corner[2] + size[2] * f64::from((k >> 2) & 1),
@@ -5,13 +5,14 @@
//! vertices nothing else used, so an edge that is one straight line was drawn (and lit on hover)
//! as many pieces, and a sketch on such a face projected every piece.
use solidcraft_engine::geom::prelude::*;
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use serde_json::json;
use solidcore_record::Vec3;
use solidcraft_engine::Session;
use solidcraft_engine::doc::kernel::{FaceSurface, face_surface};
use solidcraft_engine::geom::Vec3;
fn design() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR")).join("../../tests/action_matrix/designs/crazy-geom-cuts.sc")
+1 -1
View File
@@ -36,7 +36,7 @@ js-sys = "0.3"
serde_json = { workspace = true }
web-sys = { version = "0.3", features = [
"Window", "Document", "Element", "HtmlElement", "HtmlCanvasElement", "Location", "Navigator", "StorageManager",
"FileSystemHandle", "FileSystemDirectoryHandle", "FileSystemFileHandle", "FileSystemGetFileOptions", "FileSystemWritableFileStream",
"FileSystemHandle", "FileSystemDirectoryHandle", "FileSystemFileHandle", "FileSystemGetFileOptions", "FileSystemGetDirectoryOptions", "FileSystemWritableFileStream",
"WritableStream", "File", "FileList", "Blob", "BlobPropertyBag", "Url", "HtmlAnchorElement", "HtmlInputElement", "Storage", "StorageEvent",
"IdbFactory", "IdbDatabase", "IdbObjectStore", "IdbOpenDbRequest", "IdbRequest", "IdbTransaction", "IdbTransactionMode",
"Headers", "ReadableStream", "ReadableStreamDefaultReader",
+2
View File
@@ -0,0 +1,2 @@
This build keeps its designs and settings apart per release channel (src/channel.rs).
packaging/web-channels.sh only offers a release candidate that has this file.
+10
View File
@@ -26,6 +26,15 @@
<link data-trunk rel="copy-file" href="../../assets/fonts/craft-ui-sc/OFL.txt" data-target-path="fonts/craft-ui-sc" />
<link data-trunk rel="copy-file" href="../../assets/fonts/craft-ui-tc/CraftUI-TC.otf" data-target-path="fonts" />
<link data-trunk rel="copy-file" href="../../assets/fonts/craft-ui-tc/OFL.txt" data-target-path="fonts/craft-ui-tc" />
<!-- A drawing's Arabic, Korean and Chinese text: each fetched once, when a drawing needs it. -->
<link data-trunk rel="copy-file" href="../../assets/fonts/noto-sans-arabic/NotoSansArabic-Regular.ttf" data-target-path="fonts/noto-sans-arabic" />
<link data-trunk rel="copy-file" href="../../assets/fonts/noto-sans-arabic/OFL.txt" data-target-path="fonts/noto-sans-arabic" />
<link data-trunk rel="copy-file" href="../../assets/fonts/noto-sans-kr/NotoSansKR-Regular.otf" data-target-path="fonts/noto-sans-kr" />
<link data-trunk rel="copy-file" href="../../assets/fonts/noto-sans-kr/OFL.txt" data-target-path="fonts/noto-sans-kr" />
<link data-trunk rel="copy-file" href="../../assets/fonts/noto-sans-sc/NotoSansSC-Regular.otf" data-target-path="fonts/noto-sans-sc" />
<link data-trunk rel="copy-file" href="../../assets/fonts/noto-sans-sc/OFL.txt" data-target-path="fonts/noto-sans-sc" />
<link data-trunk rel="copy-file" href="../../assets/fonts/noto-sans-tc/NotoSansTC-Regular.otf" data-target-path="fonts/noto-sans-tc" />
<link data-trunk rel="copy-file" href="../../assets/fonts/noto-sans-tc/OFL.txt" data-target-path="fonts/noto-sans-tc" />
<link data-trunk rel="css" href="../../assets/styles/web/loading.css" />
<link data-trunk rel="copy-file" href="../../assets/styles/web/loading-logo.svg" />
<!-- Installable as an app (manifest.webmanifest); the icons are the Linux exports of the app mark. -->
@@ -34,6 +43,7 @@
<link rel="icon" type="image/png" sizes="32x32" href="icons/32x32/io.tangencylabs.solidcraft.png">
<link rel="apple-touch-icon" href="icons/256x256/io.tangencylabs.solidcraft.png">
<link data-trunk rel="copy-file" href="manifest.webmanifest" />
<link data-trunk rel="copy-file" href="channel-storage.txt" />
<link data-trunk rel="copy-file" href="../../assets/icons/app/linux/exports/hicolor/32x32/apps/io.tangencylabs.solidcraft.png" data-target-path="icons/32x32" />
<link data-trunk rel="copy-file" href="../../assets/icons/app/linux/exports/hicolor/256x256/apps/io.tangencylabs.solidcraft.png" data-target-path="icons/256x256" />
<link data-trunk rel="copy-file" href="../../assets/icons/app/linux/exports/hicolor/512x512/apps/io.tangencylabs.solidcraft.png" data-target-path="icons/512x512" />
+61
View File
@@ -0,0 +1,61 @@
//! Release channels. The site serves the stable web app at `/app/`, the release candidate at
//! `/app-rc/` and the bleeding-edge build at `/app-edge/`: one origin, so one set of browser
//! storage. A channel that kept its designs and settings in the same places could save a design
//! in a file format the stable app can't open, or overwrite stable's settings. So each
//! pre-release channel keeps everything under its own name, and stable (and any copy served from
//! another path: the release zip, `trunk serve`) keeps the names it has always used, so what
//! people have saved at `/app/` is untouched.
//!
//! The channel comes from the directory the app is served from, not from the build, so a build
//! taken from a release zip is as separate as one built on the spot.
/// The channel the app is served as, from the page's path: `"rc"`, `"edge"`, or `""` (stable).
pub fn of_path(pathname: &str) -> &'static str {
match pathname.trim_end_matches("index.html").trim_end_matches('/').rsplit('/').next() {
Some("app-rc") => "rc",
Some("app-edge") => "edge",
_ => "",
}
}
/// `name` as this channel stores it: unchanged for stable, `rc:name` and `edge:name` otherwise.
pub fn name_in(channel: &str, name: &str) -> String {
if channel.is_empty() { name.to_owned() } else { format!("{channel}:{name}") }
}
#[cfg(target_arch = "wasm32")]
pub fn current() -> &'static str {
web_sys::window().and_then(|w| w.location().pathname().ok()).map_or("", |p| of_path(&p))
}
/// A storage key (local storage, IndexedDB database name) of the running channel.
#[cfg(target_arch = "wasm32")]
pub fn key(name: &str) -> String {
name_in(current(), name)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn channel_follows_the_directory() {
assert_eq!(of_path("/app/"), "");
assert_eq!(of_path("/app/index.html"), "");
assert_eq!(of_path("/app-rc/"), "rc");
assert_eq!(of_path("/app-rc"), "rc");
assert_eq!(of_path("/app-rc/index.html"), "rc");
assert_eq!(of_path("/app-edge/"), "edge");
assert_eq!(of_path("/"), "");
assert_eq!(of_path("/solidcraft-web-0.30.1/"), "");
assert_eq!(of_path("/app/app-rc/"), "rc", "only the directory's own name counts");
assert_eq!(of_path("/app-rc/app/"), "");
}
#[test]
fn stable_keeps_its_names() {
assert_eq!(name_in("", "solidcraft.prefs"), "solidcraft.prefs");
assert_eq!(name_in("rc", "solidcraft.prefs"), "rc:solidcraft.prefs");
assert_ne!(name_in("rc", "x"), name_in("edge", "x"));
}
}
+1 -1
View File
@@ -467,7 +467,7 @@ mod web {
async fn open_db() -> Result<IdbDatabase, String> {
let factory = web_sys::window().ok_or("no window")?.indexed_db().map_err(err)?.ok_or("this browser has no IndexedDB")?;
let request: IdbOpenDbRequest = factory.open_with_u32(DB, 1).map_err(err)?;
let request: IdbOpenDbRequest = factory.open_with_u32(&crate::channel::key(DB), 1).map_err(err)?;
let target = request.clone();
let upgrade = Closure::once_into_js(move || {
if let Ok(db) = target.result().and_then(|r| r.dyn_into::<IdbDatabase>()) {
+21 -6
View File
@@ -16,7 +16,9 @@ use solidcraft_engine::io::vfs;
use crate::disk;
use wasm_bindgen::{JsCast as _, JsValue, closure::Closure};
use wasm_bindgen_futures::{JsFuture, spawn_local};
use web_sys::{FileSystemDirectoryHandle, FileSystemFileHandle, FileSystemGetFileOptions, FileSystemWritableFileStream};
use web_sys::{
FileSystemDirectoryHandle, FileSystemFileHandle, FileSystemGetDirectoryOptions, FileSystemGetFileOptions, FileSystemWritableFileStream,
};
/// Where OPFS files appear to the engine.
pub const OPFS: &str = disk::OPFS_PREFIX;
@@ -111,7 +113,7 @@ thread_local! {
static LOCAL: Cell<bool> = const { Cell::new(false) };
}
/// Local storage keys of stored files.
/// Local storage keys of stored files (under the channel's name, see `channel.rs`).
const LOCAL_PREFIX: &str = "solidcraft.file.";
/// Local-storage files kept by earlier releases (compatibility shim, docs/compatibility.md):
/// still listed and opened; removing one removes either key.
@@ -169,7 +171,17 @@ fn has(sm: &web_sys::StorageManager, method: &str) -> bool {
async fn root() -> Result<FileSystemDirectoryHandle, String> {
let sm = storage_manager().filter(|sm| has(sm, "getDirectory")).ok_or("this page has no private file storage (it needs https or localhost)")?;
let dir = JsFuture::from(sm.get_directory()).await.map_err(err)?;
dir.dyn_into::<FileSystemDirectoryHandle>().map_err(|_| "this browser has no private file storage (OPFS)".to_string())
let dir = dir.dyn_into::<FileSystemDirectoryHandle>().map_err(|_| "this browser has no private file storage (OPFS)".to_string())?;
// Stable keeps its files in the root, where they have always been; a pre-release channel has a folder of its own.
match crate::channel::current() {
"" => Ok(dir),
channel => {
let opts = FileSystemGetDirectoryOptions::new();
opts.set_create(true);
let sub = JsFuture::from(dir.get_directory_handle_with_options(&format!("channel-{channel}"), &opts)).await.map_err(err)?;
sub.dyn_into::<FileSystemDirectoryHandle>().map_err(|_| "not a folder".to_string())
}
}
}
pub(crate) async fn read_handle(h: &FileSystemFileHandle) -> Result<Vec<u8>, String> {
@@ -191,7 +203,7 @@ async fn file_handle(dir: &FileSystemDirectoryHandle, name: &str, create: bool)
async fn write_file(name: &str, bytes: &[u8]) -> Result<(), String> {
if local_only() {
return local()?
.set_item(&format!("{LOCAL_PREFIX}{name}"), &encode(bytes))
.set_item(&crate::channel::key(&format!("{LOCAL_PREFIX}{name}")), &encode(bytes))
.map_err(|e| format!("local storage is full or blocked: {}", err(e)));
}
let dir = root().await?;
@@ -207,7 +219,7 @@ async fn remove_file(name: &str) -> Result<(), String> {
if local_only() {
let st = local()?;
st.remove_item(&format!("{LEGACY_LOCAL_PREFIX}{name}")).map_err(err)?;
return st.remove_item(&format!("{LOCAL_PREFIX}{name}")).map_err(err);
return st.remove_item(&crate::channel::key(&format!("{LOCAL_PREFIX}{name}"))).map_err(err);
}
let dir = root().await?;
JsFuture::from(dir.remove_entry(name)).await.map_err(err)?;
@@ -220,7 +232,10 @@ fn read_local() -> Result<Vec<(String, Vec<u8>, f64)>, String> {
let mut out = Vec::new();
for i in 0..st.length().unwrap_or(0).min(10_000) {
let Some(k) = st.key(i).ok().flatten() else { continue };
let Some(name) = k.strip_prefix(LOCAL_PREFIX).or_else(|| k.strip_prefix(LEGACY_LOCAL_PREFIX)) else { continue };
let own = crate::channel::key(LOCAL_PREFIX);
// The old names belong to stable; a pre-release channel has none.
let legacy = crate::channel::current().is_empty().then_some(LEGACY_LOCAL_PREFIX);
let Some(name) = k.strip_prefix(own.as_str()).or_else(|| legacy.and_then(|l| k.strip_prefix(l))) else { continue };
if let Some(v) = st.get_item(&k).ok().flatten() {
out.push((name.to_string(), decode(&v), 0.0));
}
+98 -22
View File
@@ -9,6 +9,8 @@
#[cfg(target_arch = "wasm32")]
mod api;
#[cfg(any(target_arch = "wasm32", test))]
mod channel;
#[cfg(any(target_arch = "wasm32", test))]
mod disk;
#[cfg(target_arch = "wasm32")]
mod easyprint;
@@ -21,6 +23,11 @@ mod linked;
#[cfg(any(target_arch = "wasm32", test))]
mod space_mouse;
/// The folder the page serves its fonts from: index.html copies each there, and a drawing's text
/// fetches its fonts from it (#4890).
#[cfg(any(target_arch = "wasm32", test))]
const FONTS: &str = "fonts";
/// The browser switches tabs with Ctrl+Tab and Ctrl+Shift+Tab, so the page leaves those keys to
/// it (#5227); plain Tab and Shift+Tab still move between the app's fields. (Ctrl+PageUp and
/// Ctrl+PageDown already reach the browser.)
@@ -152,8 +159,15 @@ impl PrefsWriter {
/// tab of the page when one tab writes). Other keys, and settings taken away (clearing the site's
/// data), leave this tab's settings as they are.
#[cfg(any(target_arch = "wasm32", test))]
fn settings_written(key: Option<&str>, value: Option<String>) -> Option<String> {
value.filter(|_| key == Some(solidcraft_ui_egui::prefs::STORAGE_KEY))
fn settings_written(own_key: &str, key: Option<&str>, value: Option<String>) -> Option<String> {
// Tabs of another channel write to the same storage, under their own key.
value.filter(|_| key == Some(own_key))
}
/// The settings key of `channel` (see [`channel`]).
#[cfg(target_arch = "wasm32")]
fn prefs_key() -> String {
channel::key(solidcraft_ui_egui::prefs::STORAGE_KEY)
}
#[cfg(target_arch = "wasm32")]
@@ -162,6 +176,7 @@ mod web {
use solidcraft_engine::Session;
use solidcraft_engine::io::vfs;
use solidcraft_ui_egui::ui_kit::BackingTooltipExt as _;
use solidcraft_ui_egui::{Services, SolveApp, ui_kit};
use wasm_bindgen::JsCast as _;
@@ -262,7 +277,7 @@ mod web {
fn hear_settings_from_other_tabs(window: &web_sys::Window, ctx: &egui::Context, inbox: PrefsInbox) {
let ctx = ctx.clone();
let on_storage = wasm_bindgen::closure::Closure::<dyn FnMut(web_sys::StorageEvent)>::new(move |e: web_sys::StorageEvent| {
if let Some(prefs) = crate::settings_written(e.key().as_deref(), e.new_value()) {
if let Some(prefs) = crate::settings_written(&crate::prefs_key(), e.key().as_deref(), e.new_value()) {
inbox.set(Some(prefs));
ctx.request_repaint();
}
@@ -277,6 +292,8 @@ mod web {
const CHINESE_TC_FONT_PATH: &str = "./fonts/CraftUI-TC.otf";
type FontLoadResult = Rc<std::cell::RefCell<Option<Result<(String, Vec<u8>), String>>>>;
/// The fetches of drawing fonts that have finished, for the next frame to hand on.
type DrawingFontsDone = Rc<std::cell::RefCell<Vec<(solidcraft_engine::sketch::fallback::WebFont, Result<Vec<u8>, String>)>>>;
struct Shell {
app: SolveApp,
@@ -307,6 +324,9 @@ mod web {
font_fetch_kind: Option<&'static str>,
/// The language the fonts were last set up for (a CJK UI leads with the CJK face).
font_language: String,
/// The fonts a drawing's text asked for, other than Japanese, each fetched once a session.
drawing_fonts_asked: std::collections::HashSet<solidcraft_engine::sketch::fallback::WebFont>,
drawing_fonts_done: DrawingFontsDone,
space_mouse: SpaceMouse,
wheel_gestures: solidcraft_ui_egui::WebWheelGestures,
/// The settings this tab wrote or loaded last, and those another tab wrote since (#8644).
@@ -334,8 +354,7 @@ mod web {
}
let prefs = self.persisted_prefs();
let Some(prefs) = self.prefs_writer.take(prefs) else { return };
let stored =
web_sys::window().and_then(|w| w.local_storage().ok().flatten()).map(|s| s.set_item(solidcraft_ui_egui::prefs::STORAGE_KEY, &prefs));
let stored = web_sys::window().and_then(|w| w.local_storage().ok().flatten()).map(|s| s.set_item(&crate::prefs_key(), &prefs));
if !matches!(stored, Some(Ok(()))) {
log::warn!("settings: the browser's storage did not take them; they are saved again later");
self.prefs_writer = crate::PrefsWriter::default();
@@ -492,7 +511,7 @@ mod web {
files::pick_from_disk(&self.files, Intent::Open);
}
if ui_kit::button(ui, "Insert from computer…", ui_kit::Button::Normal)
.on_hover_text(solidcraft_ui_egui::i18n::tr("web.files.insert_drawings_hint"))
.backing_on_hover_text(solidcraft_ui_egui::i18n::tr("web.files.insert_drawings_hint"))
.clicked()
{
files::pick_from_disk(&self.files, Intent::Insert);
@@ -546,7 +565,7 @@ mod web {
let design = solidcraft_engine::io::is_design_path(&e.name);
if !design
&& ui_kit::button(ui, "Insert", ui_kit::Button::Small)
.on_hover_text(solidcraft_ui_egui::i18n::tr("web.files.insert_stored_drawings_hint"))
.backing_on_hover_text(solidcraft_ui_egui::i18n::tr("web.files.insert_stored_drawings_hint"))
.clicked()
{
act = Some(("insert", e.name.clone()));
@@ -556,7 +575,7 @@ mod web {
self.rename_focus = true;
}
if ui_kit::button(ui, "Save to computer…", ui_kit::Button::Small)
.on_hover_text("A copy in a file you choose; the stored design stays here")
.backing_on_hover_text("A copy in a file you choose; the stored design stays here")
.clicked()
{
act = Some(("save_copy", e.name.clone()));
@@ -711,6 +730,10 @@ mod web {
self.chinese_kind = Some("zh-TW");
}
_ => {
solidcraft_engine::sketch::fallback::provide_web_font(
solidcraft_engine::sketch::fallback::WebFont::Japanese,
bytes.clone(),
);
self.cjk_bytes = Some(bytes);
}
}
@@ -724,6 +747,9 @@ mod web {
_ => JAPANESE_FONT_PATH,
};
log::warn!("failed to fetch script font from {path}: {err}");
if !matches!(kind, Some("zh" | "zh-TW")) {
solidcraft_engine::sketch::fallback::web_font_failed(solidcraft_engine::sketch::fallback::WebFont::Japanese);
}
false
}
};
@@ -759,10 +785,12 @@ mod web {
self.font_language = effective.to_owned();
}
/// CJK text on screen with no Japanese font yet (an English UI showing a Japanese name):
/// fetch the Japanese font, so the text does not stay tofu (#720).
/// CJK text on screen with no Japanese font yet (an English UI showing a Japanese name), or a
/// drawing's text waiting for one: fetch the Japanese font, so the text does not stay tofu (#720).
fn watch_text_for_font(&mut self, ctx: &egui::Context) {
if self.font.watching_text() && solidcraft_ui_egui::fonts::painted_cjk(ctx) && self.font.text_needs_font() {
let japanese = solidcraft_engine::sketch::fallback::web_fonts_wanted().contains(&solidcraft_engine::sketch::fallback::WebFont::Japanese);
let wanted = solidcraft_ui_egui::fonts::painted_cjk(ctx) || japanese;
if self.font.watching_text() && wanted && self.font.text_needs_font() {
self.fetch_font(ctx, "ja");
}
}
@@ -783,6 +811,33 @@ mod web {
ctx.request_repaint();
});
}
/// The other fonts a drawing's text waits for (Arabic, Chinese, Korean): each fetched once,
/// and handed to the text when it comes. Japanese comes with the interface's CJK font.
fn fetch_drawing_fonts(&mut self, ctx: &egui::Context) {
use solidcraft_engine::sketch::fallback::{self, WebFont};
for (font, res) in self.drawing_fonts_done.borrow_mut().drain(..) {
match res {
Ok(bytes) => fallback::provide_web_font(font, bytes),
Err(err) => {
log::warn!("failed to fetch the drawing font {}/{}: {err}", crate::FONTS, font.file());
fallback::web_font_failed(font);
}
}
ctx.request_repaint();
}
for font in fallback::web_fonts_wanted() {
if font == WebFont::Japanese || !self.drawing_fonts_asked.insert(font) {
continue;
}
let (done, ctx) = (self.drawing_fonts_done.clone(), ctx.clone());
wasm_bindgen_futures::spawn_local(async move {
let res = fetch_font_bytes(&format!("./{}/{}", crate::FONTS, font.file())).await;
done.borrow_mut().push((font, res));
ctx.request_repaint();
});
}
}
}
impl eframe::App for Shell {
@@ -790,6 +845,7 @@ mod web {
// First: a language another tab chose fetches its font this frame.
self.load_other_tabs_prefs();
self.tick_font(ctx);
self.fetch_drawing_fonts(ctx);
self.app.logic(ctx);
self.api.poll();
self.space_mouse.logic(&mut self.app, ctx);
@@ -834,14 +890,15 @@ mod web {
self.store_prefs(&ctx);
}
fn persist_egui_memory(&self) -> bool {
self.launch.persists()
// eframe keeps this under one fixed key: only stable uses it, so a pre-release never rewrites it.
self.launch.persists() && crate::channel::current().is_empty()
}
fn save(&mut self, storage: &mut dyn eframe::Storage) {
if !self.launch.persists() {
return;
}
let prefs = self.persisted_prefs();
storage.set_string(solidcraft_ui_egui::prefs::STORAGE_KEY, prefs);
storage.set_string(&crate::prefs_key(), prefs);
}
}
@@ -851,7 +908,7 @@ mod web {
/// Where `?webgl` is remembered. The installed app starts from its manifest's start URL, which
/// has no query, so the choice has to outlive the page it was made on.
const WEBGL_KEY: &str = "solidcraft.webgl";
const WEBGL_KEY: &str = "solidcraft.webgl"; // stored under crate::channel::key
/// Render with WebGL2: asked for now (`?webgl`, remembered) or before (until `?webgpu`).
fn webgl() -> bool {
@@ -859,17 +916,17 @@ mod web {
let q = query();
if crate::query_flag(&q, "webgl") {
if let Some(s) = &storage {
let _ = s.set_item(WEBGL_KEY, "1");
let _ = s.set_item(&crate::channel::key(WEBGL_KEY), "1");
}
return true;
}
if crate::query_flag(&q, "webgpu") {
if let Some(s) = &storage {
let _ = s.remove_item(WEBGL_KEY);
let _ = s.remove_item(&crate::channel::key(WEBGL_KEY));
}
return false;
}
storage.and_then(|s| s.get_item(WEBGL_KEY).ok().flatten()).is_some()
storage.and_then(|s| s.get_item(&crate::channel::key(WEBGL_KEY)).ok().flatten()).is_some()
}
/// Fetch font bytes asynchronously through the browser.
@@ -968,7 +1025,7 @@ mod web {
if launch.persists() {
hear_settings_from_other_tabs(&window, &cc.egui_ctx, other_tab_prefs.clone());
}
if launch.persists() && let Some(p) = cc.storage.and_then(|s| solidcraft_ui_egui::prefs::stored(|k| s.get_string(k))) {
if launch.persists() && let Some(p) = cc.storage.and_then(|s| solidcraft_ui_egui::prefs::stored(|k| s.get_string(&crate::channel::key(k)))) {
app.load_prefs(&p);
}
if let Some(rs) = &cc.wgpu_render_state {
@@ -1012,6 +1069,8 @@ mod web {
chinese_kind: None,
font_fetch_kind: None,
font_language,
drawing_fonts_asked: Default::default(),
drawing_fonts_done: Default::default(),
space_mouse: SpaceMouse::default(),
wheel_gestures: solidcraft_ui_egui::WebWheelGestures::default(),
prefs_writer: crate::PrefsWriter::default(),
@@ -1171,9 +1230,26 @@ mod tests {
#[test]
fn only_settings_another_tab_wrote_are_loaded() {
let key = solidcraft_ui_egui::prefs::STORAGE_KEY;
assert_eq!(settings_written(Some(key), Some("{}".into())).as_deref(), Some("{}"));
assert_eq!(settings_written(Some("solidcraft.webgl"), Some("1".into())), None);
assert_eq!(settings_written(Some(key), None), None, "removed settings leave this tab's");
assert_eq!(settings_written(None, None), None, "clearing all storage leaves this tab's settings");
assert_eq!(settings_written(key, Some(key), Some("{}".into())).as_deref(), Some("{}"));
assert_eq!(settings_written(key, Some("solidcraft.webgl"), Some("1".into())), None);
assert_eq!(settings_written(key, Some(key), None), None, "removed settings leave this tab's");
assert_eq!(settings_written(key, None, None), None, "clearing all storage leaves this tab's settings");
assert_eq!(settings_written(key, Some("rc:solidcraft.prefs"), Some("{}".into())), None, "another channel's settings are not loaded");
}
/// Each font a drawing's text can ask for on the web is one index.html copies, from an asset
/// in the repository, into the folder the app fetches it from.
#[test]
fn each_drawing_font_is_copied_where_it_is_fetched() {
let page = include_str!("../index.html");
let here = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
for font in solidcraft_engine::sketch::fallback::WebFont::ALL {
let served = format!("{FONTS}/{}", font.file());
let (folder, file) = served.rsplit_once('/').unwrap_or_default();
let copy = page.lines().find(|l| l.contains("rel=\"copy-file\"") && l.contains(&format!("/{file}\""))).unwrap_or_default();
assert!(copy.contains(&format!("data-target-path=\"{folder}\"")), "index.html doesn't copy {served}: {copy}");
let from = copy.split("href=\"").nth(1).and_then(|rest| rest.split('"').next()).unwrap_or_default();
assert!(here.join(from).is_file(), "{served}: no {from}");
}
}
}
+2 -1
View File
@@ -81,6 +81,7 @@ windows-sys = { version = "0.61", features = [
"Win32_UI_HiDpi",
"Win32_UI_WindowsAndMessaging",
] }
winreg = "0.56"
[target.'cfg(target_os = "macos")'.dependencies]
muda = { version = "0.21.2", default-features = false }
@@ -92,7 +93,7 @@ hidapi = { version = "2.6.7", default-features = false, features = ["macos-share
# Files opened from Finder and the Dock (src/open_documents.rs), and the fullscreen title-bar
# inset (src/main.rs); all already come with eframe and rfd.
objc2 = "0.6"
objc2-app-kit = { version = "0.3", default-features = false, features = ["std", "NSApplication", "NSResponder", "NSWindow"] }
objc2-app-kit = { version = "0.3", default-features = false, features = ["std", "NSAccessibility", "NSApplication", "NSResponder", "NSWindow", "NSWorkspace"] }
objc2-foundation = { version = "0.3", default-features = false, features = ["std", "block2", "NSArray", "NSNotification", "NSOperation", "NSProcessInfo", "NSString", "NSURL"] }
block2 = "0.6"
# Native crash capture (src/native_crash_macos.rs): the signal handler, the faulting module
@@ -0,0 +1,63 @@
//! Cached accessibility signal, observed only while a theme offers blur. No UI-thread IO.
use std::sync::{
Arc,
atomic::{AtomicBool, AtomicU8, Ordering},
};
#[derive(Default)]
struct Reading {
needed: AtomicBool,
started: AtomicBool,
value: AtomicU8,
}
pub fn cached(ctx: &egui::Context) -> Box<dyn Fn(bool) -> Option<bool>> {
let state = Arc::new(Reading::default());
let ctx = ctx.clone();
Box::new(move |needed| {
state.needed.store(needed, Ordering::Relaxed);
if needed && !state.started.swap(true, Ordering::Relaxed) {
let weak = Arc::downgrade(&state);
let ctx = ctx.clone();
if let Err(error) = std::thread::Builder::new().name("desktop-transparency".into()).spawn(move || {
while let Some(state) = weak.upgrade() {
if state.needed.load(Ordering::Relaxed) {
let code = match read() {
Some(true) => 1,
Some(false) => 2,
None => 0,
};
if state.value.swap(code, Ordering::Relaxed) != code {
ctx.request_repaint();
}
}
drop(state);
std::thread::sleep(std::time::Duration::from_secs(5));
}
}) {
log::warn!("desktop transparency: could not observe accessibility setting: {error}");
}
}
match state.value.load(Ordering::Relaxed) {
1 => Some(true),
2 => Some(false),
_ => None,
}
})
}
#[cfg(target_os = "windows")]
fn read() -> Option<bool> {
let key =
winreg::RegKey::predef(winreg::enums::HKEY_CURRENT_USER).open_subkey(r"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize").ok()?;
match key.get_value::<u32, _>("EnableTransparency").ok()? {
0 => Some(true),
1 => Some(false),
_ => None,
}
}
#[cfg(target_os = "macos")]
fn read() -> Option<bool> {
objc2::rc::autoreleasepool(|_| Some(objc2_app_kit::NSWorkspace::sharedWorkspace().accessibilityDisplayShouldReduceTransparency()))
}
+6
View File
@@ -24,6 +24,8 @@ mod come_forward;
mod control_server;
#[cfg(target_os = "linux")]
mod desktop_theme;
#[cfg(any(target_os = "windows", target_os = "macos"))]
mod desktop_transparency;
mod device_lost;
#[cfg(target_os = "windows")]
mod native_crash;
@@ -539,6 +541,10 @@ fn services(cc: &eframe::CreationContext<'_>, folder: &DialogFolder) -> Services
desktop_dark: Some(desktop_theme::start(&cc.egui_ctx)),
#[cfg(not(target_os = "linux"))]
desktop_dark: None,
#[cfg(any(target_os = "windows", target_os = "macos"))]
reduce_transparency: Some(desktop_transparency::cached(&cc.egui_ctx)),
#[cfg(not(any(target_os = "windows", target_os = "macos")))]
reduce_transparency: None,
http_get: Some(std::sync::Arc::new(http_get)),
http_download: Some(std::sync::Arc::new(http_download)),
// Already an installed app: only the browser build offers to install.
+2 -2
View File
@@ -22,7 +22,7 @@ assets/
appearances/library.rs # built-in visual presets
materials/library.rs # physical defaults and colours
shaders/viewport.wgsl # original viewport shader
fonts/{liberation-sans,biz-udp-gothic}/ # unmodified fonts and OFL licences
fonts/<family>/ # unmodified fonts and OFL licences
locales/{en,de,ja,ko}.json # shared offline catalogs
platforms/{macos,linux,windows}/ # bundle, freedesktop and WiX templates
platforms/macos/dmg/ # disk image window: layout, dmgbuild settings, background art
@@ -79,7 +79,7 @@ The CLI still supports arbitrary PNG/ICO exports with `solidcraft-cli icon`.
| Linux, Nix, deb/rpm, tar, Flatpak, Snap | Copy canonical hicolor images; icon name stays `io.tangencylabs.solidcraft` |
| AppImage | Uses the Linux 256 px image and `.DirIcon` alias |
| FreeBSD | Shares Linux hicolor images and freedesktop metadata |
| Web | HTML/Trunk and `manifest.webmanifest` stay in `apps/solidcraft-web/`; copies Japanese font/licence to `fonts/`, uses the collected CSS, and copies the Linux 32/256/512 px images to `icons/` for the favicon and installed app |
| Web | HTML/Trunk and `manifest.webmanifest` stay in `apps/solidcraft-web/`; copies the Japanese font/licence to `fonts/` and the Noto Sans Arabic, KR, SC and TC fonts with their licences to `fonts/<family>/` (fetched when a drawing's text needs them), uses the collected CSS, and copies the Linux 32/256/512 px images to `icons/` for the favicon and installed app |
| Docs and link previews | `xtask book` copies the shared card to the existing `/solidcraft/og.png` public path |
## Code and provenance
+93
View File
@@ -0,0 +1,93 @@
Copyright 2022 The Noto Project Authors (https://github.com/notofonts/arabic)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://openfontlicense.org
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
Binary file not shown.
+92
View File
@@ -0,0 +1,92 @@
This Font Software is licensed under the SIL Open Font License,
Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font
creation efforts of academic and linguistic communities, and to
provide a free and open framework in which fonts may be shared and
improved in partnership with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply to
any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software
components as distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to,
deleting, or substituting -- in part or in whole -- any of the
components of the Original Version, by changing formats or by porting
the Font Software to a new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed,
modify, redistribute, and sell modified and unmodified copies of the
Font Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components, in
Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the
corresponding Copyright Holder. This restriction only applies to the
primary font name as presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created using
the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
Binary file not shown.
+92
View File
@@ -0,0 +1,92 @@
This Font Software is licensed under the SIL Open Font License,
Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font
creation efforts of academic and linguistic communities, and to
provide a free and open framework in which fonts may be shared and
improved in partnership with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply to
any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software
components as distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to,
deleting, or substituting -- in part or in whole -- any of the
components of the Original Version, by changing formats or by porting
the Font Software to a new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed,
modify, redistribute, and sell modified and unmodified copies of the
Font Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components, in
Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the
corresponding Copyright Holder. This restriction only applies to the
primary font name as presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created using
the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
Binary file not shown.
+92
View File
@@ -0,0 +1,92 @@
This Font Software is licensed under the SIL Open Font License,
Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font
creation efforts of academic and linguistic communities, and to
provide a free and open framework in which fonts may be shared and
improved in partnership with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply to
any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software
components as distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to,
deleting, or substituting -- in part or in whole -- any of the
components of the Original Version, by changing formats or by porting
the Font Software to a new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed,
modify, redistribute, and sell modified and unmodified copies of the
Font Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components, in
Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the
corresponding Copyright Holder. This restriction only applies to the
primary font name as presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created using
the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
+13 -5
View File
@@ -178,6 +178,7 @@ pattern_rect false
perforate false
perspective false
pipe false
placeholder true
plane false
plane_along_path false
plane_angle false
@@ -201,6 +202,18 @@ point_vertex false
polygon false
polygon_circumscribed false
polygon_edge false
prefs_appearance false
prefs_assistant false
prefs_design false
prefs_general false
prefs_graphics false
prefs_layout false
prefs_mouse3d false
prefs_navigation false
prefs_plugins false
prefs_shortcuts false
prefs_sketch false
prefs_tools false
presspull false
print_face false
project false
@@ -232,11 +245,6 @@ section false
select false
send false
settings false
settings_keyboard false
settings_monitor false
settings_mouse false
settings_palette false
settings_plug false
settings_profile false
sheet false
sheet_rules false
@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Appearance</title>
<path d="M32 7.5C18 7.5 7.5 18.5 7.5 32C7.5 45.5 18.5 56.5 32 56.5C37.5 56.5 40 53 38 49C36 45 38.5 42 43 42L47.5 42C52.5 42 56.5 38 56.5 33C56.5 18.5 46 7.5 32 7.5Z" fill="none" stroke="currentColor" stroke-width="6.8" stroke-linejoin="round" stroke-linecap="round"/><circle cx="20" cy="33" r="4" fill="currentColor"/><circle cx="28" cy="20" r="4" fill="currentColor"/><circle cx="42" cy="22" r="4" fill="currentColor"/>
</svg>

After

Width:  |  Height:  |  Size: 520 B

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Assistant</title>
<path d="M29 12.5Q30.2 33.8 51.5 35Q30.2 36.2 29 57.5Q27.8 36.2 6.5 35Q27.8 33.8 29 12.5Z" fill="none" stroke="currentColor" stroke-width="6.8" stroke-linejoin="round" stroke-linecap="round"/><path d="M50 6Q50.6 12.4 57 13Q50.6 13.6 50 20Q49.4 13.6 43 13Q49.4 12.4 50 6Z" fill="currentColor" stroke="currentColor" stroke-width="4.6" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 479 B

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Design</title>
<path d="M32 7.7L53.04 19.85L53.04 44.15L32 56.3L10.96 44.15L10.96 19.85Z" fill="none" stroke="currentColor" stroke-width="6.8" stroke-linejoin="round" stroke-linecap="round"/><path d="M10.96 19.85L32 32L53.04 19.85M32 32L32 56.3" fill="none" stroke="currentColor" stroke-width="6.8" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 427 B

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>General</title>
<path d="M27.24 14.85L27.15 7.07L36.85 7.07L36.76 14.85L44.48 19.3L51.17 15.34L56.02 23.73L49.23 27.54L49.23 36.46L56.02 40.27L51.17 48.66L44.48 44.7L36.76 49.15L36.85 56.93L27.15 56.93L27.24 49.15L19.52 44.7L12.83 48.66L7.98 40.27L14.77 36.46L14.77 27.54L7.98 23.73L12.83 15.34L19.52 19.3Z" fill="none" stroke="currentColor" stroke-width="6.8" stroke-linejoin="round" stroke-linecap="round"/><path d="M25 32A7 7 0 1 0 39 32A7 7 0 1 0 25 32Z" fill="none" stroke="currentColor" stroke-width="6.8" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 640 B

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Graphics</title>
<path d="M14 8L50 8A6 6 0 0 1 56 14L56 38A6 6 0 0 1 50 44L14 44A6 6 0 0 1 8 38L8 14A6 6 0 0 1 14 8ZM32 44L32 54M20 54L44 54" fill="none" stroke="currentColor" stroke-width="6.8" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 323 B

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Layout</title>
<path d="M14 10L50 10A6 6 0 0 1 56 16L56 48A6 6 0 0 1 50 54L14 54A6 6 0 0 1 8 48L8 16A6 6 0 0 1 14 10Z" fill="none" stroke="currentColor" stroke-width="6.8" stroke-linejoin="round" stroke-linecap="round"/><path d="M24 10L24 54M24 39L56 39" fill="none" stroke="currentColor" stroke-width="6.8" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 436 B

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>3D mouse</title>
<path d="M6.5 32A25.5 25.5 0 1 0 57.5 32A25.5 25.5 0 1 0 6.5 32Z" fill="none" stroke="currentColor" stroke-width="6.8" stroke-linejoin="round" stroke-linecap="round"/><path d="M24 32A8 8 0 1 0 40 32A8 8 0 1 0 24 32Z" fill="currentColor" stroke="currentColor" stroke-width="4.08" stroke-linejoin="round" stroke-linecap="round"/><path d="M53.4 32L45.96 35.9L45.96 28.1Z" fill="currentColor" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round" stroke-linecap="round"/><path d="M32 53.4L28.1 45.96L35.9 45.96Z" fill="currentColor" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round" stroke-linecap="round"/><path d="M10.6 32L18.04 28.1L18.04 35.9Z" fill="currentColor" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round" stroke-linecap="round"/><path d="M32 10.6L35.9 18.04L28.1 18.04Z" fill="currentColor" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Navigation</title>
<path d="M32 7A14 14 0 0 1 46 21L46 43A14 14 0 0 1 18 43L18 21A14 14 0 0 1 32 7Z" fill="none" stroke="currentColor" stroke-width="6.8" stroke-linejoin="round" stroke-linecap="round"/><path d="M18 38L46 38" fill="none" stroke="currentColor" stroke-width="6.8" stroke-linejoin="round" stroke-linecap="round"/><path d="M32 14L32 22" fill="none" stroke="currentColor" stroke-width="6.8" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 530 B

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Plugins</title>
<path d="M24 7L24 20M40 7L40 20M16 20L48 20L48 28A16 16 0 0 1 16 28ZM32 44L32 57" fill="none" stroke="currentColor" stroke-width="6.8" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 279 B

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Shortcuts</title>
<path d="M12 13L52 13A6 6 0 0 1 58 19L58 45A6 6 0 0 1 52 51L12 51A6 6 0 0 1 6 45L6 19A6 6 0 0 1 12 13Z" fill="none" stroke="currentColor" stroke-width="6.8" stroke-linejoin="round" stroke-linecap="round"/><circle cx="18" cy="26" r="3.6" fill="currentColor"/><circle cx="28" cy="26" r="3.6" fill="currentColor"/><circle cx="38" cy="26" r="3.6" fill="currentColor"/><circle cx="48" cy="26" r="3.6" fill="currentColor"/><path d="M20 40L44 40" fill="none" stroke="currentColor" stroke-width="6.8" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 639 B

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Sketch</title>
<path d="M7 57L25.53 51.48L12.52 38.47Z" fill="currentColor" stroke="currentColor" stroke-width="6.8" stroke-linejoin="round" stroke-linecap="round"/><path d="M7 57L25.53 51.48L53.81 23.2L40.8 10.19L12.52 38.47Z" fill="none" stroke="currentColor" stroke-width="6.8" stroke-linejoin="round" stroke-linecap="round"/><path d="M44.62 32.39L31.61 19.38" fill="none" stroke="currentColor" stroke-width="6.8" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 545 B

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Tools</title>
<path d="M56.86 18.45A16.5 16.5 0 0 1 37.12 39.04L22.33 53.83A8.6 8.6 0 0 1 10.17 41.67L24.96 26.88A16.5 16.5 0 0 1 45.55 7.14L37.55 15.14L39.94 24.06L48.86 26.45Z" fill="none" stroke="currentColor" stroke-width="6.8" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 360 B

@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Align drawing annotations</title>
<path d="M12 6L12 58" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M22.5 12L51.5 12A2.5 2.5 0 0 1 54 14.5L54 17.5A2.5 2.5 0 0 1 51.5 20L22.5 20A2.5 2.5 0 0 1 20 17.5L20 14.5A2.5 2.5 0 0 1 22.5 12Z" fill="#e2e8ef" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M22.5 28L40.5 28A2.5 2.5 0 0 1 43 30.5L43 33.5A2.5 2.5 0 0 1 40.5 36L22.5 36A2.5 2.5 0 0 1 20 33.5L20 30.5A2.5 2.5 0 0 1 22.5 28Z" fill="#e2e8ef" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M22.5 44L47.5 44A2.5 2.5 0 0 1 50 46.5L50 49.5A2.5 2.5 0 0 1 47.5 52L22.5 52A2.5 2.5 0 0 1 20 49.5L20 46.5A2.5 2.5 0 0 1 22.5 44Z" fill="#e2e8ef" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 962 B

@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Align drawing views</title>
<path d="M10.5 24L23.5 24A2.5 2.5 0 0 1 26 26.5L26 43.5A2.5 2.5 0 0 1 23.5 46L10.5 46A2.5 2.5 0 0 1 8 43.5L8 26.5A2.5 2.5 0 0 1 10.5 24Z" fill="#e2e8ef" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M39.5 12L52.5 12A2.5 2.5 0 0 1 55 14.5L55 43.5A2.5 2.5 0 0 1 52.5 46L39.5 46A2.5 2.5 0 0 1 37 43.5L37 14.5A2.5 2.5 0 0 1 39.5 12Z" fill="#e2e8ef" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M6 52L58 52" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M17 29L17 40M46 18L46 39" fill="none" stroke="#6f879c" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 846 B

@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Drawing balloon</title>
<path d="M9.5 37L29.5 37A2.5 2.5 0 0 1 32 39.5L32 52.5A2.5 2.5 0 0 1 29.5 55L9.5 55A2.5 2.5 0 0 1 7 52.5L7 39.5A2.5 2.5 0 0 1 9.5 37Z" fill="#e2e8ef" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M20 45L35 30L40 25" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M15 50L19 37L28 45Z" fill="#236ead"/>
<circle cx="45" cy="17" r="12" fill="#b9ddf8" stroke="#236ead" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 651 B

@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Base drawing view</title>
<path d="M9.5 10L54.5 10A2.5 2.5 0 0 1 57 12.5L57 51.5A2.5 2.5 0 0 1 54.5 54L9.5 54A2.5 2.5 0 0 1 7 51.5L7 12.5A2.5 2.5 0 0 1 9.5 10Z" fill="#e2e8ef" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M17.5 19L42.5 19A2.5 2.5 0 0 1 45 21.5L45 39.5A2.5 2.5 0 0 1 42.5 42L17.5 42A2.5 2.5 0 0 1 15 39.5L15 21.5A2.5 2.5 0 0 1 17.5 19Z" fill="#b9ddf8" stroke="#236ead" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<circle cx="30" cy="30.5" r="6" fill="none" stroke="#236ead" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M47 46L53 46M50 43L50 49" fill="none" stroke="#6f879c" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 851 B

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Drawing centre mark</title>
<circle cx="32" cy="32" r="18" fill="#e2e8ef" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M32 6L32 21M32 27L32 37M32 43L32 58M6 32L21 32M27 32L37 32M43 32L58 32" fill="none" stroke="#236ead" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 420 B

@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Check drawing</title>
<path d="M14.5 7L49.5 7A2.5 2.5 0 0 1 52 9.5L52 54.5A2.5 2.5 0 0 1 49.5 57L14.5 57A2.5 2.5 0 0 1 12 54.5L12 9.5A2.5 2.5 0 0 1 14.5 7Z" fill="#e2e8ef" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M21 19L42 19M21 29L35 29" fill="none" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M27 44L37 53L57 30" fill="none" stroke="#ffffff" stroke-width="6.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M27 44L37 53L57 30" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 730 B

@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Drawing datum</title>
<path d="M6 53L58 53" fill="none" stroke="#6f879c" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M32 52L24 38L40 38Z" fill="#236ead"/>
<path d="M32 38L32 27" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M21.5 7L42.5 7A2.5 2.5 0 0 1 45 9.5L45 24.5A2.5 2.5 0 0 1 42.5 27L21.5 27A2.5 2.5 0 0 1 19 24.5L19 9.5A2.5 2.5 0 0 1 21.5 7Z" fill="#b9ddf8" stroke="#236ead" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 631 B

@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Detail drawing view</title>
<path d="M9.5 11L30.5 11A2.5 2.5 0 0 1 33 13.5L33 45.5A2.5 2.5 0 0 1 30.5 48L9.5 48A2.5 2.5 0 0 1 7 45.5L7 13.5A2.5 2.5 0 0 1 9.5 11Z" fill="#e2e8ef" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M13 18L23 18L23 33L29 33" fill="none" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<circle cx="23" cy="30" r="9" fill="none" stroke="#6f879c" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M31 35L39 41" fill="none" stroke="#6f879c" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<circle cx="47" cy="46" r="12" fill="#b9ddf8" stroke="#236ead" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M40 39L46 39L46 48L53 48" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1000 B

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Drawing dimension</title>
<path d="M10 10L10 55M54 10L54 55" fill="none" stroke="#6f879c" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M10 14L54 14L54 36L10 36Z" fill="#e2e8ef" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M20 49L44 49" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M11 49L23 43L23 55Z" fill="#236ead"/>
<path d="M53 49L41 55L41 43Z" fill="#236ead"/>
</svg>

After

Width:  |  Height:  |  Size: 598 B

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Edit drawing table</title>
<path d="M10.5 10L53.5 10A2.5 2.5 0 0 1 56 12.5L56 51.5A2.5 2.5 0 0 1 53.5 54L10.5 54A2.5 2.5 0 0 1 8 51.5L8 12.5A2.5 2.5 0 0 1 10.5 10Z" fill="#e2e8ef" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M10 12L54 12L54 23L10 23Z" fill="#b8c3cf"/>
<path d="M8 24L56 24M8 39L56 39M23 24L23 54" fill="none" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M25 26L54 26L54 37L25 37Z" fill="#b9ddf8"/>
<path d="M32 32L47 32M42 27L47 32L42 37" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 741 B

@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Hole callout</title>
<circle cx="18" cy="43" r="13" fill="#e2e8ef" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<circle cx="18" cy="43" r="6" fill="none" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M27 35L39 22L57 22" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M23 40L27 28L34 35Z" fill="#236ead"/>
<circle cx="44" cy="10" r="4.5" fill="none" stroke="#236ead" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M39 16L49 4" fill="none" stroke="#236ead" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 794 B

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Insert drawing image</title>
<path d="M9.5 11L54.5 11A2.5 2.5 0 0 1 57 13.5L57 50.5A2.5 2.5 0 0 1 54.5 53L9.5 53A2.5 2.5 0 0 1 7 50.5L7 13.5A2.5 2.5 0 0 1 9.5 11Z" fill="#e2e8ef" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<circle cx="43" cy="23" r="5" fill="#b9ddf8" stroke="#236ead" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M10 45L24 27L36 41L42 34L54 47Z" fill="#b9ddf8" stroke="#236ead" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 622 B

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Drawing leader</title>
<path d="M8.5 34L33.5 34A2.5 2.5 0 0 1 36 36.5L36 51.5A2.5 2.5 0 0 1 33.5 54L8.5 54A2.5 2.5 0 0 1 6 51.5L6 36.5A2.5 2.5 0 0 1 8.5 34Z" fill="#e2e8ef" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M18 45L39 18L56 18" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M15 49L19 35L26 41Z" fill="#236ead"/>
</svg>

After

Width:  |  Height:  |  Size: 516 B

@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Drawing parts list</title>
<path d="M10.5 10L53.5 10A2.5 2.5 0 0 1 56 12.5L56 51.5A2.5 2.5 0 0 1 53.5 54L10.5 54A2.5 2.5 0 0 1 8 51.5L8 12.5A2.5 2.5 0 0 1 10.5 10Z" fill="#e2e8ef" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M10 12L54 12L54 23L10 23Z" fill="#b8c3cf"/>
<path d="M8 24L56 24M8 39L56 39M23 24L23 54" fill="none" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<circle cx="15.5" cy="31.5" r="3.5" fill="#b9ddf8" stroke="#236ead" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<circle cx="15.5" cy="46.5" r="3.5" fill="#b9ddf8" stroke="#236ead" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M30 31L48 31M30 46L48 46" fill="none" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 958 B

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Print drawing</title>
<path d="M18.5 7L45.5 7A2.5 2.5 0 0 1 48 9.5L48 29.5A2.5 2.5 0 0 1 45.5 32L18.5 32A2.5 2.5 0 0 1 16 29.5L16 9.5A2.5 2.5 0 0 1 18.5 7Z" fill="#e2e8ef" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M8.5 24L55.5 24A2.5 2.5 0 0 1 58 26.5L58 49.5A2.5 2.5 0 0 1 55.5 52L8.5 52A2.5 2.5 0 0 1 6 49.5L6 26.5A2.5 2.5 0 0 1 8.5 24Z" fill="#b8c3cf" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<circle cx="48" cy="32" r="2.5" fill="#236ead" stroke="#236ead" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M18.5 37L45.5 37A2.5 2.5 0 0 1 48 39.5L48 55.5A2.5 2.5 0 0 1 45.5 58L18.5 58A2.5 2.5 0 0 1 16 55.5L16 39.5A2.5 2.5 0 0 1 18.5 37Z" fill="#e2e8ef" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M23 44L41 44M23 51L36 51" fill="none" stroke="#236ead" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Projected drawing view</title>
<path d="M9.5 33L31.5 33A2.5 2.5 0 0 1 34 35.5L34 52.5A2.5 2.5 0 0 1 31.5 55L9.5 55A2.5 2.5 0 0 1 7 52.5L7 35.5A2.5 2.5 0 0 1 9.5 33Z" fill="#e2e8ef" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<circle cx="20.5" cy="44" r="5" fill="none" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M9.5 8L31.5 8A2.5 2.5 0 0 1 34 10.5L34 19.5A2.5 2.5 0 0 1 31.5 22L9.5 22A2.5 2.5 0 0 1 7 19.5L7 10.5A2.5 2.5 0 0 1 9.5 8Z" fill="#b9ddf8" stroke="#236ead" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M9 25L9 30M32 25L32 30M38 44L54 44" fill="none" stroke="#6f879c" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round" stroke-dasharray="3 5"/>
<path d="M44 28L44 13" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M44 7L48 14L40 14Z" fill="#236ead"/>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Browse drawing resources</title>
<path d="M15 7L51 7A3 3 0 0 1 54 10L54 48A3 3 0 0 1 51 51L15 51A3 3 0 0 1 12 48L12 10A3 3 0 0 1 15 7Z" fill="#b8c3cf" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M10 12L46 12A3 3 0 0 1 49 15L49 53A3 3 0 0 1 46 56L10 56A3 3 0 0 1 7 53L7 15A3 3 0 0 1 10 12Z" fill="#e2e8ef" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M14 20L42 20L42 47L14 47Z" fill="none" stroke="#6f879c" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M14 38L42 38M28 38L28 47" fill="none" stroke="#6f879c" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M22 27L34 27L34 36L22 36Z" fill="#b9ddf8" stroke="#236ead" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 933 B

@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Add drawing revision</title>
<path d="M14.5 7L49.5 7A2.5 2.5 0 0 1 52 9.5L52 54.5A2.5 2.5 0 0 1 49.5 57L14.5 57A2.5 2.5 0 0 1 12 54.5L12 9.5A2.5 2.5 0 0 1 14.5 7Z" fill="#e2e8ef" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M20 19L43 19M20 29L37 29" fill="none" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M43 33L58 57L28 57Z" fill="#ffffff" stroke="#ffffff" stroke-width="4.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M43 33L58 57L28 57Z" fill="#b9ddf8" stroke="#236ead" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 745 B

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Drawing revision table</title>
<path d="M10.5 10L53.5 10A2.5 2.5 0 0 1 56 12.5L56 51.5A2.5 2.5 0 0 1 53.5 54L10.5 54A2.5 2.5 0 0 1 8 51.5L8 12.5A2.5 2.5 0 0 1 10.5 10Z" fill="#e2e8ef" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M10 12L54 12L54 23L10 23Z" fill="#b8c3cf"/>
<path d="M8 24L56 24M8 39L56 39M23 24L23 54" fill="none" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M15 27L10 35L20 35ZM15 42L10 50L20 50Z" fill="#236ead"/>
<path d="M31 31L49 31M31 46L49 46" fill="none" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 752 B

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Section drawing view</title>
<path d="M8 15L56 15L56 50L37 50L37 27L27 27L27 50L8 50Z" fill="#e2e8ef" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M10 38L26 22M10 25L20 15M10 50L24 36M39 35L54 20M39 48L54 33M49 50L56 43" fill="none" stroke="#236ead" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 450 B

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Space drawing annotations</title>
<path d="M28.5 9L53.5 9A2.5 2.5 0 0 1 56 11.5L56 14.5A2.5 2.5 0 0 1 53.5 17L28.5 17A2.5 2.5 0 0 1 26 14.5L26 11.5A2.5 2.5 0 0 1 28.5 9Z" fill="#e2e8ef" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M28.5 28L53.5 28A2.5 2.5 0 0 1 56 30.5L56 33.5A2.5 2.5 0 0 1 53.5 36L28.5 36A2.5 2.5 0 0 1 26 33.5L26 30.5A2.5 2.5 0 0 1 28.5 28Z" fill="#e2e8ef" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M28.5 47L53.5 47A2.5 2.5 0 0 1 56 49.5L56 52.5A2.5 2.5 0 0 1 53.5 55L28.5 55A2.5 2.5 0 0 1 26 52.5L26 49.5A2.5 2.5 0 0 1 28.5 47Z" fill="#e2e8ef" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M15 19L15 25M15 38L15 44" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M15 14L10 21L20 21ZM15 30L10 23L20 23ZM15 33L10 40L20 40ZM15 49L10 42L20 42Z" fill="#236ead"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Update drawing</title>
<path d="M14.5 7L49.5 7A2.5 2.5 0 0 1 52 9.5L52 54.5A2.5 2.5 0 0 1 49.5 57L14.5 57A2.5 2.5 0 0 1 12 54.5L12 9.5A2.5 2.5 0 0 1 14.5 7Z" fill="#e2e8ef" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M22.5 16L41.5 16A2.5 2.5 0 0 1 44 18.5L44 28.5A2.5 2.5 0 0 1 41.5 31L22.5 31A2.5 2.5 0 0 1 20 28.5L20 18.5A2.5 2.5 0 0 1 22.5 16Z" fill="#b8c3cf" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M38 35A11 11 0 1 0 45 45" fill="none" stroke="#ffffff" stroke-width="6.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M38 35A11 11 0 1 0 45 45" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M44 40L32 37L40 28Z" fill="#236ead"/>
</svg>

After

Width:  |  Height:  |  Size: 900 B

+15 -8
View File
@@ -1,7 +1,8 @@
//! Navigation drawing definitions. Camera projection and picking remain in the UI widgets.
use crate::theme::Tokens;
use egui::{Color32, FontId, Pos2, Rect, Shape, Stroke, pos2, vec2};
use solidcraft_engine::geom::{Ring, Vec3};
use solidcore_record::Vec3;
use solidcraft_engine::geom::Ring;
pub(crate) struct Facet {
pub(crate) direction: Vec3,
pub(crate) points: Ring<Pos2>,
@@ -151,6 +152,12 @@ pub(crate) fn curved_arrow(painter: &egui::Painter, at: Pos2, clockwise: bool, h
/// The pointer target around a compass end: larger than the drawn disc, so it is easy to hit.
pub(crate) const AXIS_RADIUS: f32 = 15.0;
pub(crate) fn axis_tip(painter: &egui::Painter, c: Pos2, name: &str, d: &Vec3, at: Pos2, depth: f64, hovered: bool, t: Tokens) {
axis_tip_scaled(painter, c, name, d, at, depth, hovered, t, 1.0);
}
/// The same compass artwork at a uniform scale, including its lettering and strokes.
#[allow(clippy::too_many_arguments)]
pub(crate) fn axis_tip_scaled(painter: &egui::Painter, c: Pos2, name: &str, d: &Vec3, at: Pos2, depth: f64, hovered: bool, t: Tokens, scale: f32) {
let col = if d.x != 0.0 {
t.axis_x
} else if d.y != 0.0 {
@@ -163,11 +170,11 @@ pub(crate) fn axis_tip(painter: &egui::Painter, c: Pos2, name: &str, d: &Vec3, a
// Positive shafts are the full axis colour; negative ones are a dimmed tint (and any end
// pointing away from the viewer dims further).
let ink = if !positive || depth < -0.01 { col.gamma_multiply(0.55) } else { col };
let radius = if positive { 11.0 } else { 4.8 };
let radius = if positive { 11.0 } else { 4.8 } * scale;
let direction = (at - c).normalized();
let width = 3.6;
let width = 3.6 * scale;
// A negative shaft stops at the outer edge of its ring; a positive one runs under its disc.
let end = if positive { at - direction * radius } else { at - direction * (radius + 1.2) };
let end = if positive { at - direction * radius } else { at - direction * (radius + 1.2 * scale) };
painter.line_segment([c, end], Stroke::new(width, ink));
// The round cap sits at the origin; the other end is covered by the disc or ring.
painter.circle_filled(c, width / 2.0, ink);
@@ -179,18 +186,18 @@ pub(crate) fn axis_tip(painter: &egui::Painter, c: Pos2, name: &str, d: &Vec3, a
// kind turns fully white.
if hovered {
// Same outer size as the resting tip: a ring's outline is half its stroke outside.
painter.circle_filled(at, if positive { radius } else { radius + 1.2 }, t.on_accent);
painter.circle_filled(at, if positive { radius } else { radius + 1.2 * scale }, t.on_accent);
// On the light theme a white disc vanishes into the compass, so it carries an outline in
// the axis colour, drawn inside the disc so it does not grow.
if light && positive {
painter.circle_stroke(at, radius - 1.0, Stroke::new(2.0, col));
painter.circle_stroke(at, radius - scale, Stroke::new(2.0 * scale, col));
}
}
if positive {
// On a hovered (white) disc the letter takes the axis colour.
let letter = if hovered { col } else { t.on_accent };
let label = name.to_uppercase();
let galley = painter.layout_no_wrap(label, crate::fonts::bold(crate::theme::Tokens::get().font_compass), letter);
let galley = painter.layout_no_wrap(label, crate::fonts::bold(crate::theme::Tokens::get().font_compass * scale), letter);
let origin = at - galley.size() / 2.0;
let ppp = painter.ctx().pixels_per_point().max(0.5);
let origin = pos2((origin.x * ppp).round() / ppp, (origin.y * ppp).round() / ppp);
@@ -201,7 +208,7 @@ pub(crate) fn axis_tip(painter: &egui::Painter, c: Pos2, name: &str, d: &Vec3, a
at,
radius,
Stroke::new(
2.4,
2.4 * scale,
if !hovered {
ink
} else if light {
+242 -66
View File
@@ -379,7 +379,7 @@
"Move Type": "نوع التحريك",
"Move down": "نقل للأسفل",
"Move earlier": "نقل إلى الأقدم",
"Move holes by clicking or dragging": "تحريك الثقوب بالنقر أو السحب",
"Move holes by clicking or dragging": "التحريك بالنقر أو السحب",
"Move later": "نقل إلى الأحدث",
"Move up": "نقل للأعلى",
"Name": "الاسم",
@@ -585,7 +585,7 @@
"Show ghost": "إظهار الطيف",
"Show in folder": "إظهار في المجلد",
"Show the pointer an AI agent moves (#35)": "إظهار المؤشر الذي يحرّكه وكيل الذكاء الاصطناعي (#35)",
"Show tool at cursor": "إظهار الأداة عند المؤشر",
"Show tool at cursor": "الأداة عند المؤشر",
"Signed axes": "محاور بإشارة",
"Size": "الحجم",
"Sketch constraints": "قيود الرسم",
@@ -596,7 +596,7 @@
"Sketch points": "نقاط الرسم",
"Slider": "منزلق",
"Slot width": "عرض الشق",
"Snap to face and edge centres": "الالتقاط إلى مراكز الأوجه والحواف",
"Snap to face and edge centres": "الالتقاط إلى المراكز",
"SolidCraft cannot submit reports directly. Submit on GitHub; no token is needed in the app.": "لا يستطيع SolidCraft إرسال التقارير مباشرةً. أرسلها على GitHub؛ لا حاجة إلى رمز وصول في التطبيق.",
"SolidCraft closed before these designs were saved. Their last autosave can be opened.": "أُغلق SolidCraft قبل حفظ هذه التصاميم. يمكن فتح آخر حفظ تلقائي لها.",
"Sort A to Z": "فرز تصاعدي",
@@ -739,34 +739,34 @@
"a picked edge's body is gone": "جسم الحافة المحددة لم يعد موجودًا",
"add a key for a joint first": "أضف أولًا مفتاحًا لمفصل",
"ai.connected_toast": "أصبح SolidCraft متاحًا في {clients}. أعد تشغيلها لاستخدامه (التفضيلات > المساعد).",
"ai.prefs.allow_control": "السماح لوكلاء الذكاء الاصطناعي بالاتصال (قناة التحكم / MCP)",
"ai.prefs.allow_control_note": "يفتح قناة تحكم على هذا الحاسوب فقط (127.0.0.1، المنفذ 27878 أو أول منفذ متاح بعده). على البرنامج الذي يتصل أن يقدّم أيضًا رمزًا سريًا لا يقرؤه إلا حساب المستخدم الخاص بك، ويُنشأ من جديد عند كل تشغيل. يُطبّق في المرة القادمة التي يبدأ فيها SolidCraft. عند الإيقاف: لا شيء يستمع ما لم تشغّل SolidCraft مع --control.",
"ai.prefs.availability": "عناصر تحكم المساعد",
"ai.prefs.background_commands": "تشغيل أوامر الوكيل في الخلفية",
"ai.prefs.connect_apps": "إتاحة SolidCraft لتطبيقات الذكاء الاصطناعي على هذا الحاسوب",
"ai.prefs.connect_apps_note": "عند بدء تشغيل SolidCraft، يُضاف كخادم MCP إلى تطبيقات الذكاء الاصطناعي الموجودة على هذا الحاسوب (OpenCode وClaude Desktop وClaude Code وCursor وCodex وGemini CLI وCopilot CLI وWindsurf وZed وVS Code)، ليتمكن الوكيل من استخدامه دون أي إعداد. لا يُضاف إلا مُدخل solidcraft أو يُحدَّث مساره؛ ويُترك المُدخل الذي غيّرته كما هو، ويُنسخ الملف احتياطيًا أولًا. عند الإيقاف تُزال المُدخلات التي أضافها. الأمر «المساعدة > ربط مساعدي الذكاء الاصطناعي…» يفعل ذلك يدويًا.",
"ai.prefs.connect_at_start": "الاتصال بآخر وكيل مستخدم عند بدء SolidCraft",
"ai.prefs.allow_control": "السماح لوكلاء الذكاء الاصطناعي بالاتصال",
"ai.prefs.allow_control_note": "قناة محلية (MCP). لا شيء يغادر هذا الحاسوب.\n\nيفتح قناة تحكم على هذا الحاسوب فقط (127.0.0.1، المنفذ 27878 أو أول منفذ متاح بعده). على البرنامج الذي يتصل أن يقدّم أيضًا رمزًا سريًا لا يقرؤه إلا حساب المستخدم الخاص بك، ويُنشأ من جديد عند كل تشغيل. يُطبّق في المرة القادمة التي يبدأ فيها SolidCraft. عند الإيقاف: لا شيء يستمع ما لم تشغّل SolidCraft مع --control.",
"ai.prefs.availability": "التوفر",
"ai.prefs.background_commands": "تشغيل الأوامر البطيئة في الخلفية",
"ai.prefs.connect_apps": "إضافة SolidCraft إلى تطبيقات الذكاء الاصطناعي",
"ai.prefs.connect_apps_note": "إيقافه يزيل الإدخالات مجددًا.\n\nعند بدء تشغيل SolidCraft، يُضاف كخادم MCP إلى تطبيقات الذكاء الاصطناعي الموجودة على هذا الحاسوب (OpenCode وClaude Desktop وClaude Code وCursor وCodex وGemini CLI وCopilot CLI وWindsurf وZed وVS Code)، ليتمكن الوكيل من استخدامه دون أي إعداد. لا يُضاف إلا مُدخل solidcraft أو يُحدَّث مساره؛ ويُترك المُدخل الذي غيّرته كما هو، ويُنسخ الملف احتياطيًا أولًا. عند الإيقاف تُزال المُدخلات التي أضافها. الأمر «المساعدة > ربط مساعدي الذكاء الاصطناعي…» يفعل ذلك يدويًا.",
"ai.prefs.connect_at_start": "توصيل آخر وكيل عند البدء",
"ai.prefs.connect_at_start_note": "يبدأ في الخلفية الوكيل الذي اتصلت به آخر مرة (أو الوكيل الافتراضي) في كل مرة يبدأ فيها SolidCraft، ليكون جاهزًا للمحادثة. لا يحدث ذلك عند بدء SolidCraft مع --control. إذا تعذّر الاتصال، يعرض المساعد «فشل الاتصال» وزر «إعادة المحاولة».",
"ai.prefs.follow_camera": "توجيه الكاميرا نحو الرسومات",
"ai.prefs.follow_camera": "تتبّع الكاميرا",
"ai.prefs.follow_camera_off": "تتبّع الكاميرا التلقائي متوقف.",
"ai.prefs.follow_camera_on": "تتجه الكاميرا نحو الرسومات التي ينشئها الوكيل.",
"ai.prefs.intro": "الوكلاء والأذونات وطريقة ظهور عملهم.",
"ai.prefs.make_default": "الاستخدام افتراضيًا",
"ai.prefs.intro": "وكلاء الذكاء الاصطناعي في SolidCraft.",
"ai.prefs.make_default": "تعيين افتراضيًا",
"ai.prefs.no_agents": "لا يوجد وكلاء بعد. أضف وكيلًا أو حدّث القائمة للعثور على الوكلاء المثبّتين.",
"ai.prefs.no_saved_approvals": "لا توجد موافقات محفوظة.",
"ai.prefs.open_at_start": "فتح لوحة المساعد عند بدء SolidCraft",
"ai.prefs.open_at_start": "فتح المحادثة عند البدء",
"ai.prefs.open_at_start_note": "يعرض لوحة المساعد (أو نافذتها المستقلة) في كل مرة يبدأ فيها SolidCraft. لا يحدث ذلك عند بدء SolidCraft مع --control.",
"ai.prefs.override_all_warning": "قد يحفظ هذا الوكيل التصميم أو يكتب فوقه أو يصدّره أو يستبدله دون أن يسأل.",
"ai.prefs.permission_all": "السماح بكل شيء",
"ai.prefs.permission_all_summary": "تعمل أدوات SolidCraft المتاحة دون سؤال.",
"ai.prefs.permission_ask": "السؤال قبل التغييرات",
"ai.prefs.permission_ask_summary": "الموافقة على كل تغيير في التصميم وكل إجراء على الملفات.",
"ai.prefs.permission_edits": "السماح بتعديلات النموذج",
"ai.prefs.permission_edits_summary": "تُنفَّذ تعديلات النموذج، وتبقى إجراءات الملفات والإجراءات المدمّرة تتطلب السؤال.",
"ai.prefs.permission_all": "تنفيذ كل شيء",
"ai.prefs.permission_all_summary": "لا يسأل أبدًا",
"ai.prefs.permission_ask": "السؤال أولًا",
"ai.prefs.permission_ask_summary": "يسأل عن كل تغيير",
"ai.prefs.permission_edits": "إجراء تعديلات",
"ai.prefs.permission_edits_summary": "يعدّل بحرية ويسأل عن الباقي",
"ai.prefs.permission_scope": "لأدوات SolidCraft فقط. أوامر الطرفية وتعديلات الملفات لدى الوكيل تتبع أذوناته الخاصة.",
"ai.prefs.pointer_preview_hidden": "المؤشر مخفي",
"ai.prefs.pointer_preview_speed": "مؤشر الوكيل · {speed}",
"ai.prefs.pointer_speed": "سرعة الحركة",
"ai.prefs.pointer_speed": "السرعة",
"ai.prefs.privacy_note": "لا يستلم المحادثة إلا الوكيل أو نقطة الاتصال التي تختارها. تُخزَّن مفاتيح نقاط الاتصال في سلسلة مفاتيح النظام.",
"ai.prefs.show_pointer": "إظهار مؤشر الوكيل",
"ai.prefs.turn_limit_hint": "الحد الأقصى لاستدعاءات الأدوات التي تغيّر التصميم في الرسالة الواحدة.",
@@ -997,6 +997,11 @@
"cmd.joint_screw": "لولب",
"cmd.joint_solve": "حل المفاصل",
"cmd.joint_sweep": "مفصل كنس",
"cmd.layout.apply": "تطبيق التخطيط",
"cmd.layout.get": "فحص التخطيط",
"cmd.layout.reset": "إعادة ضبط التخطيط",
"cmd.layout.save": "حفظ التخطيط",
"cmd.layout.workspace.select": "اختيار مساحة عمل التخطيط",
"cmd.library_apply": "تطبيق عنصر من المكتبة المحلية",
"cmd.library_export": "تصدير مكتبة التصميم",
"cmd.library_inspect": "فحص المكتبة المحلية",
@@ -1030,6 +1035,21 @@
"cmd.occurrence_move": "تحريك النسخة",
"cmd.occurrence_properties": "الخصائص الفيزيائية للنسخة",
"cmd.occurrence_reparent": "نقل إلى مكوّن",
"cmd.panel.activate": "تنشيط علامة تبويب اللوحة",
"cmd.panel.close": "إغلاق اللوحة",
"cmd.panel.collapse": "طي قسم اللوحة",
"cmd.panel.dismiss": "إغلاق العرض المؤقت للوحة",
"cmd.panel.float": "تعويم اللوحة",
"cmd.panel.focus": "تركيز اللوحة",
"cmd.panel.list": "سرد اللوحات",
"cmd.panel.move": "نقل اللوحة",
"cmd.panel.native_close": "إعادة إرساء قسم اللوحة من نافذته المستقلة",
"cmd.panel.open": "فتح اللوحة",
"cmd.panel.peek": "عرض اللوحة المطوية مؤقتًا",
"cmd.panel.popout": "نقل اللوحة إلى نافذة مستقلة",
"cmd.panel.reset_pair": "إعادة ضبط فاصل اللوحة",
"cmd.panel.resize": "تغيير حجم قسم اللوحة",
"cmd.panel.resize_pair": "تحريك فاصل اللوحة",
"cmd.parameters_add": "إضافة معامل مستخدم",
"cmd.parameters_change": "تغيير المعاملات",
"cmd.parameters_comment": "التعليق على المعامل",
@@ -1376,6 +1396,9 @@
"dialog.custom_axis": "↖ محور مخصص",
"dialog.dimension_insert_hint": "انقر على بُعد قائد آخر لإدراج اسمه",
"dialog.dimension_name": "البُعد: {name}",
"dialog.dimension_step_down": "إنقاص بمقدار {step}",
"dialog.dimension_step_expression": "يتغير الرقم فقط بالخطوات؛ يبقى التعبير كما كُتب",
"dialog.dimension_step_up": "زيادة بمقدار {step}",
"dialog.edit_title": "تعديل {title}",
"dialog.no_shown_body": "لا يوجد جسم ظاهر لتغييره: أظهر جسمًا، أو اختر «جسم جديد»",
"dialog.pattern_sketch_not_object": "اختر أجسامًا أو ميزات: الرسم ليس أيًّا منهما. كرّر منحنياته بأداة النمط أو المرآة داخل الرسم، أو ابثقه أولًا.",
@@ -1383,6 +1406,7 @@
"dialog.selected_count": "↖ {count} محدد",
"dialog.selection_constraint": "قيد {id}",
"dialog.selection_external_curve": "{sketch} · منحنى {id}",
"dialog.selection_external_point": "{point} · {sketch}",
"dialog.selection_guidance": "حدد مُدخلًا لتغييره. انقر على العناصر المحددة لإزالتها؛ Ctrl/Cmd مع النقر يبدّل تحديدها أيضًا. راجع المعاينة ثم اضغط موافق.",
"dialog.selection_midpoint": "نقطة المنتصف · {id}",
"dialog.selection_profile": "{sketch} · مقطع {number}",
@@ -1400,6 +1424,7 @@
"document.read_only_save": "فُتح هذا التصميم للقراءة فقط: احفظه كملف جديد",
"document.save_changes": "حفظ التغييرات على {name}؟",
"document.save_copy": "حفظ نسخة…",
"document.tab_unsaved_changes": "تغييرات غير محفوظة",
"document.unsaved_changes": "في {name} تغييرات غير محفوظة. إن لم تحفظها فستضيع.",
"drive.rigid": "{joint} مفصل صلب: يثبّت أجزاءه في مكانها، لذا لا يوجد ما يمكن تحريكه. لتتحرك الأجزاء، غيّر نوعه في «تعديل المفصل»: «دوراني» للدوران، «منزلق» للانزلاق، «أسطواني» لكليهما.",
"drive.stopped_by_contact": "توقف عند {value}: سيخترق {a} الجسم {b}",
@@ -1536,6 +1561,11 @@
"help.joint.reach": "يحرّك نقطة على قطعة نحو هدف بتغيير مفاصل سلسلتها ضمن حدودها. استخدمه لضبط وضعية آلية حسب الموضع المطلوب لنهايتها، مثل طرف ذراع روبوت.",
"help.joint.rigid_group": "يثبّت القطع المحددة معًا كما هي لتتحرك كقطعة واحدة. استخدمه للقطع التي تنتمي إلى وحدة واحدة ولا تتحرك بالنسبة إلى بعضها.",
"help.joint.screw": "يربط الدوران والانزلاق بخطوة لولبية، بحيث تتقدم القطعة بمقدار تلك المسافة في كل دورة. استخدمه للوالب القيادة والبراغي والمحاور الملولبة.",
"help.layout.apply": "تطبيق تخطيط محفوظ على مساحة العمل الحالية. تحتفظ أدوات التحرير القائمة بمسوداتها.",
"help.layout.get": "قراءة التخطيط المعتمد وعرض مساحات العمل المسجلة.",
"help.layout.reset": "استعادة التخطيط الافتراضي المسجل لمساحة العمل الحالية. تبقى إعدادات المستخدم المسبقة متاحة.",
"help.layout.save": "حفظ التخطيط الكامل باسم محدد. يتطلب استبدال إعداد مسبق للمستخدم إذنًا صريحًا بالكتابة فوقه.",
"help.layout.workspace.select": "اختيار مساحة عمل تخطيط مسجلة وتذكر التخطيط السابق.",
"help.library.apply": "يطبّق مادة أو مظهرًا من ملف مكتبة محلي على أجسام أو أوجه أو مكوّنات. استخدمه لإعادة استخدام مواد حفظتها من تصميم آخر.",
"help.library.export": "يحفظ مواد التصميم ومظاهره في ملف مكتبة. استخدمه لمشاركتها أو إعادة استخدامها في تصميمات أخرى.",
"help.library.inspect": "يفحص ملف مكتبة ويسرد موادّه ومظاهره دون تغيير التصميم. استخدمه لمعرفة ما تحويه المكتبة قبل تطبيقها.",
@@ -1549,6 +1579,21 @@
"help.notice": "NOTICE",
"help.occurrence.ground": "يثبّت قطعة في موضعها أو يحررها مجددًا، بحيث تحرّك المفاصل القطع الأخرى حولها. استخدمه للإطار أو القاعدة التي يُبنى عليها باقي التجميع.",
"help.occurrence.move": "يحرّك جزءًا في التجميع فقط ضمن درجات الحرية التي تسمح بها مفاصله. حدد وجهًا للجزء، ثم اسحب سهمًا أو قوسًا متاحًا.",
"help.panel.activate": "اختيار علامة تبويب لوحة مفتوحة دون طلب تركيز لوحة المفاتيح.",
"help.panel.close": "إغلاق اللوحة بعد التحقق من دورة حياة محتواها. يُحفظ موضعها.",
"help.panel.collapse": "طي قسم اللوحة أو توسيعه مع الاحتفاظ بأبعاده المطلوبة.",
"help.panel.dismiss": "إغلاق العرض المؤقت للوحة مطوية.",
"help.panel.float": "نقل لوحة إلى قسم عائم ذي موضع محدد صراحةً.",
"help.panel.focus": "تركيز لوحة وإعادة فتح حالتها المحفوظة وتوسيع قسمها عند الحاجة.",
"help.panel.list": "قراءة أنواع اللوحات المسجلة والمثيلات المعتمدة وعلامات التبويب النشطة والتوافر وإمكانات المضيف.",
"help.panel.move": "نقل لوحة إلى قسم جديد أو ضمها إلى مجموعة علامات تبويب موجودة.",
"help.panel.native_close": "إعادة قسم لوحة من نافذته المستقلة إلى موضع الإرساء المحفوظ دون إلغاء محتواه.",
"help.panel.open": "فتح نوع لوحة بمعرّف مثيل صريح. يتطلب المثيل الجديد تحديد موضعه.",
"help.panel.peek": "إظهار لوحة مطوية مؤقتًا دون تغيير حجمها المحفوظ أو حالة طيها.",
"help.panel.popout": "نقل لوحة إلى نافذة مستقلة عند دعم ذلك. يستخدم مضيف المتصفح قسمًا عائمًا.",
"help.panel.reset_pair": "استعادة نسبة الحجم الافتراضية لقسمي إرساء متجاورين ضمن حدودهما المشتركة.",
"help.panel.resize": "تعيين العرض والارتفاع الخارجيين لقسم اللوحة ضمن حدود جميع علامات التبويب.",
"help.panel.resize_pair": "تغيير حجم قسمي إرساء متجاورين معًا مع الحفاظ على امتدادهما الكلي المشترك.",
"help.parameters.change": "يفتح جدول القيم المسماة والتعبيرات التي تتحكم في أبعاد التصميم. استخدمه لتغيير مقاس في مكان واحد فتتبعه كل الميزات التي تستخدمه.",
"help.parameters.export": "يحفظ معاملات التصميم بصيغة CSV أو JSON. استخدمه لتعديلها في جدول بيانات أو نقلها إلى تصميم آخر.",
"help.parameters.graph": "يبيّن أي المعاملات والميزات تستخدم أيًّا من غيرها، وأي حلقات أو أخطاء بينها. استخدمه لمعرفة ما سيتأثر بتغيير معامل.",
@@ -1878,28 +1923,28 @@
"joint.limit_loop": "{side} {value}: لا يمكن لحلقة مغلقة أن تنغلق بعده",
"joint.limit_range": "{side}: لا تلامس ضمن {range}، تُرك كما كان",
"joint.limit_turn": "{side} {value}: لا تلامس ضمن دورة واحدة",
"joint.limits_overlapping": "متداخلة بالفعل هنا: {pairs}. لا توقف البحث إلا إذا تداخلت أعمق.",
"joint.limits_overlapping": "متداخلة بالفعل هنا: {pairs}. لا توقف البحث إلا إذا ازداد تداخلها.",
"joint.one_body": "كلا الاختيارين على {body}. اختر الثاني على قطعة أخرى.",
"joint.one_component": "هذه القطع أجسام لمكوّن واحد: {bodies}. المفصل يحرّك مكوّنًا مقابل آخر، لذا تحتاج كل قطعة إلى مكوّن خاص بها.",
"joint.rigid_group_one_component": "هذه الأجسام وعددها {count} كلها في مكوّن واحد (أو في التصميم نفسه): استخدم {command} أولًا.",
"joint.rigid_group_select_components": "اختر مكوّنين أو أكثر.",
"joint_move.blocked_hint": "تحقق من نوع المفصل (مثل المفصل الثابت)، وتثبيت الجزء، وحدود المفصل، والقيم التي تتحكم بها المعاملات.",
"joint_move.blocked_title": "لا يمكن تحريك هذا الجزء",
"joint_move.coarse": "خطوة الدوران قرب المحور (°)",
"joint_move.coarse": "الخطوة قرب المحور",
"joint_move.conflict": "يتعارض أحد مفاصل هذا الجزء مع المفاصل الأخرى: {joint}.",
"joint_move.conflict_hint": "عدّل المفصل المتعارض أو عطّله أو احذفه حتى تتمكن المفاصل الأخرى من وضع الجزء.",
"joint_move.distance": "المسافة من نقطة البداية",
"joint_move.done": "تم",
"joint_move.fine": "خطوة الدوران بعيدًا عن المحور (°)",
"joint_move.fine": "الخطوة بعيدًا عن المحور",
"joint_move.fixed": "لا يملك هذا الجزء محور مفصل قابلًا للحركة.",
"joint_move.help": "تحريك — اسحب سهمًا أو قوسًا. يلغى السحب الحالي بمفتاح Esc.",
"joint_move.legacy_hint": "أوقف هذا الخيار لاستخدام لوحة التحريك السابقة.",
"joint_move.precision": "اسحب خارج الدليل المتقطع لضبط الدوران بدقة.",
"joint_move.legacy_hint": "عند الإيقاف: لوحة التحريك السابقة.",
"joint_move.precision": "اسحب خارج الدليل المتقطع للخطوة الدقيقة.",
"joint_move.rotation": "الدوران من نقطة البداية",
"joint_move.select": "اختر وجهًا أو جزءًا واحدًا من التجميع، ثم اختر تحريك.",
"joint_move.settings": "تحريك التجميع",
"joint_move.step": "الخطوة",
"joint_move.use_handles": "استخدام مقابض حركة المفاصل لتحريك التجميع",
"joint_move.use_handles": "استخدام مقابض حركة المفاصل للتحريك",
"keymap.command_id": "معرّف الأمر",
"keymap.empty": "لا توجد أوامر مطابقة",
"keymap.use_anyway": "استخدام {key} هنا على أي حال",
@@ -1959,6 +2004,7 @@
"material.selection.mixed": "مواد مختلفة",
"measure.at": "عند",
"measure.item": "العنصر {number}",
"measure.line_to_centre": "من الخط إلى المركز",
"measure.quick_copied": "تم نسخ القياس",
"measure.quick_copy": "انقر لنسخ القياس",
"measure.quick_edges": "{count} حواف",
@@ -2017,6 +2063,12 @@
"navbar.assistant": "المساعد",
"navbar.custom.add_slot": "إضافة {slot}",
"navbar.custom.always": "ظاهر دائمًا",
"navbar.custom.anchor.bottom_centre": "أسفل الوسط",
"navbar.custom.anchor.bottom_left": "أسفل اليسار",
"navbar.custom.anchor.bottom_right": "أسفل اليمين",
"navbar.custom.anchor.top_centre": "أعلى الوسط",
"navbar.custom.anchor.top_left": "أعلى اليسار",
"navbar.custom.anchor.top_right": "أعلى اليمين",
"navbar.custom.available": "{available} · اسحبها إلى الشريط، أو اضغط إضافة",
"navbar.custom.buttons": "الأزرار",
"navbar.custom.compact": "مضغوط",
@@ -2053,6 +2105,7 @@
"navbar.custom.reset_default": "إعادة الضبط إلى الافتراضي",
"navbar.custom.right": "نقل إلى اليمين",
"navbar.custom.select": "لم يتم تحديد أي خانة. حدّد خانة على الشريط لنقلها أو إزالتها أو تغيير مظهرها.",
"navbar.custom.separator_hint": "فاصل بين مجموعات الأزرار. اسحبه إلى الشريط، أو اضغط على إضافة؛ يمكنك استخدام أي عدد من الفواصل.",
"navbar.custom.separators_full": "يحتوي الشريط بالفعل على أربعة فواصل.",
"navbar.custom.show": "إظهار شريط التنقل",
"navbar.custom.shows": "يعرض",
@@ -2193,7 +2246,7 @@
"prefs.anti_aliasing_amd_dx12": "يستخدم تنعيم الحواف 1× لأن فحص بدء التشغيل لم يرسم شيئًا مع أخذ العينات المتعددة ونجح عند 1×. يمكنك اختيار إعداد آخر أعلاه؛ أعد التشغيل لتطبيقه إن نجح الفحص.",
"prefs.anti_aliasing_note": "ينعّم الحواف المسننة.",
"prefs.appearance.colour_vision_help": "يغيّر الألوان ذات الدلالة: حالات الرسم والمحاور والثنيات والتحليلات والأيقونات.",
"prefs.appearance.desktop_only": "لسطح المكتب فقط",
"prefs.appearance.desktop_only": "لتطبيق سطح المكتب فقط",
"prefs.appearance.follow_help": "يعيد التحميل عندما تعيد أداة أخرى كتابة الملف.",
"prefs.appearance.interface_scale": "مقياس الواجهة",
"prefs.appearance.next_themes": "السمات التالية",
@@ -2203,17 +2256,33 @@
"prefs.appearance.theme_count": "{count} سمات",
"prefs.appearance.theme_own": "ألوان السمة",
"prefs.appearance.viewport_colours": "ألوان العرض ثلاثي الأبعاد",
"prefs.appearance.viewport_logo": "منفذ العرض والشعار",
"prefs.appearance.viewport_logo": "منطقة العرض والشعار",
"prefs.applies_on_restart": "يُطبَّق عند إعادة التشغيل",
"prefs.assistant": "المساعد",
"prefs.assistant_connecting": "الاتصال",
"prefs.assistant_follow_help": "النظر إلى الرسومات يحرّك العرض مع المؤشر.",
"prefs.assistant_limit": "الحد",
"prefs.assistant_limited": "محدود",
"prefs.assistant_pointer_speed": "سرعة المؤشر",
"prefs.assistant_seconds": "ثوانٍ",
"prefs.assistant_seconds_aria": "الثواني",
"prefs.assistant_use_care": "استخدم بحذر",
"prefs.auto_operation": "اختيار العملية تلقائيًا",
"prefs.auto_operation_note": "تختار نوافذ الميزات «ضم» أو «جسم جديد» أو «قص» بحسب ما تلامسه المادة الجديدة، إلى أن تختار عملية بنفسك. عند الإيقاف: تحتفظ بالعملية التي تُفتح بها.",
"prefs.auto_operation_note": "ضم أو جسم جديد أو قص، حسب ما تلامسه المادة.",
"prefs.axes": "المحاور",
"prefs.axis": "المحور",
"prefs.blur_cost": "تكلفة العرض النسبية. زيادة الأشرطة تعني عادةً مزيدًا من العمل.",
"prefs.blur_device": "غير متاح على جهاز الرسوميات أو مسار العرض هذا",
"prefs.blur_guard": "خُفّضت جودة التمويه للحفاظ على استجابة الإطارات. لم يتغير نصف القطر.",
"prefs.blur_off": "إيقاف",
"prefs.blur_reduce_transparency": "معطّل بسبب إعداد تقليل الشفافية في النظام",
"prefs.blur_software": "غير متاح مع الرسوميات البرمجية (gpu-software)",
"prefs.blur_source": "غير متاح في عرض مساحة العمل هذا",
"prefs.blur_web": "غير متاح في مسار عرض الويب هذا",
"prefs.bodies_folders": "مجلدات الأجسام",
"prefs.bodies_folders_always": "دائمًا",
"prefs.bodies_folders_never": "أبدًا",
"prefs.bodies_folders_several": "عند تعدد الأجسام فقط",
"prefs.bodies_folders_several": "عند التعدد",
"prefs.bottom": "أسفل",
"prefs.bottom_centre": "أسفل الوسط",
"prefs.bottom_right": "أسفل اليمين",
@@ -2249,14 +2318,32 @@
"prefs.dark_parts_shading_help": "تظليل العرض بإضاءة خطية مع إضاءة سماء ناعمة، لتحتفظ الأوجه الداكنة بتدرج بدل أن تصبح سوداء",
"prefs.dead_zone_note": "الشريط المخطط هو المنطقة الميتة: تُتجاهل الحركة داخلها.",
"prefs.default": "افتراضي",
"prefs.design": "افتراضيات التصميم",
"prefs.design_note": "ما تبدأ به التصاميم الجديدة. التصاميم المفتوحة تحتفظ بإعداداتها.",
"prefs.design": "التصميم",
"prefs.design_note": "ما تبدأ به التصاميم الجديدة.",
"prefs.design_tools.ask": "اسأل",
"prefs.design_tools.degrees": "درجات",
"prefs.design_tools.double_click_help": "أو لتحرير مكوّن مرتبط في موضعه.",
"prefs.design_tools.ghost": "إظهار الموضع القديم للأجزاء المنقولة كشبح",
"prefs.design_tools.hole_moves_help": "اختيار الوجه مجددًا ينقل الثقب بدلًا من إزالته.",
"prefs.design_tools.hole_snaps_help": "منتصف الوجه ومراكز حوافه الدائرية.",
"prefs.design_tools.imported_files": "الملفات المستوردة",
"prefs.design_tools.note": "ملاحظة",
"prefs.design_tools.off": "إيقاف",
"prefs.design_tools.placement_group": "مسند، نتوء، تعشيق مرن",
"prefs.design_tools.rows": "صفوف",
"prefs.devices": "الفأرة ثلاثية الأبعاد",
"prefs.dialog_buttons": "أزرار مربعات الحوار",
"prefs.dialogs": "الحوارات",
"prefs.dialogs_edge": "عند حافة العرض",
"prefs.dialogs_left_column": "في العمود الأيسر",
"prefs.dialogs_note": "مكان فتح حوارات الأوامر: مثبتة عند حافة العرض، أو في العمود الأيسر مكان المستعرض والسجل، اللذين يعودان كما كانا عند إغلاق الحوار.",
"prefs.dimension_step_angle": "ما تضيفه أسهم النافذة وأعلى/أسفل والعجلة إلى الزاوية",
"prefs.dimension_step_length": "ما تضيفه أسهم النافذة وأعلى/أسفل والعجلة إلى الطول",
"prefs.dimension_steps": "خطوات النافذة",
"prefs.dimension_values": "قيم الأبعاد",
"prefs.dimension_values_note": "مكان كتابة قيمة البعد. للنافذة أسهم تغيّرها بخطوات؛ تضع أداة البعد النص أولًا.",
"prefs.dimension_values_view": "في العرض",
"prefs.dimension_values_window": "في نافذة",
"prefs.display": "العرض",
"prefs.edges": "الحواف والتظليل",
"prefs.extend": "توسيع",
@@ -2292,6 +2379,23 @@
"prefs.graphics_card_restart": "يُطبَّق بعد إعادة تشغيل SolidCraft. يتجاوز WGPU_ADAPTER_NAME هذا الاختيار.",
"prefs.graphics_card_unused": "لم تُستخدم بطاقة الرسوميات المحفوظة لهذه النافذة؛ البطاقة المستخدمة تظهر في «المساعدة › حول» وفي شريط الحالة. جرّب «تلقائي»، أو عيّن WGPU_ADAPTER_NAME إلى جزء فريد من اسم البطاقة.",
"prefs.graphics_note": "وازن بين السرعة والنعومة. كل خيار يعرض أثره على عيّنة.",
"prefs.graphics_page.current_view": "العرض الحالي",
"prefs.graphics_page.fastest": "الأسرع",
"prefs.graphics_page.fold_items": "تلخيص العناصر",
"prefs.graphics_page.in_view": "داخل العرض",
"prefs.graphics_page.live_details": "تفاصيل مباشرة",
"prefs.graphics_page.own_mix": "مزيجك الخاص",
"prefs.graphics_page.quick_measure": "قياس سريع",
"prefs.graphics_page.reference_geometry_through_bodies": "الهندسة المرجعية عبر الأجسام",
"prefs.graphics_page.restart": "تلزم إعادة التشغيل",
"prefs.graphics_page.sharpest": "الأكثر وضوحًا",
"prefs.graphics_page.single_surface_analysis": "تحليل سطح واحد",
"prefs.graphics_page.status_chips": "شارات الحالة",
"prefs.graphics_page.subtitle": "وازن بين السرعة والنعومة.",
"prefs.graphics_page.supersampling": "التنعيم الفائق",
"prefs.graphics_page.supersampling_adaptive": "فقط أثناء سكون العرض",
"prefs.graphics_page.surfaces_edges": "الأسطح والحواف",
"prefs.graphics_page.view_bar": "أيضًا في شريط العرض",
"prefs.graphics_presets_note": "الإعدادات المسبقة تضبط الخيارات أدناه معًا.",
"prefs.hide_constraint_glyphs_zoomed_out": "إخفاء رموز القيود عند التصغير",
"prefs.hide_constraint_glyphs_zoomed_out_note": "تختفي رموز القيود من العرض عندما يُصغَّر الرسم بقدر يجعلها تزحمه. معطّل: تبقى بحجم ثابت على الشاشة عند أي مستوى تكبير.",
@@ -2300,7 +2404,7 @@
"prefs.history_origin_bodies_note": "يبدأ السجل على اليسار أو تحت المستعرض بمجلد الأجسام للمكوّن النشط وبالأصل، مع أيقونات الإظهار والقوائم كما في المستعرض. لا يتغير السجل في الأسفل.",
"prefs.history_play_button": "زر تشغيل السجل في عناصر تحكم السجل",
"prefs.history_play_button_note": "يضيف تشغيل السجل بين الرجوع خطوة والتقدم خطوة في عناصر تحكم السجل. يمكنك أيضًا النقر بزر الماوس الأيمن على عناصر التحكم لإظهاره أو إخفائه.",
"prefs.history_sketches": "الرسومات",
"prefs.history_sketches": "الرسومات في السجل",
"prefs.history_sketches_in_order": "بالترتيب",
"prefs.history_sketches_note": "عند ضبط الرسومات على «تحت ميزتها»، يعرض السجل على اليسار كل رسم بمستوى واحد للداخل، تحت أول ميزة بُنيت منه، متى كان عريضًا بما يكفي لإظهار الأسماء. أما الخط الزمني في الأسفل فيحتفظ دائمًا بترتيب الإنشاء.",
"prefs.history_sketches_under_feature": "تحت ميزتها",
@@ -2325,6 +2429,22 @@
"prefs.language_system": "لغة النظام",
"prefs.layout": "التخطيط",
"prefs.layout_note": "المضغوط يثبّت اللوحات على حواف النافذة. شفافية المستعرض متاحة في الوضع العائم. غيّر جهة المساعد من صفحة المساعد.",
"prefs.layout_page.active_commands": "تمييز الأوامر النشطة",
"prefs.layout_page.always": "دائمًا",
"prefs.layout_page.card": "بطاقة",
"prefs.layout_page.column": "عمود",
"prefs.layout_page.hover": "عند مرور المؤشر",
"prefs.layout_page.icons_labels": "الأيقونات + التسميات",
"prefs.layout_page.ok_cancel": "موافق وإلغاء",
"prefs.layout_page.on_hover": "عند مرور المؤشر",
"prefs.layout_page.open": "تُفتح",
"prefs.layout_page.origin_bodies": "الأصل والأجسام في الأعلى",
"prefs.layout_page.panels": "اللوحات",
"prefs.layout_page.pinned_menus": "استبعاد الأوامر المثبتة من القوائم",
"prefs.layout_page.play_history": "زر تشغيل السجل",
"prefs.layout_page.slot_count": "{count} من {max} خانة",
"prefs.layout_page.view_corner": "أيضًا في زاوية العرض",
"prefs.layout_page.window": "النافذة",
"prefs.left": "يسار",
"prefs.left_click": "نقر أيسر",
"prefs.line_drag_draws_one": "الضغط والسحب يرسم خطًا واحدًا",
@@ -2353,6 +2473,13 @@
"prefs.mouse_heading": "الفأرة",
"prefs.mouse_mapping": "تعيين أزرار الفأرة",
"prefs.navigation": "التنقل",
"prefs.navigation.axes_signed": "المحاور",
"prefs.navigation.cube_compact": "المكعب",
"prefs.navigation.device": "الجهاز",
"prefs.navigation.free": "حر",
"prefs.navigation.orbit_style": "نمط التدوير",
"prefs.navigation.radial_help": "اضغط مطولًا أو انقر سريعًا بالزر الأيمن لعرض حلقة من الأدوات.",
"prefs.navigation.turntable": "طاولة دوارة",
"prefs.navigation_note": "كيف تحرك الفأرة ولوحة اللمس والفأرة ثلاثية الأبعاد العرض.",
"prefs.navigator_fit": "ملاءمة النموذج بعد تغيير العرض من متصفح العرض",
"prefs.navigator_fit_note": "النقر على وجه أو ركن أو سهم في المكعب، أو على محور في أداة المحاور، يوجّه العرض أيضًا نحو منتصف النموذج من المسافة الواحدة التي تُظهره كاملًا من كل جهة (كرته المحيطة). يستخدم العرض الرئيسي التكبير والمركز ذاتهما ما دام هذا مفعّلًا، فلا تغيّر العروض المختارة بعده سوى الاتجاه. عند الإيقاف: يدور العرض في مكانه محتفظًا بالهدف والتكبير.",
@@ -2369,11 +2496,15 @@
"prefs.origin_plane_names_note": "الأسماء المعروضة في المستعرض والحوارات والقوائم. {top} هو XY و{front} هو XZ و{right} هو YZ، والمحور Z للأعلى. تحتفظ الأوامر والسكربتات والتصاميم المحفوظة بـ XY وXZ وYZ.",
"prefs.overall_speed": "السرعة العامة",
"prefs.parameter_list_rows": "صفوف قائمة المعاملات",
"prefs.parameter_list_rows_tip": "يعرض السهم لأسفل في حقل قيمة فارغ المعاملات، المفضلة أولًا، حتى هذا العدد. العدد الأقل يبقي القائمة خفيفة في التصميم الكبير.",
"prefs.parameter_list_rows_tip": "الحد الأقصى للمعاملات التي يسردها السهم لأسفل في مربع تعبير فارغ.",
"prefs.persistent_ribbon_pressed": "تمييز أوامر الشريط النشطة",
"prefs.persistent_ribbon_pressed_help": "تمييز أوامر الشريط أثناء تفعيل تأثيراتها. أوقف هذا الخيار للاحتفاظ بالمظهر المحايد السابق ومساعدة الأوامر.",
"prefs.perspective_fov": "مجال الرؤية",
"prefs.placement": "الموضع",
"prefs.plugins_enabled": "مفعّل",
"prefs.plugins_installed": "المثبّتة",
"prefs.plugins_intro": "إضافات معزولة.",
"prefs.plugins_show_folder": "إظهار مجلد الإضافات",
"prefs.prefer_free_sketch_points": "تفضيل نقاط الرسم القابلة للتحريك عند تداخل النقاط",
"prefs.preview_choices": "معاينة اختياراتك",
"prefs.preview_tint_new_faces": "تلوين السطح الجديد في المعاينة",
@@ -2431,8 +2562,43 @@
"prefs.shift_add_ctrl_toggle": "Shift يضيف فقط، وCtrl / Cmd يبدّل",
"prefs.shift_add_note": "عند التشغيل: Shift يضيف، وCtrl / Cmd يبدّل. عند الإيقاف (الافتراضي): كلاهما يبدّل.",
"prefs.shortcuts": "الاختصارات",
"prefs.shortcuts_import": "استيراد",
"prefs.shortcuts_intro": "مفاتيح كل أمر.",
"prefs.shortcuts_not_assigned": "غير معيّن",
"prefs.shortcuts_press": "اضغط مفتاحًا...",
"prefs.single_surface_analysis": "استخدام تحليل سطح واحد",
"prefs.single_surface_analysis_help": "عند إيقاف هذا الخيار، يمكن إبقاء تظليل السطح وتحليل الاستمرارية مفعّلين معًا. عند تشغيله، يبقى آخر تحليل تم بدؤه أو تغييره فقط.",
"prefs.sketch.align_points": "المحاذاة مع نقاط الرسم الأخرى",
"prefs.sketch.align_points_note": "تتحاذى الخطوط أثناء الرسم مع النقاط على المحورين معًا.",
"prefs.sketch.auto_dimensions": "إضافة كل الأبعاد للأشكال الجديدة",
"prefs.sketch.auto_dimensions_note": "القيم المدخلة وكل بُعد يثبّت الشكل.",
"prefs.sketch.auto_locate": "تحديد الموضع من أصل الرسم",
"prefs.sketch.auto_locate_note": "مع إضافة كل الأبعاد.",
"prefs.sketch.center_arc": "القوس بنقطة مركز يتبع المؤشر",
"prefs.sketch.click_selects": "النقر يحدد",
"prefs.sketch.conflict_colour": "لون التعارض",
"prefs.sketch.drawing": "الرسم",
"prefs.sketch.exit_keep": "إبقاء",
"prefs.sketch.exit_previous": "العودة إلى العرض السابق",
"prefs.sketch.exit_unless_rotated": "إلا إذا تم التدوير",
"prefs.sketch.faces_edges": "الأوجه والحواف",
"prefs.sketch.hide_glyphs": "إخفاء الرموز عند التصغير",
"prefs.sketch.marks": "العلامات في الرسم",
"prefs.sketch.note": "الرسم والالتقاط والتحديد.",
"prefs.sketch.opening_closing": "الفتح والإغلاق",
"prefs.sketch.prefer_free_points": "تفضيل النقاط القابلة للتحريك عند تداخلها",
"prefs.sketch.project_options": "السؤال عن خيارات الإسقاط أولًا",
"prefs.sketch.recent_snap": "الالتقاط عند تقاطع دليلين",
"prefs.sketch.select_other_hidden": "تحديد آخر يشمل الهندسة المخفية",
"prefs.sketch.shortcut_cycles_modes": "الاختصار مجددًا يبدّل الوضع",
"prefs.sketch.shortcut_cycles_modes_tip": "عند الإيقاف: تبدأ الأداة مجددًا.",
"prefs.sketch.snapping_guides": "الالتقاط والأدلة",
"prefs.sketch.state_icons": "أيقونات الحالة",
"prefs.sketch.tracking_guides": "أدلة المراكز ونقاط المنتصف",
"prefs.sketch.tracking_guides_note": "أدلة أفقية وعمودية مؤقتة من المراكز ونقاط منتصف الحواف عند التحويم.",
"prefs.sketch.tracking_points": "أدلة من نقاط الرسم أيضًا",
"prefs.sketch.unless_turned_note": "العودة إذا لم يتم التدوير",
"prefs.sketch.whole_bodies": "الأجسام كاملة",
"prefs.sketch_align_points": "المحاذاة مع نقاط الرسم الأخرى أثناء الرسم",
"prefs.sketch_align_points_note": "أثناء الرسم، تتحاذى النقطة أفقيًا مع نقطة رسم وعموديًا مع نقطة أخرى في الوقت نفسه، مع خط إرشادي منقط، وتحتفظ النقرة بالمحاذاتين. عند الإيقاف: مع النقطة السابقة فقط، محور واحد في كل مرة.",
"prefs.sketch_conflict_colour": "الهندسة المتعارضة بلون التعارض",
@@ -2465,11 +2631,11 @@
"prefs.source": "المصدر",
"prefs.speed": "السرعة",
"prefs.stand_up_note": "بعد فتح ملف STEP أو IGES ومحوره Z للأعلى، يُعرض تدويره ربع دورة حول X.",
"prefs.stand_up_unknown": "للملفات من برامج غير معروفة",
"prefs.stand_up_unknown": "ملفات من برامج غير معروفة",
"prefs.stand_up_unknown.ask": "السؤال في مربع حوار",
"prefs.stand_up_unknown.note": "عرض ملاحظة",
"prefs.stand_up_unknown.off": "عدم العرض",
"prefs.stand_up_unknown_note": "طريقة العرض عندما لا يذكر رأس الملف برنامجًا معروفًا بحفظ المحور Y للأعلى. تظهر الملاحظة فقط عندما يبدو أن الملف ملقى على ظهره؛ أما «السؤال في مربع حوار» فيسأل عن كل ملف كهذا، كما كان سابقًا.",
"prefs.stand_up_unknown_note": "كيفية عرض الاقتراح عندما لا يذكر الملف برنامجًا معروفًا.",
"prefs.status_bar": "شريط الحالة",
"prefs.status_bar_note": "شريط رفيع على الحافة السفلية للنافذة يعرض ما يجري تحريره، وحالة الرسم المفتوح بلونها، ووحدات التصميم؛ النقر على الوحدات يغيّرها. عندها لا تظهر حالة الرسم في زاوية العرض.",
"prefs.status_done": "تم",
@@ -2505,6 +2671,10 @@
"prefs.theme_follow_off": "إيقاف",
"prefs.theme_follow_stop": "إيقاف الاتباع",
"prefs.theme_import_hint": "انقر لاختيار ملف سمة، أو أفلت ملفًا هنا، أو اضغط Ctrl+V (Cmd+V على Mac) بعد نسخ سمة",
"prefs.theme_notice_dismiss": "إخفاء",
"prefs.theme_option_blur": "تمويه خلف اللوحات",
"prefs.theme_option_blur_help": "تمويه خلف اللوحات شبه الشفافة، بالبكسل الفعلي. الصفر يوقف التمويه.",
"prefs.theme_option_invalid": "القيمة المحفوظة للخيار {option} غير صالحة؛ تُستخدم القيمة الافتراضية للسمة. تبقى القيمة المحفوظة كما هي.",
"prefs.theme_option_opacity": "العتامة",
"prefs.theme_option_opacity_help": "اضبط عتامة اللوحات العائمة. يُطبّق حد أدنى للعتامة للحفاظ على وضوح النص فوق ألوان منطقة العرض.",
"prefs.theme_option_tab_colours": "ألوان علامات التبويب",
@@ -2513,6 +2683,7 @@
"prefs.theme_option_tab_fade_ms_help": "تتغيّر ألوان اللوحات ومنطقة العرض تدريجيًا عند تبديل علامات التبويب. القيمة صفر تبدّلها فورًا.",
"prefs.theme_options_badge": "الخيارات",
"prefs.theme_options_more": "المزيد · {count}",
"prefs.theme_options_newer": "تتطلب هذه السمة إصدارًا أحدث من SolidCraft. بعد الترقية، أعد استيراد السمة الأصلية لاستعادة تعريفات خياراتها.",
"prefs.theme_options_reset": "إعادة تعيين",
"prefs.theme_options_unavailable": "غير متاح على هذا الجهاز",
"prefs.theme_paste": "لصق السمة",
@@ -2541,9 +2712,9 @@
"prefs.validation.each_step": "بعد كل خطوة",
"prefs.validation.label": "فحص النواة الكامل",
"prefs.validation.off": "متوقف (تجريبي)",
"prefs.validation_note": "بعد كل خطوة: يفحص كل عملية سحب ودمج وتقويس.\nمرة لكل ميزة: يفحص ناتج كل ميزة مرة واحدة.\nمتوقف (تجريبي): لا فحص.",
"prefs.value_boxes_in_view": "إظهار القيم بجانب مقابضها في العرض",
"prefs.value_boxes_in_view_tip": "قيمة الأمر (المسافة، نصف القطر…) في مربع بجانب مقبضه، فوق النموذج. عند الإيقاف: تكتبها في نافذة الأمر، التي تتلقى لوحة المفاتيح بعد التحديد، ولا شيء يغطي النموذج.",
"prefs.validation_note": "بعد كل خطوة: يفحص كل عملية كنس وعملية منطقية وتقويس.\nمرة لكل ميزة: يفحص ناتج كل ميزة مرة واحدة.\nمتوقف (تجريبي): لا فحص.",
"prefs.value_boxes_in_view": "إظهار القيم بجانب مقابضها",
"prefs.value_boxes_in_view_tip": "مسافة البثق ونصف قطر التدوير... في مربع داخل العرض.",
"prefs.view": "سلوك العرض",
"prefs.view_confirm_corner": "موافق وإلغاء في زاوية العرض",
"prefs.view_confirm_corner_note": "أثناء فتح مربع حوار أو رسم، تؤدي علامة الصح وعلامة الإكس أعلى يمين العرض ما يؤديه زرّا موافق وإلغاء في مربع الحوار، أو تنهي الرسم وتلغيه. وينتقل متصفح العرض إلى أسفلهما.",
@@ -3944,11 +4115,11 @@
"select two components": "حدد مكوّنين اثنين",
"set by the sketch's lines": "تحدده خطوط الرسم",
"settings.display_quality_note": "المستويات الأدق ترسم الأوجه والحواف المنحنية بنعومة أكبر وتستغرق وقتًا أطول لإنشاء الشبكة. يتغير العرض فقط، لا التصميم ولا الملفات المصدّرة.",
"settings.double_click_components": "انقر نقرًا مزدوجًا على مكوّن لتنشيطه، أو لتحرير مكوّن مرتبط في مكانه",
"settings.double_click_components": "النقر المزدوج على مكوّن لتنشيطه",
"settings.frame_rate_details_note": "يضيف رسمًا لأزمنة الإطارات الأخيرة مع المثلثات المرسومة فوقه، ومدرّجًا تكراريًا لأزمنة الإطارات، وما رسمته وحدة GPU. انقر معدل الإطارات في العرض لإظهارها أو إخفائها.",
"settings.frame_rate_note": "يعرض الإطارات في الثانية في الزاوية اليمنى السفلية من العرض. أثناء تفعيله يُعاد رسم العرض باستمرار.",
"settings.insert_lists_open_designs": "يعرض إدراج تصميم أولًا التصاميم المفتوحة في علامات التبويب الأخرى",
"settings.measure_axis_distances": "إظهار المسافات على X/Y/Z",
"settings.insert_lists_open_designs": "إدراج تصميم يسرد التصاميم المفتوحة أولًا",
"settings.measure_axis_distances": "مسافات المحاور في القياس",
"settings.measure_axis_distances_help": "إظهار مركّبات الأبعاد على محاور العالم بجانب المسافة الكلية أثناء فتح أداة القياس.",
"settings.msaa_applies_on_restart": "يُطبّق تنعيم الحواف في المرة القادمة التي يبدأ فيها SolidCraft",
"settings.msaa_reset": "{setting}: القيمة {requested}× غير مدعومة في جهاز الرسوميات هذا؛ أصبحت الآن {reset}× (المدعوم: {supported}).",
@@ -3958,7 +4129,7 @@
"settings.reset_intro": "هذه الإعدادات لا تعمل على هذا الحاسوب وكانت ستمنع SolidCraft من البدء، لذا غُيّرت:",
"settings.reset_title": "تم تغيير الإعدادات",
"settings.supersampling_note": "يرسم العرض بدقة أعلى ثم يصفّيه: حواف وخطوط وشبكة أنعم على الشاشات الأقل من 4K. يكلّف وقتًا من معالج الرسوميات: نحو 2.25× من البكسلات عند 1.5× و4× عند 2×.",
"settings.update_changed_links": "تحديث التصاميم المرتبطة التي تغيّرت عند العودة إلى علامة التبويب",
"settings.update_changed_links": "تحديث الروابط المتغيرة عند العودة إلى علامة تبويب",
"settings.window_system_details": "التفاصيل",
"settings.window_system_note_now": "يُطبَّق بعد إعادة التشغيل. الحالي: {now}.",
"settings.window_system_wayland_limited": "بعض الميزات محدودة على Wayland الأصلي.",
@@ -4056,13 +4227,13 @@
"thread.automatic_size": "حجم تلقائي",
"thread.chamfer_depth_needed": "مطلوب لهذا الشكل",
"thread.designation": "التسمية",
"thread.drill_range": "مثقاب صانع اللقمة {drill} مم. القطر الأصغر القياسي للسن الداخلي {minor} مم. يُقبل مثقاب من {low} إلى {high} مم.",
"thread.drill_range": "مثقاب التسنين الذي يوصي به صانعو ذكور التسنين {drill} مم. القطر الأصغر القياسي للسن الداخلي {minor} مم. يُقبل مثقاب من {low} إلى {high} مم.",
"thread.drill_range_custom": "المثقاب هو القطر الأكبر ناقص الخطوة، {drill} مم. القطر الأصغر القياسي للسن الداخلي {minor} مم. يُقبل مثقاب من {low} إلى {high} مم.",
"thread.drill_range_taper": "مثقاب صانع اللقمة {drill} مم. القطر الأصغر القياسي عند نهاية السن {minor} مم. يُقبل مثقاب من {low} إلى {high} مم.",
"thread.drill_range_taper": "مثقاب التسنين الذي يوصي به صانعو ذكور التسنين {drill} مم. القطر الأصغر القياسي عند نهاية السن {minor} مم. يُقبل مثقاب من {low} إلى {high} مم.",
"thread.end_square": "قائمة",
"thread.fade_short": "1.25 P (ISO 3508)",
"thread.fade_standard": "2.5 P (ISO 3508)",
"thread.fits_the_face": "يناسب الوجه، مثلاً {example}",
"thread.fits_the_face": "يناسب الوجه، مثلًا {example}",
"thread.form_bspp": "سن أنابيب G (ISO 228-1)",
"thread.form_bspt": "سن أنابيب مخروطي R، Rc (ISO 7-1)",
"thread.form_npt": "سن أنابيب مخروطي NPT (ASME B1.20.1)",
@@ -4319,7 +4490,7 @@
"ui.agent_chat_no_images": "هذا الوكيل لا يقبل الصور",
"ui.agent_chat_no_picture": "لا توجد صورة في الحافظة",
"ui.agent_chat_no_tool_call": "لم يستخدم النموذج أدوات SolidCraft، لذا لم يتغيّر التصميم. اختر نموذجًا يدعم استدعاء الأدوات؛ راجع «النماذج المحلية» في دليل محادثة الوكيل.",
"ui.agent_chat_node_too_old": "إصدار Node.js ‏{version} قديم جدًا لهذا الوكيل. ثبّت Node.js 20.10 أو أحدث، ثم اضغط على تحديث.",
"ui.agent_chat_node_too_old": "إصدار Node.js {version} قديم جدًا لهذا الوكيل. ثبّت Node.js 20.10 أو أحدث، ثم اضغط تحديث.",
"ui.agent_chat_none_found": "لم يُعثر على وكيل برمجي. ثبّت أحد هذه ثم اضغط تحديث:",
"ui.agent_chat_none_found_short": "لم يُعثر على وكيل",
"ui.agent_chat_not_installed": "غير مثبت",
@@ -4360,7 +4531,7 @@
"ui.agent_chat_send_now_tip": "إيقاف خطوة الوكيل الحالية وإرسال هذه بعدها (Ctrl+Enter)",
"ui.agent_chat_setup_body": "يتواصل SolidCraft مع وكيل برمجة مثبّت على هذا الحاسوب. اختر واحدًا من التفضيلات › الوكيل.",
"ui.agent_chat_setup_title": "إعداد مساعد",
"ui.agent_chat_side": "جانب محادثة الوكيل",
"ui.agent_chat_side": "تثبيت المحادثة",
"ui.agent_chat_signed_in": "سجّل الدخول",
"ui.agent_chat_state_auth": "يلزم تسجيل الدخول",
"ui.agent_chat_state_busy": "جارٍ العمل…",
@@ -4425,14 +4596,14 @@
"ui.ai_key_note": "يُخزّن في سلسلة مفاتيح النظام، وليس في إعدادات SolidCraft أبدًا. اتركه فارغًا لخادم محلي.",
"ui.ai_keychain_error": "رفضت سلسلة مفاتيح النظام: {error}",
"ui.ai_kind": "النوع",
"ui.ai_mode_all": "السماح بكل شيء",
"ui.ai_mode_all": "تنفيذ كل شيء",
"ui.ai_mode_all_note": "لا شيء يسأل أولًا. تبقى تقارير الأعطال محظورة وتظل حدود المجلدات سارية.",
"ui.ai_mode_all_warning": "يمكن للوكيل حفظ تصميمك والكتابة فوقه وتصديره واستبداله دون سؤال. استخدم هذا فقط مع وكيل تثق به، وعلى تصاميم حفظتها.",
"ui.ai_mode_ask": "السؤال في كل مرة",
"ui.ai_mode_ask": "السؤال أولًا",
"ui.ai_mode_ask_note": "كل تغيير في التصميم وكل إجراء على الملفات ينتظر سماحك.",
"ui.ai_mode_edits": "السماح تلقائيًا بتعديلات النموذج",
"ui.ai_mode_edits": "إجراء تعديلات",
"ui.ai_mode_edits_note": "أوامر النمذجة تعمل دون سؤال؛ أما الحفظ والفتح والتصدير وإجراءات الملفات أو الإجراءات الهدّامة الأخرى فتسأل.",
"ui.ai_mode_global": "كما هو مضبوط أعلاه",
"ui.ai_mode_global": "كما في الأعلى",
"ui.ai_model": "النموذج",
"ui.ai_model_hint": "qwen3:14b",
"ui.ai_modes_scope": "هذه الإعدادات تغطي أدوات SolidCraft. أدوات الوكيل الخاصة (الصدفة وتعديل الملفات) تتبع وضع الوكيل الخاص، الذي تختاره في رأس لوحة المحادثة عندما تكون للوكيل أوضاع.",
@@ -4458,7 +4629,7 @@
"ui.ai_save": "حفظ",
"ui.ai_settings": "إعدادات الذكاء الاصطناعي…",
"ui.ai_show_features": "إظهار ميزات الذكاء الاصطناعي",
"ui.ai_show_features_note": "محادثة الوكيل: لوحة يعمل فيها وكيل برمجي تستخدمه أصلًا، أو نقطة نهاية نموذج تختارها، على التصميم المفتوح. عند الإيقاف: لا زر ولا اختصار ولا عنصر قائمة ولا لوحة للذكاء الاصطناعي، ولا يُبحث عن شيء أو يُشغّل.",
"ui.ai_show_features_note": "إيقافه يخفي المساعد في كل مكان ولا يبدأ أي شيء. تصبح كل الخيارات أدناه باهتة.\n\nمحادثة الوكيل: لوحة يعمل فيها وكيل برمجي تستخدمه أصلًا، أو نقطة نهاية نموذج تختارها، على التصميم المفتوح. عند الإيقاف: لا زر ولا اختصار ولا عنصر قائمة ولا لوحة للذكاء الاصطناعي، ولا يُبحث عن شيء أو يُشغّل.",
"ui.ai_switch_tip": "تغيير الوكيل لهذه المحادثة",
"ui.ai_test": "اختبار",
"ui.ai_test_acp_ok": "يجيب: {agent}",
@@ -4492,7 +4663,7 @@
"ui.ai_tool_sum_then": "{first}، ثم {next}",
"ui.ai_tool_unfold": "انقر لعرض ما أعاده",
"ui.ai_tool_waiting": "بالانتظار",
"ui.ai_toolbar_button": "إظهار الزر في شريط الأدوات",
"ui.ai_toolbar_button": "زر المساعد في شريط التطبيق",
"ui.ai_toolbar_button_note": "زر الذكاء الاصطناعي في الطرف الأيمن من شريط الأدوات. يظل Ctrl+Shift+A وقائمة العرض يفتحان اللوحة.",
"ui.ai_undo_tip": "التراجع عن كل ما غيّره الوكيل لهذه الرسالة، بخطوة واحدة",
"ui.ai_url_hint": "https://example.com/v1",
@@ -4599,21 +4770,21 @@
"ui.construction.ribbon_label": "خطوط مساعدة",
"ui.construction.side_hint": "خط إنشائي: انقر الجهة التي يمتد نحوها الخط؛ انقر بزر الفأرة الأيمن للبدء من جديد",
"ui.context_menu": "القائمة السياقية",
"ui.continuity_crease": "تجعّد",
"ui.continuity_crease": "ثنية",
"ui.continuity_curvature": "الانحناء",
"ui.continuity_curvature_change": "تغيّر الانحناء",
"ui.continuity_curvature_change_tip": "تغيّر الانحناء (G3): يتغير الانحناء أيضًا بسلاسة عبر خط الالتقاء.",
"ui.continuity_curvature_tip": "متصل الانحناء (G2): تمر الانعكاسات دون انكسار.",
"ui.continuity_g0": "G0 تجعّد",
"ui.continuity_g0": "G0 ثنية",
"ui.continuity_g1": "G1 مماس",
"ui.continuity_g2": "G2 انحناء",
"ui.continuity_gap": "فجوة",
"ui.continuity_position": "الموضع",
"ui.continuity_position_tip": "الموضع (G0): تلتقي الأسطح، مع تجعّد عند التقائها.",
"ui.continuity_position_tip": "الموضع (G0): تلتقي الأسطح، مع ثنية عند التقائها.",
"ui.continuity_smooth_curvature_change": "تغيّر انحناء سلس",
"ui.continuity_smooth_curvature_change_tip": "تغيّر انحناء سلس (G4): حتى طريقة تغيّر الانحناء تستمر بسلاسة عبر خط الالتقاء.",
"ui.continuity_tangent": "مماس",
"ui.continuity_tangent_tip": "مماس (G1): لا تجعّد، لكن الانحناء يقفز عبر خط الالتقاء.",
"ui.continuity_tangent_tip": "مماس (G1): لا ثنية، لكن الانحناء يقفز عبر خط الالتقاء.",
"ui.copy": "نسخ",
"ui.copy_details": "نسخ التفاصيل",
"ui.copy_details_tip": "انسخ الرسالة كاملة للبحث عنها أو للإبلاغ عنها.",
@@ -4639,6 +4810,7 @@
"ui.default_modelling_orientation": "اتجاه النمذجة الافتراضي",
"ui.delete": "حذف",
"ui.delete_select_first": "حدّد أولًا ما تريد حذفه: الميزات أو الأجسام أو المكوّنات أو هندسة الرسم",
"ui.dependents_select_first": "حدّد أولًا ما تريد سرد معتمداته",
"ui.depth": "العمق",
"ui.design": "التصميم",
"ui.design_properties.author": "المؤلف",
@@ -4764,7 +4936,7 @@
"ui.extract_done": "أصبح المكوّن تصميمًا مستقلًا، مرتبطًا هنا.",
"ui.extrude": "بثق",
"ui.extrude_auto_to_object": "البثق إلى هدف مختار",
"ui.extrude_auto_to_object_tip": "في البثق، مع اختيار مقطع، يؤدي اختيار وجه أو رأس أو مستوى موازٍ خارج مستواه إلى تبديل الامتداد إلى «إلى كائن»",
"ui.extrude_auto_to_object_tip": "وجه أو رأس أو مستوى موازٍ يغيّر الامتداد إلى «إلى كائن».",
"ui.factor": "المعامل",
"ui.fade": "تلاشٍ",
"ui.fade_length": "طول التلاشي",
@@ -4794,9 +4966,13 @@
"ui.fillet_setback": "مسافة الإرجاع",
"ui.fillet_setback_hint": "{factor} × نصف القطر",
"ui.fillet_setback_least": "أكثر من {least}",
"ui.fillet_setback_tip": "حيث تبدأ رقعة الزاوية الناعمة: المسافة على طول كل حافة من الزاوية. الافتراضي {factor} ضعف نصف القطر. يجب أن تكون أكبر من أقل مسافة إرجاع، حيث تلتقي الاستدارات بدونها: نصف القطر حيث تكون الأوجه متعامدة.",
"ui.fillet_setback_tip": "حيث تبدأ رقعة الزاوية الناعمة: المسافة على طول كل حافة من الزاوية. الافتراضي {factor} ضعف نصف القطر. يجب أن تكون أكبر من أقل مسافة إرجاع، وهي التي تلتقي عندها التقويسات دون إرجاع: نصف القطر حين تكون الأوجه متعامدة.",
"ui.fillet_smooth_radius": "تغيّر ناعم لنصف القطر",
"ui.fillet_type_choices_tip": "نصف قطر ثابت: نصف قطر واحد على طول الحافة.\nطول الوتر: يبقى عرض التقويس نفسه مهما كانت الزاوية بين الوجوه.\nنصف قطر متغيّر: نصف قطر عند كل طرف، ومزيد على طول الحافة إن أضفته.\nمتصل الانحناء: مقطع بلا انكسار في الانعكاسات حيث يلتقي بالوجوه المستوية (G2).\nمنحنى: يتبع نصف القطر على طول الحافة أنصاف أقطار تحكّم تحدّدها.",
"ui.fillet_turn": "الالتفاف",
"ui.fillet_turn_mitre": "وصلة مائلة",
"ui.fillet_turn_swept": "ملتف",
"ui.fillet_turn_tip": "حيث يلتف تقويسان حول حافة ركن حادة بينهما، مثل التقويسات على قاعدة نتوء عند حوافه القائمة.\nوصلة مائلة: يمتد التقويسان حتى يلتقيا في وصلة مائلة.\nملتف: يتوقف التقويسان عند حافة الركن ويلتفان حولها، والركن طارة مماسة لكليهما. فقط لتقويسين بنصف قطر واحد حول حافة ركن مستقيمة ومحدبة.",
"ui.fillet_type_choices_tip": "نصف قطر ثابت: نصف قطر واحد على طول الحافة.\nطول الوتر: يبقى عرض التقويس نفسه مهما كانت الزاوية بين الوجوه.\nنصف قطر متغيّر: نصف قطر عند كل طرف، ومزيد على طول الحافة إن أضفته.\nمتصل الانحناء: مقطع بلا انكسار في الانعكاسات حيث يلتقي بالوجوه المستوية (G2).\nمنحنى شريحي: يتبع نصف القطر على طول الحافة أنصاف أقطار تحكّم تحدّدها.",
"ui.fillet_type_curvature": "متصل الانحناء",
"ui.fillet_type_tip": "المقطع عبر الحافة. متصل الانحناء: مقطع متصل الانحناء (G2) بين وجوه مستوية، فتمر الانعكاسات من الوجوه إلى التقويس دون انكسار.",
"ui.filter_commands_or_keys": "تصفية الأوامر أو المفاتيح",
@@ -4908,7 +5084,7 @@
"ui.hidePlanes": "إخفاء المستويات",
"ui.hideProfile": "إخفاء المقطع",
"ui.hide_component_history": "إخفاء سجله",
"ui.hide_origin_by_default": "إخفاء نقطة الأصل افتراضيًا",
"ui.hide_origin_by_default": "إخفاء مجلد نقطة الأصل في التصاميم الجديدة",
"ui.hide_origin_note": "تبدأ التصاميم الجديدة ومجلد نقطة الأصل مخفي. يحتفظ كل تصميم بإعداد نقطة الأصل الخاص به بمجرد إظهارها أو إخفائها.",
"ui.history_back": "الرجوع خطوة",
"ui.history_below_browser": "أسفل المستعرض",
@@ -5068,7 +5244,7 @@
"ui.pitch": "الخطوة",
"ui.placement": "الموضع",
"ui.placement_handles": "مقابض الموضع",
"ui.placement_handles_tip": "مقابض على معاينة المسند أو النتوء أو وصلة الكبس: اسحب العلامة لتحريكها على النموذج، وأدِرها من حلقتها",
"ui.placement_handles_tip": "علامة لسحب الميزة عبر النموذج وحلقة لتدويرها.",
"ui.plane": "المستوى",
"ui.plane_at_angle": "مستوى بزاوية",
"ui.plugins": "الإضافات",
@@ -5266,8 +5442,8 @@
"ui.start": "البداية",
"ui.start_over": "البدء من جديد",
"ui.start_page": "صفحة البداية",
"ui.step_values": "تغيير القيم بمفاتيح الأسهم وعجلة الفأرة",
"ui.step_values_tip": "في حقل قيمة تكتب فيه، يغيّر السهمان لأعلى ولأسفل الرقم العادي بمقدار 1 (Shift: 10، Alt: 0.1)، وكذلك عجلة الفأرة فوق الحقل. أما التعابير فتبقى كما كُتبت.",
"ui.step_values": "تغيير القيم بالأسهم والعجلة",
"ui.step_values_tip": "يغيّر السهمان لأعلى ولأسفل والعجلة فوق حقل قيمة الرقم فيه.",
"ui.stitch_tolerance_hint": "دقيقة",
"ui.stretch": "مطّ",
"ui.supersampling": "التنعيم الفائق (SSAA)",
+78
View File
@@ -477,6 +477,7 @@
"Rectangular": "Pravougaoni",
"Rectangular Tube": "Pravougaona cijev",
"Reference face": "Referentno lice",
"Reference plane": "Referentna ravan",
"Relief Depth": "Dubina rasterećenja",
"Relief Width": "Širina rasterećenja",
"Remove Tools": "Ukloni alate",
@@ -556,6 +557,7 @@
"Section": "Presjek",
"Section (optional)": "Presjek (neobavezno)",
"Section position": "Položaj presjeka",
"Sections": "Presjeci",
"Sections (one sketch each)": "Presjeci (po jedna skica)",
"Select Other": "Izaberi drugo",
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "Izaberi drugo: držite lijevo dugme mirno na modelu (ili desni klik › Izaberi drugo…) da prikažete sve ispod pokazivača; ponovo kliknite na isto mjesto da izaberete sljedeći element. Dvaput kliknite srednjim dugmetom za Početni prikaz (Postavke › Navigacija).",
@@ -859,6 +861,7 @@
"cmd.component_extract": "Izdvoji u datoteku",
"cmd.component_finish_edit": "Završi uređivanje",
"cmd.component_from_bodies": "Napravi komponente iz tijela",
"cmd.component_hide_history": "Sakrij vremensku traku komponente",
"cmd.component_insert_design": "Umetni dizajn",
"cmd.component_links": "Popis veza",
"cmd.component_list": "Popis komponenti",
@@ -875,6 +878,7 @@
"cmd.component_rename": "Preimenuj komponentu",
"cmd.component_replace_with_link": "Zamijeni vezom",
"cmd.component_revert_position": "Vrati položaj",
"cmd.component_show_history": "Prikaži vremensku traku komponente",
"cmd.component_unlock": "Otključaj sadržaj",
"cmd.component_update_links": "Ažuriraj veze",
"cmd.config_activate": "Aktiviraj konfiguraciju",
@@ -885,6 +889,7 @@
"cmd.config_rule": "Pravilo konfiguracije",
"cmd.config_rules": "Pravila konfiguracije",
"cmd.config_table": "Prikaži tabelu konfiguracija",
"cmd.construct.plane.axis_point": "Ravan kroz osu i tačku",
"cmd.construct_axis_edge": "Osa kroz ivicu",
"cmd.construct_axis_normal_to_face": "Osa okomita na lice",
"cmd.construct_axis_through_cylinder": "Osa kroz cilindar/konus/torus",
@@ -933,6 +938,11 @@
"cmd.explode_show": "Prikaži rastavljeni prikaz",
"cmd.expr_evaluate": "Izračunaj izraz",
"cmd.face_delete": "Izbriši lice",
"cmd.file.export_3mf": "Izvezi 3MF",
"cmd.file.export_obj": "Izvezi OBJ",
"cmd.file.export_step": "Izvezi STEP",
"cmd.file.export_stl": "Izvezi STL",
"cmd.file.export_urdf": "Izvezi URDF",
"cmd.file_export": "Izvoz",
"cmd.file_insert_mesh": "Umetni mrežu",
"cmd.file_insert_step": "Umetni STEP",
@@ -950,6 +960,7 @@
"cmd.inspect_colored_components": "Omogući boje komponenti",
"cmd.inspect_colored_components_disable": "Onemogući boje komponenti",
"cmd.inspect_compare": "Uporedi tijela",
"cmd.inspect_compare_picture": "Uporedi sliku",
"cmd.inspect_continuity": "Analiza kontinuiteta",
"cmd.inspect_curvature_comb": "Analiza češlja zakrivljenosti",
"cmd.inspect_curvature_map": "Analiza mape zakrivljenosti",
@@ -1040,6 +1051,9 @@
"cmd.pointcloud_edit": "Uredi oblak tačaka",
"cmd.pointcloud_insert": "Umetni oblak tačaka",
"cmd.pointcloud_list": "Oblaci tačaka",
"cmd.render.compare.divider": "Pomjeri razdjelnik poređenja renderovanja",
"cmd.render.result.discard": "Odbaci rezultat renderovanja",
"cmd.render.result.select": "Izaberi rezultat renderovanja",
"cmd.render_aging_describe": "Slojevito starenje površine",
"cmd.render_backends": "Pozadinski sistemi renderovanja",
"cmd.render_batch": "Renderuj sve kadrove",
@@ -1254,6 +1268,7 @@
"cmd.timeline_rollback": "Pomjeri oznaku vraćanja",
"cmd.timeline_stop": "Zaustavi vremensku traku",
"cmd.timeline_suppress": "Potisni operaciju",
"cmd.ui.assistant": "Prikaži ili sakrij Asistenta",
"cmd.view.render_browser": "Stablo renderovanja",
"cmd.view.render_dock": "Premjesti panel renderovanja",
"cmd.view.render_focus": "Prikaz fokusa renderovanja",
@@ -1288,9 +1303,11 @@
"cmd.view_tool": "Alat za navigaciju",
"cmd.view_top": "Pogled odozgo",
"cmd.view_visual_style": "Vizuelni stil",
"color.blue": "Plava",
"color.copy": "Kliknite da kopirate vrijednosti boje",
"color.edit": "Kliknite da uredite boju",
"color.gamma": "Prikaz vrijednosti boje u gama prostoru 0-255",
"color.green": "Zelena",
"color.hex": "Hex",
"color.hex_help": "Unesite #rrggbb ili #rrggbbaa; šest cifara zadržava neprozirnost",
"color.hex_hint": "#rrggbb",
@@ -1298,6 +1315,7 @@
"color.linear": "Prikaz vrijednosti boje u linearnom prostoru 0-1",
"color.recent": "Nedavne boje",
"color.recent_help": "Ovdje se pojavljuju boje koje izaberete u bilo kojem polju boje",
"color.red": "Crvena",
"color.saturation": "Zasićenost (procenat)",
"color.saturation_value": "Zasićenost i vrijednost",
"color.selected": "Izabrana boja",
@@ -1305,6 +1323,8 @@
"color.step_up": "Povećaj za korak",
"color.value": "Vrijednost, svjetlina (procenat)",
"config.column_filter_empty": "Nema odgovarajućih kolona",
"config.rule_parameter_columns": "Prikazani su samo parametri koji imaju kolonu u tabeli. Dodajte kolonu pomoću Dodaj kolonu (+) da biste parametar koristili ovdje.",
"config.rule_suppression_columns": "Prikazane su samo operacije koje imaju kolonu Potisni u tabeli. Dodajte kolonu pomoću Dodaj kolonu (+) da biste ovdje mogli upravljati operacijom.",
"contact.components_count": "Komponente: {count}",
"contact.enabled_label": "omogućeno",
"coupling.gear_1": "Zupčanik 1",
@@ -1353,7 +1373,11 @@
"dialog.pattern_sketch_not_object": "Izaberite tijela ili operacije: skica nije ni jedno ni drugo. Ponovite njene krive pomoću alata Niz ili Preslikaj u samoj skici ili je prvo ekstrudirajte.",
"dialog.select": "↖ Izaberi",
"dialog.selected_count": "↖ Izabrano: {count}",
"dialog.selection_constraint": "Ograničenje {id}",
"dialog.selection_external_curve": "{sketch} · kriva {id}",
"dialog.selection_guidance": "Izaberite ulaz da ga promijenite. Kliknite izabrane objekte da ih uklonite; Ctrl/Cmd-klik ih također uključuje i isključuje. Pogledajte pregled, a zatim pritisnite U redu.",
"dialog.selection_midpoint": "Središnja tačka · {id}",
"dialog.selection_profile": "{sketch} · profil {number}",
"dialog.selection_step": "{step} od {total}: Izaberite {input}",
"dialog.step": "Korak",
"document.changed_on_disk": "{name} je promijenjen na disku",
@@ -1436,6 +1460,7 @@
"help.construct.axis.two_points": "Pravi pomoćnu osu kroz dvije tačke, vrhove ili tačke skice. Koristite je za osu koja prolazi između dva poznata položaja.",
"help.construct.plane.along_path": "Pravi pomoćnu ravan okomitu na ivicu ili krivu, na nekom položaju duž nje. Koristite je da skicirate profil za povlačenje ili presjek putanje.",
"help.construct.plane.at_angle": "Pravi pomoćnu ravan zakrenutu za ugao u odnosu na ravan ili lice, oko ose ili ivice. Koristite je za nagnute skice, npr. kutni nosač.",
"help.construct.plane.axis_point": "Pravi pomoćnu ravan koja sadrži osu i tačku izvan te ose. Izaberite osu ishodišta ili pomoćnu osu, ravnu ivicu ili liniju skice, pa vrh, tačku skice ili pomoćnu tačku. Tačka na osi ne može odrediti jedinstvenu ravan.",
"help.construct.plane.midplane": "Pravi pomoćnu ravan na pola puta između dvije ravni ili ravna lica, ili koja ih raspolavlja tamo gdje se sastaju pod uglom. Koristite je kao ravan simetrije za preslikavanje.",
"help.construct.plane.offset": "Pravi pomoćnu ravan paralelnu s ravni ili ravnim licem, na određenoj udaljenosti ili kroz tačku. Koristite je da skicirate na zadatoj visini iznad lica.",
"help.construct.plane.perpendicular": "Pravi pomoćnu ravan koja sadrži liniju ili ivicu i okomita je na ravan ili lice. Koristite je da skicirate uspravno na modelu duž ivice.",
@@ -1812,6 +1837,8 @@
"joint_move.blocked_hint": "Provjerite vrstu spoja (npr. Kruti), učvršćenje, granice spoja i vrijednosti koje pokreću parametri.",
"joint_move.blocked_title": "Ovaj dio se ne može pomjeriti",
"joint_move.coarse": "Korak rotacije blizu ose (°)",
"joint_move.conflict": "Jedan spoj ovog dijela je u sukobu s ostalima: {joint}.",
"joint_move.conflict_hint": "Uredite, potisnite ili izbrišite spoj koji je u sukobu da bi ostali spojevi mogli postaviti dio.",
"joint_move.distance": "Udaljenost od početka",
"joint_move.done": "Gotovo",
"joint_move.fine": "Korak rotacije dalje od ose (°)",
@@ -1911,6 +1938,8 @@
"motion.track_name": "Staza {number}",
"nav.alias.help": "Shift+Alt+povlačenje lijevim dugmetom orbitira, Shift+Alt+srednjim pomjera, Shift+Alt+desnim zumira; povlačenje srednjim dugmetom također pomjera, a desnim orbitira",
"nav.blender.help": "Povlačenje srednjim dugmetom orbitira, Shift+srednje pomjera, Ctrl+srednje zumira; povlačenje desnim dugmetom ne dira kameru",
"nav.creo.help": "Povlačenje srednjim dugmetom orbitira, Shift+srednje pomjera, Ctrl+srednje uz vertikalno povlačenje zumira (prema dolje za približavanje); točkić također zumira",
"nav.creo.label": "Creo",
"nav.free_orbit.help": "Miš i 3D miš rotiraju kroz oba pola i čuvaju nagib. Privlačenje osa tipkom Shift je isključeno.",
"nav.free_orbit.label": "Slobodno orbitiranje",
"nav.free_orbit.snap_help": "Miš i 3D miš rotiraju kroz oba pola i čuvaju nagib. Držite Shift tokom povlačenja za orbitiranje da privučete smjer pogleda i nagib.",
@@ -2063,12 +2092,19 @@
"parts.family.button_head_cap_screw": "Vijak s poluokruglom glavom i imbusom",
"parts.family.dowel_pin": "Cilindrični zatik",
"parts.family.hex_bolt": "Vijak sa šestougaonom glavom",
"parts.family.hex_cap_screw_inch": "Vijak sa šestougaonom glavom (inč)",
"parts.family.hex_nut": "Šestougaona matica",
"parts.family.hex_nut_inch": "Šestougaona matica (inč)",
"parts.family.set_screw": "Bezglavi vijak s imbusom, ravni vrh",
"parts.family.socket_head_cap_screw": "Vijak s cilindričnom glavom i imbusom",
"parts.family.washer": "Obična podloška",
"parts.family.washer_sae": "Obična podloška, SAE (uska)",
"parts.family.washer_uss": "Obična podloška, USS (široka)",
"parts.holes": "Rupe (neobavezno)",
"parts.placement_hint": "izaberite jednu ili više rupa da u svaku postavite dio, ili ga U redu postavlja u ishodište",
"parts.system": "Sistem jedinica",
"parts.system.inch": "Inčni",
"parts.system.metric": "Metrički",
"pattern.alignment_2": "Poravnanje 2",
"pattern.alignment_hint": "Broj uključuje original. Centar ravnomjerno raspoređuje kopije; Obrni bira dodatnu stranu za parne ukupne brojeve.",
"pattern.direction_1": "Smjer 1",
@@ -2130,6 +2166,8 @@
"prefs.button_left": "Povlačenje lijevim dugmetom",
"prefs.button_middle": "Povlačenje srednjim dugmetom",
"prefs.button_right": "Povlačenje desnim dugmetom",
"prefs.carousel_next": "Sljedeće",
"prefs.carousel_previous": "Prethodno",
"prefs.choose_font": "Izaberi datoteku fonta…",
"prefs.click": "Klik",
"prefs.contact_shading": "Meko sjenčenje dodira",
@@ -2142,6 +2180,8 @@
"prefs.ctrl_cmd": "Ctrl / Cmd",
"prefs.curved_surfaces": "Zakrivljene površi",
"prefs.curves": "Zakrivljene površi",
"prefs.cut_preview_tint": "Prikaži uklonjeni materijal u crvenoj boji",
"prefs.cut_preview_tint_help": "Pregled ispunjava materijal koji operacija uklanja i oblik koji zamjenjuje crvenom prozirnom sjenom. Kada je isključeno, te ispune su potpuno prozirne i crtaju se samo njihovi obrisi.",
"prefs.dark_parts_shading": "Zadrži oblik tamnih dijelova",
"prefs.dark_parts_shading_help": "Sjenči prikaz u linearnom svjetlu s mekim dopunskim svjetlom neba, tako da tamna lica zadrže gradijent umjesto da pocrne",
"prefs.dead_zone_note": "Šrafirana traka je mrtva zona: kretanje unutar nje se zanemaruje.",
@@ -2200,6 +2240,8 @@
"prefs.interface_language": "Jezik interfejsa",
"prefs.interface_note": "Kako SolidCraft izgleda i gdje su njegovi dijelovi.",
"prefs.interval_minutes": "{n} min",
"prefs.invert_curved_roll_arrows": "Obrni zakrivljene strelice za valjanje",
"prefs.invert_curved_roll_arrows_note": "Dvije zakrivljene strelice u uglu kocke valjaju prikaz unutar ravni ekrana. Uključeno: strelica u smjeru kazaljke na satu valja prikaz suprotno od smjera kazaljke na satu, a strelica suprotno od smjera kazaljke na satu valja ga u smjeru kazaljke na satu. Isključeno: svaka strelica valja prikaz onako kako je zakrivljena.",
"prefs.invert_navigator_arrows": "Obrni strelice za rotaciju kocke prikaza",
"prefs.invert_navigator_arrows_note": "Strelice oko kocke orbitiraju kameru u smjeru u kojem pokazuju, kao u programima Fusion i Inventor: strelica s lijeve strane pokazuje udesno i zakreće kameru udesno. Isključeno: strelica okreće kocku prema strani na kojoj se nalazi, pa lijeva strelica zakreće kameru ulijevo. Postavka ne utiče na zakrivljene strelice za valjanje.",
"prefs.keep_work_on_tab_switch": "Zadrži dijaloge i alate otvorenima pri prebacivanju između dizajna",
@@ -2259,6 +2301,8 @@
"prefs.placement": "Postavljanje",
"prefs.prefer_free_sketch_points": "Daj prednost pokretnim tačkama skice kada se tačke preklapaju",
"prefs.preview_choices": "Pregled vaših izbora",
"prefs.preview_tint_new_faces": "Oboji novu površ u pregledu",
"prefs.preview_tint_new_faces_help": "Pregled operacije koja mijenja tijelo može nacrtati površ koju dodaje u boji pregleda. Kada je isključeno, tijelo se crta kakvo jest.",
"prefs.print_face_highlight": "Isticanje površine za štampu",
"prefs.print_face_highlight_tip": "Koliko se jako površina za štampu tijela boji i obrubljuje u prikazu. 0 % ne prikazuje isticanje.",
"prefs.profile_active": "Aktivni profil",
@@ -2556,6 +2600,10 @@
"render.base_color": "Osnovna boja · sRGB",
"render.base_metallic": "Metalni udio",
"render.base_opaque_only": "Ovaj uređivač čuva semantiku neprozirnih tijela. Za optičko propuštanje koristite izričite kontrole stakla.",
"render.batch_all_shots": "Svi kadrovi ({count})",
"render.batch_empty": "Sačuvajte kadar prije renderovanja svih kadrova.",
"render.batch_limit": "Renderovanje svih kadrova ograničeno je na 32 kadra.",
"render.batch_status": "U toku: {running} · Na čekanju: {waiting}",
"render.bloom_enable": "Sjaj prikaza",
"render.bloom_help": "Kreativni sjaj samo na izlazu iz pozitivnih linearnih svijetlih dijelova, raširen kroz tri normalizovana prolaza kutijastog filtera. Poluprečnik je u pikselima izlaza. Dodaje se u linearnoj svjetlosti prikaza prije sRGB-a; nije objektiv, volumetrijski efekat niti poboljšanje konvergencije. Sirovi EXR ostaje nepromijenjen.",
"render.bloom_opaque": "Sjaj zahtijeva neprozirni izlaz. Prozirni PNG se odbija, a ne spljoštava tiho.",
@@ -2666,6 +2714,13 @@
"render.choose_look": "Prvo izaberite izgled u Izgledima",
"render.chromatic_offset": "Hromatski pomak ivica",
"render.clear_stage": "Ukloni sačuvanu scenu",
"render.compare_divider": "Razdjelnik poređenja",
"render.compare_live": "Uživo",
"render.compare_live_preview": "Pregled uživo",
"render.compare_render": "Renderovanje {number}",
"render.compare_side": "Renderovanje {number} · {shot}",
"render.compare_summary": "{shot} · {width} × {height} · PNG · {seconds} s · {renderer} · zabilježeno u reviziji {revision}",
"render.compare_swap": "Zamijeni strane",
"render.copy_request": "Kopiraj JSON",
"render.cpu_aa_help": "Prostorni uzorci: više uzoraka poboljšava ivice i produžava renderovanje.",
"render.cpu_area_add": "Dodaj pravougaono CPU svjetlo",
@@ -3415,6 +3470,10 @@
"render.result_retained": "Prikazuje se posljednja završena slika; novije renderovanje je još nije zamijenilo.",
"render.result_sequence": "Kontrolna tačka",
"render.result_title": "Rezultat renderovanja",
"render.results_choose": "Zadržani rezultati",
"render.results_label": "Renderovanje {number} · {shot} · {seconds} s",
"render.results_stale_label": "{result} [zastarjelo]",
"render.results_unnamed": "Slika",
"render.return_design": "Nazad na Dizajn",
"render.review_stale": "Ovaj pregled je zastario. Riješite ga ili ponovo provjerite prije primjene.",
"render.ribbon_aging": "Starenje",
@@ -4072,6 +4131,7 @@
"ui.agent_chat_auth_terminal": "Pokrenite ovo u terminalu, a zatim pritisnite Poveži ponovo:",
"ui.agent_chat_auth_title": "Prijavi se kod agenta",
"ui.agent_chat_cancel": "Otkaži",
"ui.agent_chat_cancel_timeout": "Agent nije odgovorio na Zaustavi pa je veza s njim prekinuta. Povežite se ponovo da biste nastavili.",
"ui.agent_chat_cannot_start": "Instalirano, ali se ne pokreće: {error}",
"ui.agent_chat_changes": "Promjene: {n}, jedan korak poništavanja",
"ui.agent_chat_changes_n": "Promjene: {n}",
@@ -4081,11 +4141,13 @@
"ui.agent_chat_command": "Komanda agenta",
"ui.agent_chat_connect": "Poveži",
"ui.agent_chat_copy": "Kopiraj",
"ui.agent_chat_copy_message": "Kopiraj poruku",
"ui.agent_chat_ctx_bodies": "Tijela",
"ui.agent_chat_ctx_elements": "Elementi",
"ui.agent_chat_ctx_features": "Operacije",
"ui.agent_chat_ctx_more": "+još {n}",
"ui.agent_chat_ctx_other": "Ostalo",
"ui.agent_chat_custom_heading": "Prilagođeni model",
"ui.agent_chat_custom_hint": "Bilo koji program koji govori Agent Client Protocol na stdin i stdout. Ne pokreće ga nikakva ljuska.",
"ui.agent_chat_denied": "Odbijeno",
"ui.agent_chat_deny": "Odbij",
@@ -4129,8 +4191,16 @@
"ui.agent_chat_mode_all": "Sve",
"ui.agent_chat_mode_ask": "Pitaj",
"ui.agent_chat_mode_edits": "Izmjene",
"ui.agent_chat_model_custom": "Koristi prilagođeni: {model}",
"ui.agent_chat_model_no_tools": "{model} ne može koristiti SolidCraft alate, pa može samo razgovarati. Izaberite model s podrškom za alate.",
"ui.agent_chat_model_rejected": "Agent nije prihvatio model {model}: {error}",
"ui.agent_chat_model_search": "Pretraži modele…",
"ui.agent_chat_model_tip": "Model koji agent koristi. Vaš izbor se pamti za ovog agenta.",
"ui.agent_chat_models_failed": "Popis modela nije moguće učitati: {error}",
"ui.agent_chat_models_heading": "Dostupni modeli",
"ui.agent_chat_models_listing": "Učitavanje popisa modela…",
"ui.agent_chat_models_none": "Krajnja tačka ne navodi nijedan model",
"ui.agent_chat_models_not_a_list": "Odgovor nije popis modela",
"ui.agent_chat_new": "Novi razgovor",
"ui.agent_chat_new_confirm": "Započeti novi razgovor? Trenutni će biti obrisan.",
"ui.agent_chat_next_picture": "Sljedeća slika",
@@ -4404,9 +4474,11 @@
"ui.construction.contact_hint": "Tangenta: izaberite krug/luk ili otvoreni splajn/koniku, pa tačku dodira (krive slobodnog oblika: krajnje tačke)",
"ui.construction.external_hint": "Tangenta: izaberite krug ili luk, vanjsku tačku, pa željeno rješenje",
"ui.construction.free_hint": "Pomoćna prava: kliknite dvije tačke; desnim klikom počnite ispočetka",
"ui.construction.normal_needs_length": "Beskonačna linija je uvijek pomoćna: upišite dužinu da biste nacrtali običnu liniju",
"ui.construction.point_hint": "Pomoćna prava: kliknite tačku kroz koju prolazi",
"ui.construction.reference_hint": "Pomoćna prava: izaberite pravu liniju, pa tačku; desnim klikom promijenite referencu",
"ui.construction.ribbon_label": "Pomoćne",
"ui.construction.side_hint": "Pomoćna prava: kliknite stranu prema kojoj linija ide; desnim klikom počnite ispočetka",
"ui.context_menu": "Kontekstni meni",
"ui.continuity_g0": "G0 pregib",
"ui.continuity_g1": "G1 tangentno",
@@ -4672,6 +4744,7 @@
"ui.hideOrigin": "Sakrij ishodište",
"ui.hidePlanes": "Sakrij ravni",
"ui.hideProfile": "Sakrij profil",
"ui.hide_component_history": "Sakrij njenu vremensku traku",
"ui.hide_origin_by_default": "Podrazumijevano sakrij ishodište",
"ui.hide_origin_note": "Novi dizajni počinju sa skrivenim folderom Ishodište. Dizajn zadržava vlastitu postavku Ishodišta kada ga prikažete ili sakrijete.",
"ui.history_back": "Idi korak nazad",
@@ -4679,6 +4752,7 @@
"ui.history_edge_hint": "dvaput kliknite ivicu",
"ui.history_first": "Idi na početak",
"ui.history_forward": "Idi korak naprijed",
"ui.history_hide_hidden": "Sakrij skrivene komponente",
"ui.history_hide_names": "Sakrij nazive",
"ui.history_last": "Idi na kraj",
"ui.history_marker_tip": "Oznaka vraćanja: povucite da vratite model unazad; nove operacije dolaze ovdje",
@@ -4688,10 +4762,12 @@
"ui.history_play_button_show": "Prikaži dugme za reprodukciju",
"ui.history_play_finish_action": "Završite trenutnu radnju prije reprodukcije vremenske trake",
"ui.history_play_roll_back": "Pomjerite oznaku vraćanja unazad prije reprodukcije",
"ui.history_show_hidden": "Prikaži skrivene komponente",
"ui.history_show_names": "Prikaži nazive",
"ui.history_sketch_listed_under": "Navedeno ispod {feature}",
"ui.history_title": "Vremenska traka",
"ui.history_toggle_names": "Prikaži ili sakrij nazive operacija",
"ui.history_waits_for_editor": "Poništi, Ponovi i vremenska traka čekaju dok se ne otvori uređivač operacije",
"ui.hole": "Rupa",
"ui.hole_tap_type": "Vrsta navoja rupe",
"ui.hole_type": "Vrsta rupe",
@@ -4972,6 +5048,7 @@
"ui.showDimension": "Prikaži kotu",
"ui.showOrigin": "Prikaži ishodište",
"ui.showPlanes": "Prikaži ravni",
"ui.show_component_history": "Prikaži njenu vremensku traku",
"ui.show_constraints": "Ograničenja",
"ui.show_construction_geometries": "Pomoćna geometrija",
"ui.show_dimensions": "Kote",
@@ -5198,6 +5275,7 @@
"web.open_link": "Otvoriti povezani dizajn?",
"web.open_link_confirm": "Postoje nesačuvane promjene. Otvoriti povezani dizajn u novoj kartici?",
"↑ ↓ choose · Enter / Tab complete · Esc dismiss": "↑ ↓ izaberi · Enter / Tab dovrši · Esc odbaci",
"↑ ↓ choose · Enter applies this value": "↑ ↓ izbor · Enter primjenjuje ovu vrijednost",
"↑ ↓ choose · Enter applies what you typed · Esc dismiss": "↑ ↓ izaberi · Enter primjenjuje ono što ste upisali · Esc odbaci",
"⌘Enter continues on GitHub.": "⌘Enter nastavlja na GitHubu."
}
+236 -54
View File
@@ -379,7 +379,7 @@
"Move Type": "Verschiebungstyp",
"Move down": "Nach unten",
"Move earlier": "Nach vorn verschieben",
"Move holes by clicking or dragging": "Bohrungen durch Anklicken oder Ziehen verschieben",
"Move holes by clicking or dragging": "Durch Klicken oder Ziehen verschieben",
"Move later": "Nach hinten verschieben",
"Move up": "Nach oben",
"Name": "Name",
@@ -585,7 +585,7 @@
"Show ghost": "Geistermodell anzeigen",
"Show in folder": "Im Ordner anzeigen",
"Show the pointer an AI agent moves (#35)": "Den Zeiger anzeigen, den ein KI-Agent bewegt (#35)",
"Show tool at cursor": "Werkzeug am Cursor anzeigen",
"Show tool at cursor": "Werkzeug am Mauszeiger",
"Signed axes": "Vorzeichenbehaftete Achsen",
"Size": "Größe",
"Sketch constraints": "Skizzenbedingungen",
@@ -596,7 +596,7 @@
"Sketch points": "Skizzenpunkte",
"Slider": "Schubgelenk",
"Slot width": "Langlochbreite",
"Snap to face and edge centres": "An Flächen- und Kantenmittelpunkten einrasten",
"Snap to face and edge centres": "An Mittelpunkten einrasten",
"SolidCraft cannot submit reports directly. Submit on GitHub; no token is needed in the app.": "SolidCraft kann Berichte nicht direkt einreichen. Reichen Sie Ihren Bericht auf GitHub ein. In der App wird dafür kein Token benötigt.",
"SolidCraft closed before these designs were saved. Their last autosave can be opened.": "SolidCraft wurde beendet, bevor diese Designs gespeichert wurden. Ihr letzter Autosave kann geöffnet werden.",
"Sort A to Z": "Von A bis Z sortieren",
@@ -739,36 +739,36 @@
"a picked edge's body is gone": "der Körper einer gewählten Kante existiert nicht mehr",
"add a key for a joint first": "zuerst einen Schlüssel für ein Gelenk hinzufügen",
"ai.connected_toast": "SolidCraft ist jetzt in {clients} verfügbar. Starten Sie diese neu, um es zu nutzen (Einstellungen > Assistent).",
"ai.prefs.allow_control": "Verbindungen von KI-Agenten zulassen (Steuerkanal / MCP)",
"ai.prefs.allow_control_note": "Öffnet einen Steuerkanal nur auf diesem Computer (127.0.0.1, Port 27878 oder der nächste freie). Ein verbindendes Programm muss zusätzlich ein geheimes Token vorweisen, das nur Ihr Benutzerkonto lesen kann und bei jedem Start neu erzeugt wird. Wirkt ab dem nächsten Start von SolidCraft. Aus: Es wartet nichts auf Verbindungen, außer Sie starten SolidCraft mit --control.",
"ai.prefs.availability": "Assistenten-Steuerung",
"ai.prefs.background_commands": "Agentenbefehle im Hintergrund ausführen",
"ai.prefs.connect_apps": "SolidCraft für KI-Apps auf diesem Computer verfügbar machen",
"ai.prefs.connect_apps_note": "Trägt SolidCraft beim Start als MCP-Server in die auf diesem Computer gefundenen KI-Apps ein (OpenCode, Claude Desktop, Claude Code, Cursor, Codex, Gemini CLI, Copilot CLI, Windsurf, Zed, VS Code), damit ein Agent es ohne Einrichtung nutzen kann. Nur der Eintrag „solidcraft“ wird hinzugefügt oder sein Pfad aktualisiert; ein von Ihnen geänderter Eintrag bleibt unberührt, und die Datei wird vorher gesichert. Aus: Die hinzugefügten Einträge werden entfernt. Hilfe > KI-Assistenten verbinden… erledigt das von Hand.",
"ai.prefs.connect_at_start": "Zuletzt verwendeten Agenten beim Start von SolidCraft verbinden",
"ai.prefs.allow_control": "KI-Agenten verbinden lassen",
"ai.prefs.allow_control_note": "Ein lokaler Kanal (MCP). Nichts verlässt diesen Computer.\n\nÖffnet einen Steuerkanal nur auf diesem Computer (127.0.0.1, Port 27878 oder der nächste freie). Ein verbindendes Programm muss zusätzlich ein geheimes Token vorweisen, das nur Ihr Benutzerkonto lesen kann und bei jedem Start neu erzeugt wird. Wirkt ab dem nächsten Start von SolidCraft. Aus: Es wartet nichts auf Verbindungen, außer Sie starten SolidCraft mit --control.",
"ai.prefs.availability": "Verfügbarkeit",
"ai.prefs.background_commands": "Langsame Befehle im Hintergrund ausführen",
"ai.prefs.connect_apps": "SolidCraft zu KI-Apps hinzufügen",
"ai.prefs.connect_apps_note": "Aus entfernt die Einträge wieder.\n\nTrägt SolidCraft beim Start als MCP-Server in die auf diesem Computer gefundenen KI-Apps ein (OpenCode, Claude Desktop, Claude Code, Cursor, Codex, Gemini CLI, Copilot CLI, Windsurf, Zed, VS Code), damit ein Agent es ohne Einrichtung nutzen kann. Nur der Eintrag „solidcraft“ wird hinzugefügt oder sein Pfad aktualisiert; ein von Ihnen geänderter Eintrag bleibt unberührt, und die Datei wird vorher gesichert. Aus: Die hinzugefügten Einträge werden entfernt. Hilfe > KI-Assistenten verbinden… erledigt das von Hand.",
"ai.prefs.connect_at_start": "Letzten Agenten beim Start verbinden",
"ai.prefs.connect_at_start_note": "Verbindet beim Start von SolidCraft automatisch den zuletzt verwendeten Agenten (oder den Standardagenten) im Hintergrund, damit er für den Chat bereitsteht. Diese Funktion ist deaktiviert, wenn SolidCraft mit --control gestartet wird. Kann keine Verbindung hergestellt werden, zeigt der Assistent „Verbindung fehlgeschlagen“ und eine Schaltfläche zum erneuten Verbindungsversuch an.",
"ai.prefs.follow_camera": "Kamera zu Skizzen drehen",
"ai.prefs.follow_camera": "Der Kamera folgen",
"ai.prefs.follow_camera_off": "Die automatische Kameraführung ist aus.",
"ai.prefs.follow_camera_on": "Die Kamera dreht sich zu Skizzen, die der Agent erstellt.",
"ai.prefs.intro": "Agenten, Berechtigungen und wie ihre Arbeit angezeigt wird.",
"ai.prefs.make_default": "Standardmäßig verwenden",
"ai.prefs.intro": "KI-Agenten in SolidCraft.",
"ai.prefs.make_default": "Als Standard festlegen",
"ai.prefs.no_agents": "Noch keine Agenten. Fügen Sie einen Agenten hinzu oder aktualisieren Sie, um installierte Agenten zu finden.",
"ai.prefs.no_saved_approvals": "Keine gespeicherten Freigaben.",
"ai.prefs.open_at_start": "Assistentenbereich beim Start von SolidCraft öffnen",
"ai.prefs.open_at_start": "Chat beim Start öffnen",
"ai.prefs.open_at_start_note": "Öffnet beim Start von SolidCraft automatisch den Assistentenbereich oder dessen separates Fenster. Diese Funktion ist deaktiviert, wenn SolidCraft mit --control gestartet wird.",
"ai.prefs.override_all_warning": "Dieser Agent darf das Design ohne Rückfrage speichern, überschreiben, exportieren oder ersetzen.",
"ai.prefs.permission_all": "Alles erlauben",
"ai.prefs.permission_all_summary": "Verfügbare SolidCraft-Werkzeuge werden ohne Rückfrage ausgeführt.",
"ai.prefs.permission_ask": "Vor Änderungen fragen",
"ai.prefs.permission_ask_summary": "Jede Designänderung und Dateiaktion bestätigen.",
"ai.prefs.permission_edits": "Modelländerungen erlauben",
"ai.prefs.permission_edits_summary": "Modelländerungen werden ohne Rückfrage ausgeführt; Datei- und destruktive Aktionen fragen weiterhin nach.",
"ai.prefs.permission_all": "Alles ausführen",
"ai.prefs.permission_all_summary": "Fragt nie nach",
"ai.prefs.permission_ask": "Zuerst fragen",
"ai.prefs.permission_ask_summary": "Fragt bei jeder Änderung",
"ai.prefs.permission_edits": "Änderungen vornehmen",
"ai.prefs.permission_edits_summary": "Ändert frei, fragt beim Rest",
"ai.prefs.permission_scope": "Gilt nur für SolidCraft-Werkzeuge. Shell und Dateiänderungen des Agenten unterliegen dessen eigenen Berechtigungen.",
"ai.prefs.pointer_preview_hidden": "Zeiger ausgeblendet",
"ai.prefs.pointer_preview_speed": "Agentenzeiger · {speed}",
"ai.prefs.pointer_speed": "Bewegungsgeschwindigkeit",
"ai.prefs.pointer_speed": "Geschwindigkeit",
"ai.prefs.privacy_note": "Die Unterhaltung geht nur an den von Ihnen gewählten Agenten oder API-Endpunkt. API-Schlüssel werden im Schlüsselbund des Systems gespeichert.",
"ai.prefs.show_pointer": "Agentenzeiger anzeigen",
"ai.prefs.show_pointer": "Zeiger des Agenten anzeigen",
"ai.prefs.turn_limit_hint": "Höchstzahl designändernder Werkzeugaufrufe, die der Assistent für eine Nachricht ausführen darf.",
"angle, height": "Winkel, Höhe",
"appearance.dielectric_ior": "Brechungsindex des Dielektrikums",
@@ -833,6 +833,8 @@
"cmd.appearance_pick_up": "Aussehen aufnehmen",
"cmd.appearance_replace": "Erscheinungsbild ersetzen",
"cmd.assembly_bom": "Stückliste",
"cmd.assembly_bom_item_set": "Positionsnummer ändern",
"cmd.assembly_bom_renumber": "Positionen neu nummerieren",
"cmd.assembly_mass_properties": "Masseeigenschaften",
"cmd.body.print_face": "Auflagefläche",
"cmd.body_rename": "Körper umbenennen",
@@ -992,6 +994,11 @@
"cmd.joint_screw": "Schraube",
"cmd.joint_solve": "Gelenke lösen",
"cmd.joint_sweep": "Gelenk bis zum Kontakt durchfahren",
"cmd.layout.apply": "Layout anwenden",
"cmd.layout.get": "Layout prüfen",
"cmd.layout.reset": "Layout zurücksetzen",
"cmd.layout.save": "Layout speichern",
"cmd.layout.workspace.select": "Layout-Arbeitsbereich auswählen",
"cmd.library_apply": "Lokalen Bibliothekseintrag anwenden",
"cmd.library_export": "Designbibliothek exportieren",
"cmd.library_inspect": "Lokale Bibliothek prüfen",
@@ -1019,12 +1026,28 @@
"cmd.motion_trace_edit": "Bewegungsbahn bearbeiten",
"cmd.motion_trace_keep": "Bewegungsbahn behalten",
"cmd.occurrence_align": "Instanz ausrichten",
"cmd.occurrence_bom_type": "Stücklistentyp",
"cmd.occurrence_copy": "Einfügen (Instanz)",
"cmd.occurrence_drag": "Instanz ziehen",
"cmd.occurrence_ground": "Festsetzen",
"cmd.occurrence_move": "Instanz verschieben",
"cmd.occurrence_properties": "Physikalische Eigenschaften der Instanz",
"cmd.occurrence_reparent": "In andere Komponente verschieben",
"cmd.panel.activate": "Panel-Registerkarte aktivieren",
"cmd.panel.close": "Panel schließen",
"cmd.panel.collapse": "Panel-Bereich einklappen",
"cmd.panel.dismiss": "Panel-Vorschau schließen",
"cmd.panel.float": "Panel frei platzieren",
"cmd.panel.focus": "Panel fokussieren",
"cmd.panel.list": "Panels auflisten",
"cmd.panel.move": "Panel verschieben",
"cmd.panel.native_close": "Nativen Panel-Bereich wieder andocken",
"cmd.panel.open": "Panel öffnen",
"cmd.panel.peek": "Eingeklapptes Panel anzeigen",
"cmd.panel.popout": "Panel in eigenes Fenster verschieben",
"cmd.panel.reset_pair": "Panel-Trennlinie zurücksetzen",
"cmd.panel.resize": "Panel-Bereich skalieren",
"cmd.panel.resize_pair": "Panel-Trennlinie verschieben",
"cmd.parameters_add": "Benutzerparameter hinzufügen",
"cmd.parameters_change": "Parameter ändern",
"cmd.parameters_comment": "Parameter kommentieren",
@@ -1371,6 +1394,9 @@
"dialog.custom_axis": "↖ Eigene Achse",
"dialog.dimension_insert_hint": "Doppelklicken Sie auf eine andere steuernde Bemaßung, um ihren Namen einzufügen",
"dialog.dimension_name": "Bemaßung: {name}",
"dialog.dimension_step_down": "Um {step} verringern",
"dialog.dimension_step_expression": "Nur eine Zahl lässt sich schrittweise ändern; ein Ausdruck bleibt, wie er eingegeben ist",
"dialog.dimension_step_up": "Um {step} erhöhen",
"dialog.edit_title": "{title} bearbeiten",
"dialog.no_shown_body": "Kein sichtbarer Körper zum Ändern vorhanden. Blenden Sie einen Körper ein oder wählen Sie „Neuer Körper“.",
"dialog.pattern_sketch_not_object": "Körper oder Funktionen wählen: Eine Skizze ist keines von beiden. Ihre Kurven mit Muster oder Spiegeln in der Skizze wiederholen oder sie zuerst extrudieren.",
@@ -1395,6 +1421,7 @@
"document.read_only_save": "Dieses Design wurde schreibgeschützt geöffnet: Speichern Sie es als neue Datei",
"document.save_changes": "Änderungen in {name} speichern?",
"document.save_copy": "Kopie speichern…",
"document.tab_unsaved_changes": "Ungespeicherte Änderungen",
"document.unsaved_changes": "{name} enthält ungespeicherte Änderungen. Wenn Sie nicht speichern, gehen sie verloren.",
"drive.rigid": "{joint} ist ein starres Gelenk. Es hält die verbundenen Teile fest zusammen und ermöglicht daher keine Bewegung. Um die Teile beweglich zu machen, ändern Sie den Gelenktyp unter „Gelenk bearbeiten“: Drehgelenk für Drehbewegungen, Schubgelenk für lineare Bewegungen oder Zylindergelenk für beides.",
"drive.stopped_by_contact": "Angehalten bei {value}: {a} würde {b} durchdringen",
@@ -1531,6 +1558,11 @@
"help.joint.reach": "Bewegt einen Punkt eines Teils auf ein Ziel zu, indem die Gelenke seiner Kette innerhalb ihrer Grenzen verstellt werden. Damit stellen Sie einen Mechanismus über die Lage seines Endes ein, etwa der Spitze eines Roboterarms.",
"help.joint.rigid_group": "Verbindet die gewählten Teile starr in ihrer jetzigen Lage, sodass sie sich als Einheit bewegen. Für Teile, die zusammengehören und sich gegeneinander nicht bewegen.",
"help.joint.screw": "Koppelt Drehen und Schieben über eine Steigung, sodass jede Umdrehung um diesen Weg vorrückt. Für Gewindespindeln, Schrauben und Trapezspindeln.",
"help.layout.apply": "Ein gespeichertes Layout auf den aktuellen Arbeitsbereich anwenden. Geöffnete Editoren behalten ihre Entwürfe.",
"help.layout.get": "Das übernommene Layout lesen und registrierte Arbeitsbereiche anzeigen.",
"help.layout.reset": "Das registrierte Standardlayout des aktuellen Arbeitsbereichs wiederherstellen. Eigene Vorlagen bleiben erhalten.",
"help.layout.save": "Das vollständige Layout unter einem Namen speichern. Eine vorhandene eigene Vorlage wird nur mit ausdrücklicher Erlaubnis überschrieben.",
"help.layout.workspace.select": "Einen registrierten Layout-Arbeitsbereich auswählen und das bisherige Layout merken.",
"help.library.apply": "Wendet ein Material oder eine Darstellung aus einer lokalen Bibliotheksdatei auf Körper, Flächen oder Komponenten an. Damit nutzen Sie Materialien aus einem anderen Design wieder.",
"help.library.export": "Speichert die Materialien und Darstellungen des Designs in einer Bibliotheksdatei. Damit teilen Sie sie oder nutzen sie in anderen Designs wieder.",
"help.library.inspect": "Prüft eine Bibliotheksdatei und listet ihre Materialien und Darstellungen auf, ohne das Design zu ändern. Damit sehen Sie vor dem Anwenden, was eine Bibliothek enthält.",
@@ -1544,6 +1576,21 @@
"help.notice": "NOTICE",
"help.occurrence.ground": "Setzt ein Teil an seiner Stelle fest oder gibt es wieder frei, sodass Gelenke die übrigen Teile um es herum bewegen. Für den Rahmen oder Sockel, auf dem der Rest der Baugruppe aufbaut.",
"help.occurrence.move": "Bewegt eine Baugruppeninstanz ausschließlich entlang der Freiheitsgrade, die ihre Gelenke zulassen. Wählen Sie eine Fläche des Bauteils aus und ziehen Sie anschließend einen der verfügbaren Pfeile oder Bögen.",
"help.panel.activate": "Eine geöffnete Panel-Registerkarte auswählen, ohne den Tastaturfokus anzufordern.",
"help.panel.close": "Ein Panel unter Beachtung seines Inhaltslebenszyklus schließen. Seine Platzierung wird gespeichert.",
"help.panel.collapse": "Einen Panel-Bereich ein- oder ausklappen und seine gewünschten Abmessungen beibehalten.",
"help.panel.dismiss": "Die vorübergehende Ansicht eines eingeklappten Panels schließen.",
"help.panel.float": "Ein Panel in einen frei positionierten Bereich verschieben.",
"help.panel.focus": "Ein Panel fokussieren, seinen gespeicherten Zustand bei Bedarf wieder öffnen und seinen Bereich ausklappen.",
"help.panel.list": "Registrierte Panel-Arten, übernommene Instanzen, aktive Registerkarten, Verfügbarkeit und Host-Funktionen lesen.",
"help.panel.move": "Ein Panel in einen neuen Bereich verschieben oder einer vorhandenen Registerkartengruppe hinzufügen.",
"help.panel.native_close": "Einen nativen Panel-Bereich an seine gespeicherte Position zurückbringen, ohne seinen Inhalt abzubrechen.",
"help.panel.open": "Eine Panel-Art mit einer ausdrücklichen Instanzkennung öffnen. Neue Instanzen benötigen eine Platzierung.",
"help.panel.peek": "Ein eingeklapptes Panel vorübergehend anzeigen, ohne seine gespeicherte Größe oder seinen Klappzustand zu ändern.",
"help.panel.popout": "Ein Panel bei Unterstützung in ein natives Fenster verschieben. Im Browser wird ein frei positionierter Bereich verwendet.",
"help.panel.reset_pair": "Das Standardgrößenverhältnis zweier benachbarter Andockbereiche innerhalb ihrer gemeinsamen Grenzen wiederherstellen.",
"help.panel.resize": "Die äußere Breite und Höhe eines Panel-Bereichs innerhalb der Grenzen aller Registerkarten festlegen.",
"help.panel.resize_pair": "Benachbarte Andockbereiche gemeinsam skalieren und ihre gemeinsame Ausdehnung beibehalten.",
"help.parameters.change": "Öffnet die Tabelle mit benannten Parametern und Ausdrücken, die die Abmessungen des Designs steuern. Ändern Sie einen Wert an einer zentralen Stelle, werden alle Features, die diesen Wert verwenden, entsprechend aktualisiert.",
"help.parameters.export": "Speichert die Parameter des Designs als CSV oder JSON. Damit bearbeiten Sie sie in einer Tabellenkalkulation oder übertragen sie in ein anderes Design.",
"help.parameters.graph": "Zeigt, welche Parameter und Elemente welche anderen nutzen, samt Schleifen oder Fehlern. Damit sehen Sie, was eine Parameteränderung beeinflusst.",
@@ -1868,21 +1915,21 @@
"joint.rigid_group_select_components": "Wählen Sie zwei oder mehr Komponenten aus.",
"joint_move.blocked_hint": "Prüfen Sie den Gelenktyp (z. B. „Starr“), die Fixierung, die Gelenkgrenzen und die durch Parameter gesteuerten Werte.",
"joint_move.blocked_title": "Dieses Bauteil kann nicht bewegt werden",
"joint_move.coarse": "Drehschritt nahe der Achse (°)",
"joint_move.coarse": "Schritt nahe der Achse",
"joint_move.conflict": "Ein Gelenk dieses Bauteils steht im Konflikt mit den anderen: {joint}.",
"joint_move.conflict_hint": "Bearbeiten, unterdrücken oder löschen Sie das widersprüchliche Gelenk, damit die anderen Gelenke das Bauteil platzieren können.",
"joint_move.distance": "Abstand vom Ausgangspunkt",
"joint_move.done": "Fertig",
"joint_move.fine": "Drehschritt weit von der Achse entfernt (°)",
"joint_move.fine": "Schritt fern der Achse",
"joint_move.fixed": "Dieses Bauteil verfügt über keine bewegliche Gelenkachse.",
"joint_move.help": "Verschieben: Ziehen Sie einen Pfeil oder Ring. Mit Esc brechen Sie den aktuellen Ziehvorgang ab.",
"joint_move.legacy_hint": "Deaktivieren, um wieder den bisherigen Verschiebebereich zu verwenden.",
"joint_move.precision": "Ziehen Sie außerhalb der gestrichelten Führung, um die Drehung fein einzustellen.",
"joint_move.legacy_hint": "Aus: das bisherige Verschieben-Panel.",
"joint_move.precision": "Für den feinen Schritt außerhalb der gestrichelten Führung ziehen.",
"joint_move.rotation": "Drehwinkel vom Ausgangspunkt",
"joint_move.select": "Wählen Sie eine Fläche oder eine Baugruppenkomponente aus und anschließend „Verschieben“.",
"joint_move.settings": "Baugruppe verschieben",
"joint_move.step": "Schrittweite",
"joint_move.use_handles": "Gelenkgriffe zum Bewegen von Baugruppen verwenden",
"joint_move.use_handles": "Gelenkbewegungsgriffe für Verschieben verwenden",
"keymap.command_id": "Befehls-ID",
"keymap.empty": "Keine passenden Befehle",
"keymap.use_anyway": "{key} hier trotzdem verwenden",
@@ -1942,6 +1989,7 @@
"material.selection.mixed": "Unterschiedliche Materialien",
"measure.at": "Bei",
"measure.item": "Element {number}",
"measure.line_to_centre": "Linie zum Mittelpunkt",
"measure.quick_copied": "Messwert kopiert",
"measure.quick_copy": "Klicken, um den Messwert zu kopieren",
"measure.quick_edges": "{count} Kanten",
@@ -2000,6 +2048,12 @@
"navbar.assistant": "Assistent",
"navbar.custom.add_slot": "{slot} hinzufügen",
"navbar.custom.always": "Immer sichtbar",
"navbar.custom.anchor.bottom_centre": "Unten mittig",
"navbar.custom.anchor.bottom_left": "Unten links",
"navbar.custom.anchor.bottom_right": "Unten rechts",
"navbar.custom.anchor.top_centre": "Oben mittig",
"navbar.custom.anchor.top_left": "Oben links",
"navbar.custom.anchor.top_right": "Oben rechts",
"navbar.custom.available": "{available} · auf die Leiste ziehen oder auf Hinzufügen klicken",
"navbar.custom.buttons": "Schaltflächen",
"navbar.custom.compact": "Kompakt",
@@ -2036,6 +2090,7 @@
"navbar.custom.reset_default": "Auf Standard zurücksetzen",
"navbar.custom.right": "Nach rechts",
"navbar.custom.select": "Kein Feld ausgewählt. Wählen Sie ein Feld in der Leiste, um es zu verschieben, zu entfernen oder sein Aussehen zu ändern.",
"navbar.custom.separator_hint": "Eine Trennlinie zwischen Gruppen von Schaltflächen. Auf die Leiste ziehen oder auf Hinzufügen klicken; beliebig oft verwendbar.",
"navbar.custom.separators_full": "Die Leiste hat bereits vier Trennlinien.",
"navbar.custom.show": "Navigationsleiste anzeigen",
"navbar.custom.shows": "Zeigt",
@@ -2189,14 +2244,30 @@
"prefs.appearance.viewport_logo": "Ansichtsfenster und Logo",
"prefs.applies_on_restart": "Wird nach einem Neustart übernommen",
"prefs.assistant": "Assistent",
"prefs.assistant_connecting": "Verbindung",
"prefs.assistant_follow_help": "Look At bei Skizzen bewegt die Ansicht mit dem Zeiger.",
"prefs.assistant_limit": "Limit",
"prefs.assistant_limited": "Begrenzt",
"prefs.assistant_pointer_speed": "Zeigergeschwindigkeit",
"prefs.assistant_seconds": "Sekunden",
"prefs.assistant_seconds_aria": "Sekunden",
"prefs.assistant_use_care": "Mit Vorsicht verwenden",
"prefs.auto_operation": "Operation automatisch auswählen",
"prefs.auto_operation_note": "Die Feature-Dialoge wählen automatisch zwischen „Verbinden“, „Neuer Körper“ und „Ausschneiden“, abhängig davon, was das neue Material berührt. Sobald Sie selbst eine Operation auswählen, wird diese Einstellung nicht mehr angewendet. Deaktiviert: Die beim Öffnen des Dialogs voreingestellte Operation wird beibehalten.",
"prefs.auto_operation_note": "Vereinigung, Neuer Körper oder Schnitt, je nachdem, was das Material berührt.",
"prefs.axes": "Achsen",
"prefs.axis": "Achse",
"prefs.blur_cost": "Relativer Renderaufwand. Mehr Balken bedeuten meist mehr Arbeit.",
"prefs.blur_device": "Auf diesem Grafikgerät oder Renderpfad nicht verfügbar",
"prefs.blur_guard": "Die Unschärfequalität wurde für flüssigere Bilder verringert. Der Radius bleibt unverändert.",
"prefs.blur_off": "Aus",
"prefs.blur_reduce_transparency": "Durch die Systemeinstellung zur Transparenzreduzierung deaktiviert",
"prefs.blur_software": "Mit Softwaregrafik nicht verfügbar (gpu-software)",
"prefs.blur_source": "In dieser Arbeitsbereichsansicht nicht verfügbar",
"prefs.blur_web": "Auf diesem Web-Renderpfad nicht verfügbar",
"prefs.bodies_folders": "Körperordner",
"prefs.bodies_folders_always": "Immer",
"prefs.bodies_folders_never": "Nie",
"prefs.bodies_folders_several": "Nur bei mehreren Körpern",
"prefs.bodies_folders_several": "Mehrere",
"prefs.bottom": "Unten",
"prefs.bottom_centre": "Unten mittig",
"prefs.bottom_right": "Unten rechts",
@@ -2232,15 +2303,34 @@
"prefs.dark_parts_shading_help": "Die Ansicht in linearem Licht mit sanftem Himmelslicht schattieren, damit dunkle Flächen einen Verlauf behalten statt schwarz zu werden",
"prefs.dead_zone_note": "Der schraffierte Bereich kennzeichnet die Totzone. Bewegungen innerhalb dieses Bereichs werden ignoriert.",
"prefs.default": "Standard",
"prefs.design": "Design-Vorgaben",
"prefs.design_note": "Legt fest, mit welchen Einstellungen neue Designs beginnen. Bereits geöffnete Designs behalten ihre eigenen Einstellungen.",
"prefs.design": "Konstruktion",
"prefs.design_note": "Womit neue Konstruktionen beginnen.",
"prefs.design_tools.ask": "Fragen",
"prefs.design_tools.degrees": "Grad",
"prefs.design_tools.double_click_help": "Oder zum Bearbeiten einer verknüpften Komponente an Ort und Stelle.",
"prefs.design_tools.ghost": "Alte Position verschobener Teile als Geisterbild zeigen",
"prefs.design_tools.hole_moves_help": "Erneutes Auswählen der Fläche verschiebt die Bohrung, statt sie zu entfernen.",
"prefs.design_tools.hole_snaps_help": "Die Mitte einer Fläche und die Mittelpunkte ihrer kreisförmigen Kanten.",
"prefs.design_tools.imported_files": "Importierte Dateien",
"prefs.design_tools.note": "Hinweis",
"prefs.design_tools.off": "Aus",
"prefs.design_tools.placement_group": "Auflage, Zapfen, Schnappverbindung",
"prefs.design_tools.rows": "Zeilen",
"prefs.devices": "3D-Maus",
"prefs.dialog_buttons": "Dialogschaltflächen",
"prefs.dialogs": "Dialoge",
"prefs.dialogs_edge": "Am Rand der Ansicht",
"prefs.dialogs_left_column": "In der linken Spalte",
"prefs.dialogs_note": "Wo Befehlsdialoge öffnen: am Rand der Ansicht angedockt oder in der linken Spalte anstelle von Browser und Verlauf, die beim Schließen des Dialogs wie zuvor zurückkehren.",
"prefs.dimension_step_angle": "Was die Pfeile des Fensters, Auf/Ab und das Mausrad zu einem Winkel addieren",
"prefs.dimension_step_length": "Was die Pfeile des Fensters, Auf/Ab und das Mausrad zu einer Länge addieren",
"prefs.dimension_steps": "Schritte im Fenster",
"prefs.dimension_values": "Bemaßungswerte",
"prefs.dimension_values_note": "Wo der Wert einer Bemaßung eingegeben wird. Ein Fenster hat Pfeile, die ihn schrittweise ändern; das Bemaßungswerkzeug platziert zuerst den Text.",
"prefs.dimension_values_view": "In der Ansicht",
"prefs.dimension_values_window": "In einem Fenster",
"prefs.display": "Anzeige",
"prefs.drawings": "Zeichnungen",
"prefs.edges": "Kanten und Schattierung",
"prefs.extend": "Erweitern",
"prefs.extra_navigation": "Mit Strg und gedrückter linker Maustaste auf einer freien Fläche drehen. Leertaste gedrückt halten, um die Ansicht zu verschieben.",
@@ -2275,6 +2365,23 @@
"prefs.graphics_card_restart": "Diese Einstellung wird nach einem Neustart von SolidCraft wirksam. Die Umgebungsvariable WGPU_ADAPTER_NAME hat Vorrang vor dieser Auswahl.",
"prefs.graphics_card_unused": "Die gespeicherte Grafikkarte wurde für dieses Fenster nicht verwendet; die tatsächlich genutzte Karte steht unter Hilfe › Info und in der Statuszeile. Versuchen Sie Automatisch, oder setzen Sie WGPU_ADAPTER_NAME auf einen eindeutigen Teil des Kartennamens.",
"prefs.graphics_note": "Wägen Sie Leistung und Darstellungsqualität gegeneinander ab. Jede Option veranschaulicht ihre Wirkung anhand eines Beispiels.",
"prefs.graphics_page.current_view": "Aktuelle Ansicht",
"prefs.graphics_page.fastest": "Am schnellsten",
"prefs.graphics_page.fold_items": "Elemente einklappen",
"prefs.graphics_page.in_view": "In der Ansicht",
"prefs.graphics_page.live_details": "Live-Details",
"prefs.graphics_page.own_mix": "Eigene Mischung",
"prefs.graphics_page.quick_measure": "Schnellmessung",
"prefs.graphics_page.reference_geometry_through_bodies": "Referenzgeometrie durch Körper hindurch",
"prefs.graphics_page.restart": "Neustart erforderlich",
"prefs.graphics_page.sharpest": "Am schärfsten",
"prefs.graphics_page.single_surface_analysis": "Einzelne Flächenanalyse",
"prefs.graphics_page.status_chips": "Status-Chips",
"prefs.graphics_page.subtitle": "Geschwindigkeit gegen Glätte abwägen.",
"prefs.graphics_page.supersampling": "Supersampling",
"prefs.graphics_page.supersampling_adaptive": "Nur solange die Ansicht stillsteht",
"prefs.graphics_page.surfaces_edges": "Flächen und Kanten",
"prefs.graphics_page.view_bar": "Auch in der Ansichtsleiste",
"prefs.graphics_presets_note": "Mit den Profilen werden die unten aufgeführten Optionen gemeinsam eingestellt.",
"prefs.hide_constraint_glyphs_zoomed_out": "Bindungssymbole bei starkem Herauszoomen ausblenden",
"prefs.hide_constraint_glyphs_zoomed_out_note": "Bindungssymbole verschwinden, wenn die Skizze so weit herausgezoomt ist, dass sie sie überladen würden. Aus: sie behalten bei jedem Zoom eine feste Bildschirmgröße.",
@@ -2283,7 +2390,7 @@
"prefs.history_origin_bodies_note": "Der Verlauf links oder unter dem Browser beginnt mit dem Körper-Ordner der aktiven Komponente und dem Ursprung, mit Augen und Menüs wie im Browser. Der Verlauf unten bleibt unverändert.",
"prefs.history_play_button": "Schaltfläche „Verlauf abspielen“ in den Verlaufssteuerelementen",
"prefs.history_play_button_note": "Fügt „Verlauf abspielen“ zwischen „Einen Schritt zurück“ und „Einen Schritt vor“ in die Verlaufssteuerelemente ein. Mit einem Rechtsklick auf die Steuerelemente lässt sich die Schaltfläche auch ein- oder ausblenden.",
"prefs.history_sketches": "Skizzen",
"prefs.history_sketches": "Skizzen im Verlauf",
"prefs.history_sketches_in_order": "In Erstellungsreihenfolge",
"prefs.history_sketches_note": "Wenn „Skizzen“ auf „Unter dem zugehörigen Feature“ eingestellt ist, wird jede Skizze im linken Verlauf eine Ebene unter dem ersten daraus erstellten Feature angezeigt, sofern der Bereich breit genug für die Namen ist. Die Timeline unten zeigt weiterhin die Reihenfolge, in der die Elemente erstellt wurden.",
"prefs.history_sketches_under_feature": "Unter dem zugehörigen Feature",
@@ -2306,6 +2413,22 @@
"prefs.language_system": "System",
"prefs.layout": "Layout",
"prefs.layout_note": "Im Layout „Kompakt“ werden Bereiche an den Fensterrändern angedockt. Transparenz für den Browser ist nur im Layout „Schwebend“ verfügbar. Die Position des Assistenten ändern Sie auf der Seite „Assistent“.",
"prefs.layout_page.active_commands": "Aktive Befehle hervorheben",
"prefs.layout_page.always": "Immer",
"prefs.layout_page.card": "Karte",
"prefs.layout_page.column": "Spalte",
"prefs.layout_page.hover": "Bei Mauszeiger",
"prefs.layout_page.icons_labels": "Symbole + Beschriftungen",
"prefs.layout_page.ok_cancel": "OK und Abbrechen",
"prefs.layout_page.on_hover": "Bei Mauszeiger",
"prefs.layout_page.open": "Öffnen",
"prefs.layout_page.origin_bodies": "Ursprung und Körper oben",
"prefs.layout_page.panels": "Bereiche",
"prefs.layout_page.pinned_menus": "Angeheftete Befehle aus Menüs ausblenden",
"prefs.layout_page.play_history": "Verlaufswiedergabe",
"prefs.layout_page.slot_count": "{count} von {max} Plätzen",
"prefs.layout_page.view_corner": "Auch in der Ansichtsecke",
"prefs.layout_page.window": "Fenster",
"prefs.left": "Links",
"prefs.left_click": "Linksklick",
"prefs.line_drag_draws_one": "Drücken und Ziehen zeichnet eine Linie",
@@ -2334,6 +2457,13 @@
"prefs.mouse_heading": "Maus",
"prefs.mouse_mapping": "Mausbelegung",
"prefs.navigation": "Navigation",
"prefs.navigation.axes_signed": "Achsen",
"prefs.navigation.cube_compact": "Würfel",
"prefs.navigation.device": "Gerät",
"prefs.navigation.free": "Frei",
"prefs.navigation.orbit_style": "Drehstil",
"prefs.navigation.radial_help": "Die rechte Taste halten oder kurz bewegen, um einen Werkzeugring zu öffnen.",
"prefs.navigation.turntable": "Drehteller",
"prefs.navigation_note": "Legt fest, wie Maus, Trackpad und 3D-Maus die Ansicht steuern.",
"prefs.navigator_fit": "Modell nach einem Ansichtswechsel am Navigator einpassen",
"prefs.navigator_fit_note": "Eine Fläche, Ecke oder ein Pfeil des Würfels oder eine Achse des Gizmos blickt auch auf die Mitte des Modells, aus dem einen Abstand, der es von jeder Seite einpasst (seine Hüllkugel). Home nutzt denselben Zoom und dieselbe Mitte, solange dies an ist, sodass danach gewählte Ansichten sich nur drehen. Aus: Die Ansicht dreht sich an Ort und Stelle und behält Ziel und Zoom.",
@@ -2350,11 +2480,15 @@
"prefs.origin_plane_names_note": "Namen in Browser, Dialogen und Listen. {top} ist XY, {front} ist XZ und {right} ist YZ, mit Z nach oben. Befehle, Skripte und gespeicherte Konstruktionen behalten XY, XZ und YZ.",
"prefs.overall_speed": "Gesamtgeschwindigkeit",
"prefs.parameter_list_rows": "Zeilen der Parameterliste",
"prefs.parameter_list_rows_tip": "Der Pfeil nach unten in einem leeren Wertefeld listet die Parameter auf, Favoriten zuerst, bis zu dieser Anzahl. Weniger hält die Liste in einem großen Design übersichtlich.",
"prefs.parameter_list_rows_tip": "Maximale Anzahl der Parameter, die Pfeil abwärts in einem leeren Ausdrucksfeld auflistet.",
"prefs.persistent_ribbon_pressed": "Aktive Menübandbefehle hervorheben",
"prefs.persistent_ribbon_pressed_help": "Hebt Befehle im Menüband hervor, solange ihre Effekte eingeschaltet sind. Deaktivieren Sie diese Option, um die bisherige neutrale Darstellung und Befehlshilfe beizubehalten.",
"prefs.perspective_fov": "Sichtfeld",
"prefs.placement": "Platzierung",
"prefs.plugins_enabled": "Aktiviert",
"prefs.plugins_installed": "Installiert",
"prefs.plugins_intro": "Isolierte Erweiterungen.",
"prefs.plugins_show_folder": "Plugin-Ordner anzeigen",
"prefs.prefer_free_sketch_points": "Bei überlappenden Punkten bewegliche Skizzenpunkte bevorzugen",
"prefs.preview_choices": "Vorschau Ihrer Einstellungen",
"prefs.preview_tint_new_faces": "Neue Fläche in der Vorschau einfärben",
@@ -2409,8 +2543,43 @@
"prefs.shift_add_ctrl_toggle": "Umschalt fügt nur hinzu; Strg / Cmd schaltet die Auswahl um",
"prefs.shift_add_note": "Aktiviert: Umschalt fügt Elemente zur Auswahl hinzu, Strg / Cmd schaltet ihre Auswahl um. Deaktiviert (Standard): Beide Tasten schalten die Auswahl um.",
"prefs.shortcuts": "Tastenkürzel",
"prefs.shortcuts_import": "Importieren",
"prefs.shortcuts_intro": "Tasten für jeden Befehl.",
"prefs.shortcuts_not_assigned": "Nicht zugewiesen",
"prefs.shortcuts_press": "Taste drücken...",
"prefs.single_surface_analysis": "Nur eine Flächenanalyse verwenden",
"prefs.single_surface_analysis_help": "Wenn deaktiviert, können Flächenschattierung und Stetigkeitsanalyse gleichzeitig aktiv bleiben. Beim Aktivieren bleibt die zuletzt gestartete oder geänderte Analyse erhalten.",
"prefs.sketch.align_points": "An anderen Skizzenpunkten ausrichten",
"prefs.sketch.align_points_note": "Beim Zeichnen richten sich Linien gleichzeitig an Punkten auf beiden Achsen aus.",
"prefs.sketch.auto_dimensions": "Neue Formen vollständig bemaßen",
"prefs.sketch.auto_dimensions_note": "Eingegebene Werte und alle Maße, die die Form festlegen.",
"prefs.sketch.auto_locate": "Vom Skizzenursprung aus positionieren",
"prefs.sketch.auto_locate_note": "Mit vollständiger Bemaßung.",
"prefs.sketch.center_arc": "Mittelpunktbogen folgt dem Zeiger",
"prefs.sketch.click_selects": "Klick wählt",
"prefs.sketch.conflict_colour": "Konfliktfarbe",
"prefs.sketch.drawing": "Zeichnen",
"prefs.sketch.exit_keep": "Beibehalten",
"prefs.sketch.exit_previous": "Zur Ansicht davor zurückkehren",
"prefs.sketch.exit_unless_rotated": "Außer bei Drehung",
"prefs.sketch.faces_edges": "Flächen und Kanten",
"prefs.sketch.hide_glyphs": "Symbole beim Herauszoomen ausblenden",
"prefs.sketch.marks": "Markierungen in der Skizze",
"prefs.sketch.note": "Zeichnen, Fangen und Auswählen.",
"prefs.sketch.opening_closing": "Öffnen und Schließen",
"prefs.sketch.prefer_free_points": "Bei Überlappung bewegliche Punkte bevorzugen",
"prefs.sketch.project_options": "Zuerst Projektionsoptionen abfragen",
"prefs.sketch.recent_snap": "Am Schnittpunkt zweier Hilfslinien fangen",
"prefs.sketch.select_other_hidden": "Andere auswählen erfasst verdeckte Geometrie",
"prefs.sketch.shortcut_cycles_modes": "Erneuter Kurzbefehl wechselt den Modus",
"prefs.sketch.shortcut_cycles_modes_tip": "Aus: Das Werkzeug startet erneut.",
"prefs.sketch.snapping_guides": "Fangen und Hilfslinien",
"prefs.sketch.state_icons": "Statussymbole",
"prefs.sketch.tracking_guides": "Hilfslinien für Mittelpunkte und Kantenmitten",
"prefs.sketch.tracking_guides_note": "Temporäre waagerechte und senkrechte Hilfslinien von überfahrenen Mittelpunkten und Kantenmitten.",
"prefs.sketch.tracking_points": "Hilfslinien auch von Skizzenpunkten",
"prefs.sketch.unless_turned_note": "zurück, wenn nicht gedreht",
"prefs.sketch.whole_bodies": "Ganze Körper",
"prefs.sketch_align_points": "Beim Zeichnen an anderen Skizzenpunkten ausrichten",
"prefs.sketch_align_points_note": "Beim Zeichnen richtet sich ein Punkt zugleich waagerecht an einem Skizzenpunkt und senkrecht an einem anderen aus, mit einer gepunkteten Hilfslinie; der Klick behält beide Ausrichtungen. Aus: nur am vorherigen Punkt, jeweils eine Achse.",
"prefs.sketch_conflict_colour": "Widersprüchliche Geometrie in der Konfliktfarbe",
@@ -2443,11 +2612,11 @@
"prefs.source": "Quelle",
"prefs.speed": "Geschwindigkeit",
"prefs.stand_up_note": "Wenn eine STEP- oder IGES-Datei mit der Z-Achse nach oben geöffnet wird, wird eine Vierteldrehung um die X-Achse angeboten.",
"prefs.stand_up_unknown": "Für Dateien aus unbekannten Programmen",
"prefs.stand_up_unknown": "Dateien aus unbekannten Programmen",
"prefs.stand_up_unknown.ask": "In einem Dialog fragen",
"prefs.stand_up_unknown.note": "Einen Hinweis zeigen",
"prefs.stand_up_unknown.off": "Nicht anbieten",
"prefs.stand_up_unknown_note": "Wie das Aufrichten angeboten wird, wenn der Dateikopf kein Programm nennt, das bekanntlich mit Y nach oben speichert. Ein Hinweis erscheint nur, wenn die Datei auf dem Rücken zu liegen scheint; „In einem Dialog fragen“ fragt wie bisher bei jeder solchen Datei.",
"prefs.stand_up_unknown_note": "Wie der Vorschlag erscheint, wenn die Datei kein bekanntes Programm nennt.",
"prefs.status_bar": "Statusleiste",
"prefs.status_bar_note": "Eine schmale Leiste am unteren Fensterrand zeigt, was gerade bearbeitet wird, den Zustand der offenen Skizze in seiner Farbe und die Einheiten der Konstruktion; ein Klick auf die Einheiten ändert sie. Der Zustand der Skizze steht dann nicht mehr in der Ecke der Ansicht.",
"prefs.status_done": "Fertig",
@@ -2483,6 +2652,10 @@
"prefs.theme_follow_off": "Aus",
"prefs.theme_follow_stop": "Nicht mehr folgen",
"prefs.theme_import_hint": "Klicken, um eine Designdatei zu wählen, eine hierher ziehen oder mit kopiertem Design Strg+V (Cmd+V auf dem Mac) drücken",
"prefs.theme_notice_dismiss": "Ausblenden",
"prefs.theme_option_blur": "Unschärfe hinter Panels",
"prefs.theme_option_blur_help": "Unschärfe hinter durchscheinenden Panels in physischen Pixeln. Null bedeutet aus.",
"prefs.theme_option_invalid": "Der gespeicherte Wert für {option} ist ungültig; der Designstandard wird verwendet. Der gespeicherte Wert bleibt erhalten.",
"prefs.theme_option_opacity": "Deckkraft",
"prefs.theme_option_opacity_help": "Deckkraft schwebender Bedienfelder anpassen. Eine Mindestdeckkraft schützt die Lesbarkeit über den Ansichtsfarben.",
"prefs.theme_option_tab_colours": "Tabfarben",
@@ -2491,6 +2664,7 @@
"prefs.theme_option_tab_fade_ms_help": "Panel- und Ansichtsfarben beim Tabwechsel überblenden. Null wechselt sofort.",
"prefs.theme_options_badge": "Optionen",
"prefs.theme_options_more": "Mehr · {count}",
"prefs.theme_options_newer": "Dieses Design benötigt eine neuere Version von SolidCraft. Nach dem Upgrade das Originaldesign erneut importieren, um seine Optionsdefinitionen wiederherzustellen.",
"prefs.theme_options_reset": "Zurücksetzen",
"prefs.theme_options_unavailable": "Auf diesem Gerät nicht verfügbar",
"prefs.theme_paste": "Design einfügen",
@@ -2520,8 +2694,8 @@
"prefs.validation.label": "Vollständige Kernel-Prüfung",
"prefs.validation.off": "Aus (experimentell)",
"prefs.validation_note": "Nach jedem Schritt: prüft jeden Sweep, jede Boolesche Operation und jede Rundung.\nEinmal je Feature: prüft das Ergebnis jedes Features einmal.\nAus (experimentell): keine Prüfung.",
"prefs.value_boxes_in_view": "Werte in der Ansicht neben ihren Griffen zeigen",
"prefs.value_boxes_in_view_tip": "Der Wert eines Befehls (Abstand, Radius …) in einem Feld neben seinem Griff, über dem Modell. Aus: Sie geben ihn im Befehlsfenster ein, das nach einer Auswahl die Tastatur erhält, und nichts verdeckt das Modell.",
"prefs.value_boxes_in_view": "Werte neben ihren Griffen anzeigen",
"prefs.value_boxes_in_view_tip": "Abstand beim Extrudieren, Radius beim Verrunden … als Feld in der Ansicht.",
"prefs.view": "Ansichtsverhalten",
"prefs.view_confirm_corner": "OK und Abbrechen in der Ansichtsecke",
"prefs.view_confirm_corner_note": "Solange ein Dialog oder eine Skizze geöffnet ist, übernehmen ein Häkchen und ein Kreuz oben rechts in der Ansicht dieselben Funktionen wie „OK“ und „Abbrechen“ des Dialogs. Eine Skizze wird damit beendet oder verworfen. Der Ansichtsnavigator rückt darunter.",
@@ -3922,11 +4096,11 @@
"select two components": "zwei Komponenten auswählen",
"set by the sketch's lines": "durch die Skizzenlinien festgelegt",
"settings.display_quality_note": "Feinere Stufen zeigen gekrümmte Flächen und Kanten glatter und brauchen länger zum Vernetzen. Nur die Ansicht ändert sich, nie das Design oder Exporte.",
"settings.double_click_components": "Doppelklick auf eine Komponente aktiviert sie oder bearbeitet eine verknüpfte an Ort und Stelle",
"settings.double_click_components": "Komponente zum Aktivieren doppelklicken",
"settings.frame_rate_details_note": "Fügt ein Diagramm der Bildzeiten der letzten Bilder hinzu, darüber die gezeichneten Dreiecke, dazu ein Histogramm der Bildzeiten und was die GPU gezeichnet hat. Ein Klick auf die Bildrate in der Ansicht blendet sie ein oder aus.",
"settings.frame_rate_note": "Zeigt die Bilder pro Sekunde unten rechts in der Ansicht.",
"settings.insert_lists_open_designs": "Konstruktion einfügen zeigt zuerst die in anderen Tabs geöffneten Konstruktionen",
"settings.measure_axis_distances": "X/Y/Z-Maße anzeigen",
"settings.insert_lists_open_designs": "Konstruktion einfügen listet offene Konstruktionen zuerst",
"settings.measure_axis_distances": "Achsabstände beim Messen",
"settings.measure_axis_distances_help": "Bei geöffneter Messung die Abstände entlang der Weltachsen zusätzlich zum Gesamtabstand anzeigen.",
"settings.msaa_applies_on_restart": "Die Kantenglättung wird beim nächsten Start von SolidCraft übernommen",
"settings.msaa_reset": "{setting}: {requested}× wird von diesem Grafikgerät nicht unterstützt; jetzt {reset}× (unterstützt: {supported}).",
@@ -3936,7 +4110,7 @@
"settings.reset_intro": "Diese Einstellungen funktionieren auf diesem Computer nicht und würden den Start von SolidCraft verhindern. Sie wurden daher geändert:",
"settings.reset_title": "Einstellungen wurden geändert",
"settings.supersampling_note": "Zeichnet die Ansicht in höherer Auflösung und rechnet sie herunter: glattere Kanten, Linien und Raster auf Bildschirmen unter 4K. Kostet GPU-Zeit: etwa 2,25-mal so viele Pixel bei 1,5× und 4-mal bei 2×.",
"settings.update_changed_links": "Geänderte verknüpfte Designs aktualisieren, wenn ein Tab wieder angezeigt wird",
"settings.update_changed_links": "Geänderte Verknüpfungen bei Rückkehr zum Tab aktualisieren",
"settings.window_system_details": "Details",
"settings.window_system_note_now": "Wird nach einem Neustart wirksam. Aktuell: {now}.",
"settings.window_system_wayland_limited": "Einige Funktionen sind unter nativem Wayland eingeschränkt.",
@@ -4337,7 +4511,7 @@
"ui.agent_chat_send_now_tip": "Den aktuellen Schritt des Agenten stoppen und dies als Nächstes senden (Strg+Eingabe)",
"ui.agent_chat_setup_body": "SolidCraft kommuniziert mit einem auf diesem Computer installierten Programmieragenten. Wählen Sie einen unter Einstellungen › KI aus.",
"ui.agent_chat_setup_title": "Assistenten einrichten",
"ui.agent_chat_side": "Seite des Agenten-Chats",
"ui.agent_chat_side": "Chat andocken",
"ui.agent_chat_signed_in": "angemeldet",
"ui.agent_chat_state_auth": "Anmeldung nötig",
"ui.agent_chat_state_busy": "Arbeitet…",
@@ -4356,7 +4530,7 @@
"ui.agent_chat_undone": "(rückgängig gemacht)",
"ui.agent_chat_view_attached": "Ansicht angehängt",
"ui.agent_chat_waiting": "Wartet auf Sie",
"ui.agent_cursor": "Agent-Cursor",
"ui.agent_cursor": "Agentenzeiger",
"ui.agent_cursor_speed": "Agent-Cursor-Geschwindigkeit",
"ui.agent_off_thread": "Fenster bedienbar halten, während Agenten Befehle ausführen",
"ui.agent_off_thread_note": "Langsame Befehle eines Agenten (aus dem Assistentenbereich oder von einem MCP-Client) werden neben dem Fenster berechnet: Sie können die Ansicht weiter drehen und prüfen und einen Befehl über den Chip in der Ansicht stoppen. Aus: Jeder läuft im Thread des Fensters, und das Fenster wartet darauf.",
@@ -4402,14 +4576,14 @@
"ui.ai_key_note": "Im Schlüsselbund des Systems gespeichert, nie in den Einstellungen von SolidCraft. Für einen lokalen Server leer lassen.",
"ui.ai_keychain_error": "Der Schlüsselbund des Systems hat abgelehnt: {error}",
"ui.ai_kind": "Art",
"ui.ai_mode_all": "Alles erlauben",
"ui.ai_mode_all": "Alles ausführen",
"ui.ai_mode_all_note": "Nichts fragt vorher. Absturzberichte bleiben tabu, und die Ordnergrenzen gelten weiter.",
"ui.ai_mode_all_warning": "Der Agent kann Ihr Design ohne Nachfrage speichern, überschreiben, exportieren und ersetzen. Nutzen Sie das nur mit einem Agenten, dem Sie vertrauen, und mit gespeicherten Designs.",
"ui.ai_mode_ask": "Jedes Mal fragen",
"ui.ai_mode_ask": "Zuerst fragen",
"ui.ai_mode_ask_note": "Jede Änderung am Design und jede Dateiaktion wartet auf Ihr Erlauben.",
"ui.ai_mode_edits": "Modelländerungen automatisch erlauben",
"ui.ai_mode_edits": "Änderungen vornehmen",
"ui.ai_mode_edits_note": "Modellierbefehle laufen ohne Nachfrage; Speichern, Öffnen, Exportieren und andere Datei- oder destruktive Aktionen fragen weiterhin.",
"ui.ai_mode_global": "Wie oben eingestellt",
"ui.ai_mode_global": "Wie oben",
"ui.ai_model": "Modell",
"ui.ai_model_hint": "qwen3:14b",
"ui.ai_modes_scope": "Diese Einstellungen gelten für die Werkzeuge von SolidCraft. Die eigenen Werkzeuge eines Agenten (seine Shell, seine Dateiänderungen) folgen seinem eigenen Modus, den Sie im Kopf des Chat-Panels wählen, wenn der Agent Modi hat.",
@@ -4435,7 +4609,7 @@
"ui.ai_save": "Speichern",
"ui.ai_settings": "KI-Einstellungen…",
"ui.ai_show_features": "KI-Funktionen anzeigen",
"ui.ai_show_features_note": "Der Agenten-Chat: ein Panel, in dem ein Coding-Agent, den Sie schon nutzen, oder ein Modell-Endpunkt Ihrer Wahl am geöffneten Design arbeitet. Aus: keine KI-Schaltfläche, kein Kürzel, kein Menüeintrag, kein Panel, und nichts wird gesucht oder gestartet.",
"ui.ai_show_features_note": "Aus blendet den Assistenten überall aus und startet nichts. Alles darunter wird gedimmt.\n\nDer Agenten-Chat: ein Panel, in dem ein Coding-Agent, den Sie schon nutzen, oder ein Modell-Endpunkt Ihrer Wahl am geöffneten Design arbeitet. Aus: keine KI-Schaltfläche, kein Kürzel, kein Menüeintrag, kein Panel, und nichts wird gesucht oder gestartet.",
"ui.ai_switch_tip": "Den Agenten für dieses Gespräch wechseln",
"ui.ai_test": "Testen",
"ui.ai_test_acp_ok": "Antwortet: {agent}",
@@ -4469,7 +4643,7 @@
"ui.ai_tool_sum_then": "{first}, dann {next}",
"ui.ai_tool_unfold": "Klicken, um das Ergebnis zu zeigen",
"ui.ai_tool_waiting": "wartet",
"ui.ai_toolbar_button": "Schaltfläche in der Werkzeugleiste anzeigen",
"ui.ai_toolbar_button": "Assistent-Schaltfläche in der Anwendungsleiste",
"ui.ai_toolbar_button_note": "Die KI-Schaltfläche am rechten Ende der Werkzeugleiste. Strg+Umschalt+A und das Ansicht-Menü öffnen das Panel weiterhin.",
"ui.ai_undo_tip": "Alles, was der Agent für diese Nachricht geändert hat, in einem Schritt rückgängig machen",
"ui.ai_url_hint": "https://example.com/v1",
@@ -4614,6 +4788,7 @@
"ui.default_modelling_orientation": "Standard-Modellierungsorientierung",
"ui.delete": "Löschen",
"ui.delete_select_first": "Wählen Sie zuerst aus, was gelöscht werden soll: Features, Körper, Komponenten oder Skizzengeometrie",
"ui.dependents_select_first": "Wählen Sie zuerst aus, wessen Abhängige aufgelistet werden sollen",
"ui.depth": "Tiefe",
"ui.design": "Design",
"ui.design_properties.author": "Autor",
@@ -4737,7 +4912,7 @@
"ui.extract_done": "Die Komponente ist jetzt ein eigenes Design und hier verknüpft.",
"ui.extrude": "Extrudieren",
"ui.extrude_auto_to_object": "Bis zu einem gewählten Ziel extrudieren",
"ui.extrude_auto_to_object_tip": "Wenn in „Extrudieren“ ein Profil gewählt ist, schaltet das Wählen einer Fläche, eines Eckpunkts oder einer parallelen Ebene außerhalb der Profilebene die Ausdehnung auf „Bis Objekt“ um",
"ui.extrude_auto_to_object_tip": "Eine Fläche, ein Scheitelpunkt oder eine parallele Ebene stellt die Ausdehnung auf Bis Objekt um.",
"ui.factor": "Faktor",
"ui.fade": "Auslauf",
"ui.fade_length": "Auslauflänge",
@@ -4769,6 +4944,10 @@
"ui.fillet_setback_least": "Mehr als {least}",
"ui.fillet_setback_tip": "Wo das glatte Eckflächenstück beginnt: wie weit von der Ecke entlang jeder Kante. Standard ist {factor} × der Radius. Er muss größer sein als der kleinste Rücksprung, wo sich die Rundungen ohne ihn treffen: der Radius, wo die Flächen rechtwinklig zueinander stehen.",
"ui.fillet_smooth_radius": "Radius glatt ändern",
"ui.fillet_turn": "Wendungen",
"ui.fillet_turn_mitre": "Gehrung",
"ui.fillet_turn_swept": "Geschwenkt",
"ui.fillet_turn_tip": "Wo zwei Rundungen um eine scharfe Eckkante zwischen ihnen herumlaufen, etwa die Rundungen am Fuß eines Zapfens an seinen senkrechten Kanten.\nGehrung: Die Rundungen laufen weiter, bis sie sich in einer Gehrung treffen.\nGeschwenkt: Die Rundungen enden an der Eckkante und schwenken um sie herum; die Ecke ist ein Torus, tangential zu beiden. Nur für Rundungen mit einem Radius um eine gerade, konvexe Eckkante.",
"ui.fillet_type_choices_tip": "Konstanter Radius: ein Radius entlang der ganzen Kante.\nSehnenlänge: die Breite der Rundung bleibt gleich, in welchem Winkel die Flächen auch aufeinandertreffen.\nVariabler Radius: ein Radius an jedem Ende und weitere entlang der Kante, wenn Sie sie hinzufügen.\nKrümmungsstetig: ein Querschnitt ohne Knick in den Spiegelungen, wo er auf ebene Flächen trifft (G2).\nSpline: der Radius entlang der Kante folgt Steuerradien, die Sie festlegen.",
"ui.fillet_type_curvature": "Krümmungsstetig",
"ui.fillet_type_tip": "Der Querschnitt über die Kante. Krümmungsstetig: ein krümmungsstetiger Querschnitt (G2) zwischen ebenen Flächen, sodass Spiegelungen ohne Knick von den Flächen in die Rundung laufen.",
@@ -4778,6 +4957,7 @@
"ui.fine": "Fein",
"ui.finest": "Sehr fein",
"ui.finish_sketch": "Skizze beenden",
"ui.first_angle": "Erster Winkel",
"ui.fit": "Einpassen",
"ui.fix_unfix": "Fixieren/Lösen",
"ui.flat": "Flach",
@@ -4880,7 +5060,7 @@
"ui.hideOrigin": "Ursprung ausblenden",
"ui.hidePlanes": "Ebenen ausblenden",
"ui.hideProfile": "Profil ausblenden",
"ui.hide_origin_by_default": "Ursprung standardmäßig ausblenden",
"ui.hide_origin_by_default": "Ursprungsordner in neuen Konstruktionen ausblenden",
"ui.hide_origin_note": "Neue Designs beginnen mit ausgeblendetem Ordner „Ursprung“. Ein Design behält seine eigene Ursprungseinstellung, sobald Sie ihn ein- oder ausblenden.",
"ui.history_back": "Einen Schritt zurück",
"ui.history_below_browser": "Unter dem Browser",
@@ -5037,7 +5217,7 @@
"ui.pitch": "Steigung",
"ui.placement": "Platzierung",
"ui.placement_handles": "Platzierungsgriffe",
"ui.placement_handles_tip": "Griffe an der Vorschau einer Auflage, eines Ansatzes oder einer Rastverbindung: Ziehen Sie die Markierung, um sie über das Modell zu bewegen, und drehen Sie sie am Ring",
"ui.placement_handles_tip": "Eine Markierung zum Verschieben des Features über das Modell und ein Ring zum Drehen.",
"ui.plane": "Ebene",
"ui.plane_at_angle": "Ebene im Winkel",
"ui.plugins": "Plugins",
@@ -5078,6 +5258,7 @@
"ui.profiles": "Profile",
"ui.project": "Projizieren",
"ui.project_face_edges_sketch": "Flächenkanten in neue Skizzen projizieren",
"ui.projection_of_new_drawings": "Projektion neuer Zeichnungen",
"ui.properties": "Eigenschaften",
"ui.properties.copy_matrix": "Matrix kopieren",
"ui.properties.first_moments": "Statische Momente",
@@ -5232,8 +5413,8 @@
"ui.start": "Start",
"ui.start_over": "Von vorn beginnen",
"ui.start_page": "Startseite",
"ui.step_values": "Werte mit Pfeiltasten und Mausrad schrittweise ändern",
"ui.step_values_tip": "In einem Wertefeld, in das Sie gerade tippen, ändern Auf/Ab eine einfache Zahl um 1 (Umschalt: 10, Alt: 0,1), ebenso das Mausrad über dem Feld. Ausdrücke bleiben wie eingegeben.",
"ui.step_values": "Werte mit Pfeilen und Mausrad ändern",
"ui.step_values_tip": "Pfeil auf/ab und das Mausrad über einem Wertefeld ändern dessen Zahl.",
"ui.stitch_tolerance_hint": "eng",
"ui.stretch": "Strecken",
"ui.supersampling": "Supersampling (SSAA)",
@@ -5260,6 +5441,7 @@
"ui.text": "Text",
"ui.theme": "Design",
"ui.thickness": "Dicke",
"ui.third_angle": "Dritter Winkel",
"ui.thread": "Gewinde",
"ui.tie_ends": "Verknüpfen",
"ui.tie_ends_tip": "Alle unverknüpften Enden mit Abhängigkeiten versehen",
+9 -1
View File
@@ -16,7 +16,7 @@
"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",
"Snap to face and edge centres": "Snap to centres",
"Specialise": "Specialise",
"cmd.appearance_favorite": "Favourite Look",
"cmd.component_cycle_color": "Cycle Component Colour",
@@ -69,6 +69,9 @@
"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",
"measure.line_to_centre": "Line to centre",
"navbar.custom.anchor.bottom_centre": "Bottom centre",
"navbar.custom.anchor.top_centre": "Top centre",
"navbar.custom.customise": "Customise navigation bar…",
"navbar.custom.position_centre": "Centre",
"navbar.description.separator": "A thin line that groups neighbouring slots.",
@@ -76,6 +79,7 @@
"prefs.appearance.colour_vision_help": "Changes the colours that carry a meaning: sketch states, axes, bends, analyses and icons.",
"prefs.appearance.viewport_colours": "3D view colours",
"prefs.bottom_centre": "Bottom centre",
"prefs.design_tools.hole_snaps_help": "The middle of a face and the centres of its round edges.",
"prefs.logo_theme": "Theme's colours",
"prefs.max_file_size": "Largest file to open",
"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.",
@@ -83,6 +87,10 @@
"prefs.max_file_size_web": "The web app opens STEP and import files up to 512 MB and designs up to 256 MB: a browser tab can't hold larger files. The desktop app can raise these limits.",
"prefs.megabytes": "MB",
"prefs.network_locations_note": "For organisations that keep designs on file servers. Off: only servers you allow.",
"prefs.sketch.center_arc": "Centre-point arc follows the cursor",
"prefs.sketch.conflict_colour": "Conflict colour",
"prefs.sketch.tracking_guides": "Centre and midpoint guides",
"prefs.sketch.tracking_guides_note": "Temporary horizontal and vertical guides from hovered centres and edge midpoints.",
"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",
+269 -56
View File
@@ -379,7 +379,7 @@
"Move Type": "Move Type",
"Move down": "Move down",
"Move earlier": "Move earlier",
"Move holes by clicking or dragging": "Move holes by clicking or dragging",
"Move holes by clicking or dragging": "Move by click or drag",
"Move later": "Move later",
"Move up": "Move up",
"Name": "Name",
@@ -585,7 +585,7 @@
"Show ghost": "Show ghost",
"Show in folder": "Show in folder",
"Show the pointer an AI agent moves (#35)": "Show the pointer an AI agent moves (#35)",
"Show tool at cursor": "Show tool at cursor",
"Show tool at cursor": "Tool at the cursor",
"Signed axes": "Signed axes",
"Size": "Size",
"Sketch constraints": "Sketch constraints",
@@ -596,7 +596,7 @@
"Sketch points": "Sketch points",
"Slider": "Slider",
"Slot width": "Slot width",
"Snap to face and edge centres": "Snap to face and edge centers",
"Snap to face and edge centres": "Snap to centers",
"SolidCraft cannot submit reports directly. Submit on GitHub; no token is needed in the app.": "SolidCraft cannot submit reports directly. Submit on GitHub; no token is needed in the app.",
"SolidCraft closed before these designs were saved. Their last autosave can be opened.": "SolidCraft closed before these designs were saved. Their last autosave can be opened.",
"Sort A to Z": "Sort A to Z",
@@ -739,36 +739,36 @@
"a picked edge's body is gone": "a picked edge's body is gone",
"add a key for a joint first": "add a key for a joint first",
"ai.connected_toast": "SolidCraft is now available in {clients}. Restart them to use it (Preferences > Assistant).",
"ai.prefs.allow_control": "Let AI agents connect (control channel / MCP)",
"ai.prefs.allow_control_note": "Opens a control channel on this computer only (127.0.0.1, port 27878 or the next free one). A program that connects must also show a secret token that only your user account can read, made fresh at every start. Takes effect the next time SolidCraft starts. Off: nothing listens unless you start SolidCraft with --control.",
"ai.prefs.availability": "Assistant controls",
"ai.prefs.background_commands": "Run agent commands in the background",
"ai.prefs.connect_apps": "Make SolidCraft available to AI apps on this computer",
"ai.prefs.connect_apps_note": "When SolidCraft starts, adds it as an MCP server to the AI apps found on this computer (OpenCode, Claude Desktop, Claude Code, Cursor, Codex, Gemini CLI, Copilot CLI, Windsurf, Zed, VS Code), so an agent can use it without any setup. Only the solidcraft entry is added or its path refreshed; an entry you changed is left alone, and the file is backed up first. Off: the entries it added are removed. Help > Connect AI assistants… does this by hand.",
"ai.prefs.connect_at_start": "Connect the last-used agent when SolidCraft starts",
"ai.prefs.allow_control": "Let AI agents connect",
"ai.prefs.allow_control_note": "A local channel (MCP). Nothing leaves this computer.\n\nOpens a control channel on this computer only (127.0.0.1, port 27878 or the next free one). A program that connects must also show a secret token that only your user account can read, made fresh at every start. Takes effect the next time SolidCraft starts. Off: nothing listens unless you start SolidCraft with --control.",
"ai.prefs.availability": "Availability",
"ai.prefs.background_commands": "Run slow commands in the background",
"ai.prefs.connect_apps": "Add SolidCraft to AI apps",
"ai.prefs.connect_apps_note": "Off takes the entries out again.\n\nWhen SolidCraft starts, adds it as an MCP server to the AI apps found on this computer (OpenCode, Claude Desktop, Claude Code, Cursor, Codex, Gemini CLI, Copilot CLI, Windsurf, Zed, VS Code), so an agent can use it without any setup. Only the solidcraft entry is added or its path refreshed; an entry you changed is left alone, and the file is backed up first. Off: the entries it added are removed. Help > Connect AI assistants… does this by hand.",
"ai.prefs.connect_at_start": "Connect the last agent at start",
"ai.prefs.connect_at_start_note": "Starts the agent you connected last (or the default one) in the background each time SolidCraft starts, so it is ready for chatting. Not when SolidCraft is started with --control. If it cannot connect, the Assistant shows Connection failed and a Retry button.",
"ai.prefs.follow_camera": "Turn the camera to sketches",
"ai.prefs.follow_camera": "Follow the camera",
"ai.prefs.follow_camera_off": "Automatic camera following is off.",
"ai.prefs.follow_camera_on": "The camera turns to face sketches the agent creates.",
"ai.prefs.intro": "Agents, permissions and how their work appears.",
"ai.prefs.make_default": "Use by default",
"ai.prefs.intro": "AI agents in SolidCraft.",
"ai.prefs.make_default": "Make default",
"ai.prefs.no_agents": "No agents yet. Add an agent or refresh to find installed agents.",
"ai.prefs.no_saved_approvals": "No saved approvals.",
"ai.prefs.open_at_start": "Open the Assistant panel when SolidCraft starts",
"ai.prefs.open_at_start": "Open the chat at start",
"ai.prefs.open_at_start_note": "Shows the Assistant panel (or its own window) each time SolidCraft starts. Not when SolidCraft is started with --control.",
"ai.prefs.override_all_warning": "This agent may save, overwrite, export or replace the design without asking.",
"ai.prefs.permission_all": "Allow everything",
"ai.prefs.permission_all_summary": "Available SolidCraft tools run without asking.",
"ai.prefs.permission_ask": "Ask before changes",
"ai.prefs.permission_ask_summary": "Approve every design change and file action.",
"ai.prefs.permission_edits": "Allow model edits",
"ai.prefs.permission_edits_summary": "Model edits run; file and destructive actions still ask.",
"ai.prefs.permission_all": "Do everything",
"ai.prefs.permission_all_summary": "Never asks",
"ai.prefs.permission_ask": "Ask first",
"ai.prefs.permission_ask_summary": "Asks for every change",
"ai.prefs.permission_edits": "Make edits",
"ai.prefs.permission_edits_summary": "Edits freely, asks for the rest",
"ai.prefs.permission_scope": "For SolidCraft tools only. The agent’s shell and file edits use its own permissions.",
"ai.prefs.pointer_preview_hidden": "Pointer hidden",
"ai.prefs.pointer_preview_speed": "Agent pointer · {speed}",
"ai.prefs.pointer_speed": "Motion speed",
"ai.prefs.pointer_speed": "Speed",
"ai.prefs.privacy_note": "Only your chosen agent or endpoint receives the conversation. Endpoint keys are stored in your system keychain.",
"ai.prefs.show_pointer": "Show agent pointer",
"ai.prefs.show_pointer": "Show the agent's pointer",
"ai.prefs.turn_limit_hint": "Maximum design-changing tool calls the Assistant may make for one message.",
"angle, height": "angle, height",
"appearance.dielectric_ior": "Dielectric IOR",
@@ -833,6 +833,8 @@
"cmd.appearance_pick_up": "Pick Up Appearance",
"cmd.appearance_replace": "Replace Appearance",
"cmd.assembly_bom": "Bill of Materials",
"cmd.assembly_bom_item_set": "Change Item Number",
"cmd.assembly_bom_renumber": "Renumber Items",
"cmd.assembly_mass_properties": "Mass Properties",
"cmd.body.print_face": "Print Face",
"cmd.body_rename": "Rename Body",
@@ -932,6 +934,36 @@
"cmd.document_metadata_set": "Change Design Properties",
"cmd.document_revision": "Design Revision",
"cmd.document_units": "Change Design Units",
"cmd.drawing_balloon_add": "Add Balloon",
"cmd.drawing_balloon_edit": "Edit Balloon",
"cmd.drawing_balloon_inspect": "Inspect Balloon",
"cmd.drawing_close": "Close Drawing",
"cmd.drawing_create": "Create Drawing",
"cmd.drawing_delete": "Delete Drawing",
"cmd.drawing_dimension_add": "Add Drawing Dimension",
"cmd.drawing_dimension_edit": "Edit Drawing Dimension",
"cmd.drawing_dimension_inspect": "Inspect Drawing Dimension",
"cmd.drawing_export": "Export Sheet",
"cmd.drawing_inspect": "Inspect Drawing",
"cmd.drawing_list": "List Drawings",
"cmd.drawing_open": "Open Drawing",
"cmd.drawing_properties": "Drawing Properties",
"cmd.drawing_properties_remove": "Remove Drawing Property",
"cmd.drawing_properties_set": "Change Drawing Properties",
"cmd.drawing_revision_add": "Add Revision",
"cmd.drawing_revision_delete": "Delete Revision",
"cmd.drawing_revision_edit": "Edit Revision",
"cmd.drawing_scene": "Draw Sheet",
"cmd.drawing_set": "Drawing Settings",
"cmd.drawing_sheet_add": "Add Sheet",
"cmd.drawing_sheet_delete": "Delete Sheet",
"cmd.drawing_sheet_reorder": "Reorder Sheets",
"cmd.drawing_sheet_set": "Sheet Settings",
"cmd.drawing_view_copy_to_sheet": "Copy View to Sheet",
"cmd.drawing_view_edit": "Edit Drawing View",
"cmd.drawing_view_inspect": "Inspect Drawing View",
"cmd.drawing_view_move_to_sheet": "Move View to Sheet",
"cmd.drawing_view_snapshot": "Drawing View Snapshot",
"cmd.edit_checkpoint": "Checkpoint",
"cmd.edit_repeat": "Repeat Last Command",
"cmd.edit_restore_checkpoint": "Restore Checkpoint",
@@ -997,6 +1029,11 @@
"cmd.joint_screw": "Screw",
"cmd.joint_solve": "Solve Joints",
"cmd.joint_sweep": "Sweep Joint",
"cmd.layout.apply": "Apply Layout",
"cmd.layout.get": "Inspect Layout",
"cmd.layout.reset": "Reset Layout",
"cmd.layout.save": "Save Layout",
"cmd.layout.workspace.select": "Select Layout Workspace",
"cmd.library_apply": "Apply Local Library Entry",
"cmd.library_export": "Export Design Library",
"cmd.library_inspect": "Inspect Local Library",
@@ -1024,12 +1061,28 @@
"cmd.motion_trace_edit": "Edit Motion Trace",
"cmd.motion_trace_keep": "Keep Motion Trace",
"cmd.occurrence_align": "Align Occurrence",
"cmd.occurrence_bom_type": "Bill of Materials Type",
"cmd.occurrence_copy": "Paste (Instance)",
"cmd.occurrence_drag": "Drag Occurrence",
"cmd.occurrence_ground": "Ground",
"cmd.occurrence_move": "Move Occurrence",
"cmd.occurrence_properties": "Occurrence Physical Properties",
"cmd.occurrence_reparent": "Move to Component",
"cmd.panel.activate": "Activate Panel Tab",
"cmd.panel.close": "Close Panel",
"cmd.panel.collapse": "Collapse Panel Section",
"cmd.panel.dismiss": "Dismiss Panel Peek",
"cmd.panel.float": "Float Panel",
"cmd.panel.focus": "Focus Panel",
"cmd.panel.list": "List Panels",
"cmd.panel.move": "Move Panel",
"cmd.panel.native_close": "Redock Native Panel Section",
"cmd.panel.open": "Open Panel",
"cmd.panel.peek": "Peek Collapsed Panel",
"cmd.panel.popout": "Pop Out Panel",
"cmd.panel.reset_pair": "Reset Panel Sash",
"cmd.panel.resize": "Resize Panel Section",
"cmd.panel.resize_pair": "Resize Panel Sash",
"cmd.parameters_add": "Add User Parameter",
"cmd.parameters_change": "Change Parameters",
"cmd.parameters_comment": "Comment Parameter",
@@ -1376,6 +1429,9 @@
"dialog.custom_axis": "↖ Custom Axis",
"dialog.dimension_insert_hint": "Click another driving dimension to insert its name",
"dialog.dimension_name": "Dimension: {name}",
"dialog.dimension_step_down": "Down by {step}",
"dialog.dimension_step_expression": "Only a number steps; an expression stays as typed",
"dialog.dimension_step_up": "Up by {step}",
"dialog.edit_title": "EDIT {title}",
"dialog.no_shown_body": "No shown body to change: show one, or choose New Body",
"dialog.pattern_sketch_not_object": "Pick bodies or features: a sketch is neither. Repeat its curves with the sketch's own Pattern or Mirror, or extrude it first.",
@@ -1383,6 +1439,7 @@
"dialog.selected_count": "↖ {count} selected",
"dialog.selection_constraint": "Constraint {id}",
"dialog.selection_external_curve": "{sketch} · curve {id}",
"dialog.selection_external_point": "{point} · {sketch}",
"dialog.selection_guidance": "Select an input to change it. Click selected objects to remove them; Ctrl/Cmd-click also toggles them. Review the preview, then press OK.",
"dialog.selection_midpoint": "Midpoint · {id}",
"dialog.selection_profile": "{sketch} · profile {number}",
@@ -1400,6 +1457,7 @@
"document.read_only_save": "This design was opened read-only: save it as a new file",
"document.save_changes": "Save changes to {name}?",
"document.save_copy": "Save a Copy…",
"document.tab_unsaved_changes": "Unsaved changes",
"document.unsaved_changes": "{name} has unsaved changes. If you don't save, they are lost.",
"drive.rigid": "{joint} is a rigid joint: it holds its parts still, so it has nothing to drive. To let them move, change its type in Edit Joint: Revolute to rotate, Slider to slide, Cylindrical for both.",
"drive.stopped_by_contact": "Stopped at {value}: {a} would pass through {b}",
@@ -1536,6 +1594,11 @@
"help.joint.reach": "Moves a point on a part toward a target by changing the joints on its chain, within their limits. Use it to pose a mechanism by where its end should be, such as the tip of a robot arm.",
"help.joint.rigid_group": "Locks the selected parts together as they are, so they move as one. Use it for parts that belong together and have no motion between them.",
"help.joint.screw": "Links turning and sliding by a pitch, so each turn advances by that distance. Use it for lead screws, bolts and threaded spindles.",
"help.layout.apply": "Apply a saved layout to the current workspace. Retained editors keep their drafts.",
"help.layout.get": "Read the committed layout and discover registered workspaces.",
"help.layout.reset": "Restore the current workspace's registered default layout. User presets remain available.",
"help.layout.save": "Save the complete layout under a name. Replacing a user preset requires explicit overwrite permission.",
"help.layout.workspace.select": "Select a registered layout workspace and remember the outgoing layout.",
"help.library.apply": "Applies a material or appearance from a local library file to bodies, faces or components. Use it to reuse materials you saved from another design.",
"help.library.export": "Saves the design's materials and appearances to a library file. Use it to share them or reuse them in other designs.",
"help.library.inspect": "Checks a library file and lists its materials and appearances, without changing the design. Use it to see what a library holds before applying it.",
@@ -1549,6 +1612,21 @@
"help.notice": "NOTICE",
"help.occurrence.ground": "Fixes a part in place, or frees it again, so that joints move the other parts around it. Use it for the frame or base that the rest of the assembly is built on.",
"help.occurrence.move": "Moves an assembly part only along freedoms allowed by its joints. Select a part face, then drag an available arrow or arc.",
"help.panel.activate": "Select an open panel tab without requesting keyboard focus.",
"help.panel.close": "Close a panel through its content lifecycle guard. Its placement is remembered.",
"help.panel.collapse": "Collapse or expand a panel section while retaining its requested dimensions.",
"help.panel.dismiss": "Dismiss the temporary view of a collapsed panel.",
"help.panel.float": "Move a panel into an explicitly positioned floating section.",
"help.panel.focus": "Focus a panel, reopening its retained state and expanding its section when needed.",
"help.panel.list": "Read registered panel kinds, committed instances, active tabs, availability and host capabilities.",
"help.panel.move": "Move a panel into a new section or join an existing tab group.",
"help.panel.native_close": "Return a native panel section to its remembered dock without cancelling its content.",
"help.panel.open": "Open a panel kind with an explicit instance identity and placement for a new instance.",
"help.panel.peek": "Temporarily show a collapsed panel without changing its saved size or collapse state.",
"help.panel.popout": "Move a panel to a native host when supported. Browser hosts use a floating section.",
"help.panel.reset_pair": "Restore an adjacent dock pair's default size ratio within its shared bounds.",
"help.panel.resize": "Set a panel section's outer width and height within every tab's bounds.",
"help.panel.resize_pair": "Resize adjacent dock sections together while conserving their shared extent.",
"help.parameters.change": "Opens the table of named values and expressions that drive the dimensions of the design. Use it to change a size in one place and let every feature that uses it follow.",
"help.parameters.export": "Saves the design's parameters as CSV or JSON. Use it to edit them in a spreadsheet or move them to another design.",
"help.parameters.graph": "Shows which parameters and features use which others, and any loops or errors among them. Use it to see what a change to a parameter will affect.",
@@ -1560,7 +1638,7 @@
"help.parts.resize": "Changes the size or length of an inserted standard part and rebuilds it; its joints keep it seated. Use it when a screw turns out too long or a hole needs another size.",
"help.plastic.assign_rule": "Assigns a plastic rule to bodies, so their plastic features take its draft and clearance as defaults. Use it to set up a moulded part once for all its features.",
"help.plastic.boss": "Adds a screw boss on a face: a post with an optional hole, draft, root fillet and supporting ribs. Use it for screw points in moulded or printed housings.",
"help.plastic.lip": "Adds a lip or a matching groove along the rim of a shelled body. Use it so that two housing halves locate on each other and close tightly.",
"help.plastic.lip": "Adds a lip or a matching groove along the rim of a shelled body (Shell a face open first, then pick the rim around that opening). Use it so that two housing halves locate on each other and close tightly.",
"help.plastic.manage_rules": "Creates and edits plastic rules: material, wall thickness, radii, clearance and draft. Use them to keep the features of a moulded part consistent.",
"help.plastic.rest": "Adds a rest on a face: a round or rectangular pad, solid or hollow, with draft. Use it as a support or standoff in a moulded housing.",
"help.plastic.snap_fit": "Adds a snap-fit hook on a face, with its length, thickness and catch. Use it to clip parts together without screws.",
@@ -1885,21 +1963,21 @@
"joint.rigid_group_select_components": "Select two or more components.",
"joint_move.blocked_hint": "Check the joint type (such as Rigid), grounding, joint limits, and parameter-driven values.",
"joint_move.blocked_title": "Cannot move this part",
"joint_move.coarse": "Near-axis rotation step (°)",
"joint_move.coarse": "Near-axis step",
"joint_move.conflict": "A joint of this part conflicts with the others: {joint}.",
"joint_move.conflict_hint": "Edit, suppress or delete the conflicting joint so the other joints can place the part.",
"joint_move.distance": "Distance from start",
"joint_move.done": "Done",
"joint_move.fine": "Far-axis rotation step (°)",
"joint_move.fine": "Far-axis step",
"joint_move.fixed": "This part has no movable joint axis.",
"joint_move.help": "Move — drag an arrow or ring. Esc cancels the current drag.",
"joint_move.legacy_hint": "Turn off to use the previous Move panel.",
"joint_move.precision": "Pull outside the dashed guide for fine rotation.",
"joint_move.legacy_hint": "Off: the previous Move panel.",
"joint_move.precision": "Pull outside the dashed guide for the fine step.",
"joint_move.rotation": "Rotation from start",
"joint_move.select": "Select a face or one assembly part, then choose Move.",
"joint_move.settings": "Assembly Move",
"joint_move.step": "Step",
"joint_move.use_handles": "Use joint motion handles for assembly Move",
"joint_move.use_handles": "Use joint motion handles for Move",
"keymap.command_id": "Command ID",
"keymap.empty": "No matching commands",
"keymap.use_anyway": "Use {key} here anyway",
@@ -1959,6 +2037,7 @@
"material.selection.mixed": "Different materials",
"measure.at": "At",
"measure.item": "Item {number}",
"measure.line_to_centre": "Line to center",
"measure.quick_copied": "Measurement copied",
"measure.quick_copy": "Click to copy the measurement",
"measure.quick_edges": "{count} edges",
@@ -2017,6 +2096,12 @@
"navbar.assistant": "Assistant",
"navbar.custom.add_slot": "Add {slot}",
"navbar.custom.always": "Always visible",
"navbar.custom.anchor.bottom_centre": "Bottom center",
"navbar.custom.anchor.bottom_left": "Bottom left",
"navbar.custom.anchor.bottom_right": "Bottom right",
"navbar.custom.anchor.top_centre": "Top center",
"navbar.custom.anchor.top_left": "Top left",
"navbar.custom.anchor.top_right": "Top right",
"navbar.custom.available": "{available} · drag onto the bar, or press Add",
"navbar.custom.buttons": "Buttons",
"navbar.custom.compact": "Compact",
@@ -2053,6 +2138,7 @@
"navbar.custom.reset_default": "Reset to default",
"navbar.custom.right": "Move right",
"navbar.custom.select": "No slot selected. Select a slot on the bar to move it, remove it or change how it looks.",
"navbar.custom.separator_hint": "A divider between groups of buttons. Drag it onto the bar, or press Add; use as many as you like.",
"navbar.custom.separators_full": "The bar already has four separators.",
"navbar.custom.show": "Show navigation bar",
"navbar.custom.shows": "Shows",
@@ -2206,21 +2292,37 @@
"prefs.appearance.viewport_logo": "Viewport and logo",
"prefs.applies_on_restart": "Applies on restart",
"prefs.assistant": "Assistant",
"prefs.assistant_connecting": "Connecting",
"prefs.assistant_follow_help": "Look At sketches move the view with the pointer.",
"prefs.assistant_limit": "Limit",
"prefs.assistant_limited": "Limited",
"prefs.assistant_pointer_speed": "Pointer speed",
"prefs.assistant_seconds": "seconds",
"prefs.assistant_seconds_aria": "Seconds",
"prefs.assistant_use_care": "Use with care",
"prefs.auto_operation": "Choose the operation automatically",
"prefs.auto_operation_note": "Feature windows pick Join, New Body or Cut from what the new material touches, until you pick one. Off: they keep the operation they open with.",
"prefs.auto_operation_note": "Join, New Body or Cut, from what the material touches.",
"prefs.axes": "Axes",
"prefs.axis": "Axis",
"prefs.blur_cost": "Relative rendering cost. More bars usually mean more work.",
"prefs.blur_device": "Unavailable on this graphics device or rendering path",
"prefs.blur_guard": "Blur quality was reduced to keep frames responsive. The radius is unchanged.",
"prefs.blur_off": "Off",
"prefs.blur_reduce_transparency": "Disabled by the system's Reduce transparency setting",
"prefs.blur_software": "Unavailable with software graphics (gpu-software)",
"prefs.blur_source": "Unavailable in this workspace view",
"prefs.blur_web": "Unavailable on this web rendering path",
"prefs.bodies_folders": "Bodies folders",
"prefs.bodies_folders_always": "Always",
"prefs.bodies_folders_never": "Never",
"prefs.bodies_folders_several": "Several bodies only",
"prefs.bodies_folders_several": "Several",
"prefs.bottom": "Bottom",
"prefs.bottom_centre": "Bottom center",
"prefs.bottom_right": "Bottom right",
"prefs.browser": "Browser",
"prefs.browser_components_open": "Components start open",
"prefs.browser_components_open_note": "Every component in the browser starts open, as before, instead of closed until you open it.",
"prefs.browser_eye_left": "Eye next to the fold arrow",
"prefs.browser_eye_left": "Eye beside the fold arrow",
"prefs.browser_eye_left_note": "Browser rows show their show/hide eye next to the fold arrow, as before, instead of in a column at the right end of the rows.",
"prefs.browser_reveal_on_hover": "Reveal on hover",
"prefs.browser_show_note": "Shown keeps the browser on screen. Reveal on hover keeps it out of sight until the pointer comes over its place or the thin tab at its left edge, and it stays open while the pointer is inside it or a menu from it is open.",
@@ -2249,15 +2351,34 @@
"prefs.dark_parts_shading_help": "Shade the view in linear light with a soft sky fill, so dark faces keep a gradient instead of turning black",
"prefs.dead_zone_note": "The hatched band is the dead zone: motion inside it is ignored.",
"prefs.default": "default",
"prefs.design": "Design defaults",
"prefs.design_note": "What new designs start with. Open designs keep their own.",
"prefs.design": "Design",
"prefs.design_note": "What new designs start with.",
"prefs.design_tools.ask": "Ask",
"prefs.design_tools.degrees": "Degrees",
"prefs.design_tools.double_click_help": "Or to edit a linked one in place.",
"prefs.design_tools.ghost": "Show moved parts' old place as a ghost",
"prefs.design_tools.hole_moves_help": "Picking the face again moves the hole instead of removing it.",
"prefs.design_tools.hole_snaps_help": "The middle of a face and the centers of its round edges.",
"prefs.design_tools.imported_files": "Imported files",
"prefs.design_tools.note": "Note",
"prefs.design_tools.off": "Off",
"prefs.design_tools.placement_group": "Rest, Boss, Snap Fit",
"prefs.design_tools.rows": "Rows",
"prefs.devices": "3D mouse",
"prefs.dialog_buttons": "Dialog buttons",
"prefs.dialogs": "Dialogs",
"prefs.dialogs_edge": "At the view's edge",
"prefs.dialogs_left_column": "In the left column",
"prefs.dialogs_note": "Where command dialogs open: docked at the view's edge, or in the left column in place of the browser and the History, which come back as they were when the dialog closes.",
"prefs.dimension_step_angle": "What the window's arrows, Up/Down and the wheel add to an angle",
"prefs.dimension_step_length": "What the window's arrows, Up/Down and the wheel add to a length",
"prefs.dimension_steps": "Window steps",
"prefs.dimension_values": "Dimension values",
"prefs.dimension_values_note": "Where you type a dimension's value. A window has arrows that step it; the Dimension tool places the text first.",
"prefs.dimension_values_view": "In the view",
"prefs.dimension_values_window": "In a window",
"prefs.display": "Display",
"prefs.drawings": "Drawings",
"prefs.edges": "Edges and shading",
"prefs.extend": "Extend",
"prefs.extra_navigation": "Ctrl+left drag on empty space orbits; hold Space to pan.",
@@ -2292,6 +2413,23 @@
"prefs.graphics_card_restart": "Takes effect after restarting SolidCraft. WGPU_ADAPTER_NAME overrides this choice.",
"prefs.graphics_card_unused": "The saved graphics card was not used for this window; the card in use is shown in Help › About and in the status line. Try Automatic, or set WGPU_ADAPTER_NAME to a unique part of the card's name.",
"prefs.graphics_note": "Trade speed for smoothness. Each choice shows its effect on a sample.",
"prefs.graphics_page.current_view": "Current view",
"prefs.graphics_page.fastest": "Fastest",
"prefs.graphics_page.fold_items": "Fold items",
"prefs.graphics_page.in_view": "In the view",
"prefs.graphics_page.live_details": "Live details",
"prefs.graphics_page.own_mix": "Your own mix",
"prefs.graphics_page.quick_measure": "Quick measurement",
"prefs.graphics_page.reference_geometry_through_bodies": "Reference geometry through bodies",
"prefs.graphics_page.restart": "Restart needed",
"prefs.graphics_page.sharpest": "Sharpest",
"prefs.graphics_page.single_surface_analysis": "Single surface analysis",
"prefs.graphics_page.status_chips": "Status chips",
"prefs.graphics_page.subtitle": "Trade speed for smoothness.",
"prefs.graphics_page.supersampling": "Supersampling",
"prefs.graphics_page.supersampling_adaptive": "Only while the view is still",
"prefs.graphics_page.surfaces_edges": "Surfaces and edges",
"prefs.graphics_page.view_bar": "Also in the view bar",
"prefs.graphics_presets_note": "Presets set the choices below together.",
"prefs.hide_constraint_glyphs_zoomed_out": "Hide constraint glyphs when zoomed out",
"prefs.hide_constraint_glyphs_zoomed_out_note": "Constraint glyphs leave the view when the sketch is zoomed out far enough that they would clutter it. Off: they stay at a fixed screen size at every zoom.",
@@ -2300,7 +2438,7 @@
"prefs.history_origin_bodies_note": "The History on the left or under the browser starts with the active component's Bodies folder and the Origin, with their eyes and menus as in the browser. The History at the bottom is unchanged.",
"prefs.history_play_button": "Play History button in the History controls",
"prefs.history_play_button_note": "Puts Play History between Go back one step and Go forward one step in the History controls. You can also right-click the controls to show or hide it.",
"prefs.history_sketches": "Sketches",
"prefs.history_sketches": "Sketches in History",
"prefs.history_sketches_in_order": "In order",
"prefs.history_sketches_note": "With Sketches set to Under their feature, the History at the left lists each sketch one level in, under the first feature built from it, whenever it is wide enough for names. The timeline at the bottom always keeps the creation order.",
"prefs.history_sketches_under_feature": "Under their feature",
@@ -2325,6 +2463,22 @@
"prefs.language_system": "System",
"prefs.layout": "Layout",
"prefs.layout_note": "Compact docks panels to the window edges. Browser translucency is available in Floating. Change the Assistant side on the Assistant page.",
"prefs.layout_page.active_commands": "Highlight active commands",
"prefs.layout_page.always": "Always",
"prefs.layout_page.card": "Card",
"prefs.layout_page.column": "Column",
"prefs.layout_page.hover": "Hover",
"prefs.layout_page.icons_labels": "Icons + labels",
"prefs.layout_page.ok_cancel": "OK and Cancel",
"prefs.layout_page.on_hover": "On hover",
"prefs.layout_page.open": "Open",
"prefs.layout_page.origin_bodies": "Origin and bodies on top",
"prefs.layout_page.panels": "Panels",
"prefs.layout_page.pinned_menus": "Pinned commands out of menus",
"prefs.layout_page.play_history": "Play History button",
"prefs.layout_page.slot_count": "{count} of {max} slots",
"prefs.layout_page.view_corner": "Also in the view's corner",
"prefs.layout_page.window": "Window",
"prefs.left": "Left",
"prefs.left_click": "Left click",
"prefs.line_drag_draws_one": "Press and drag draws one line",
@@ -2353,6 +2507,13 @@
"prefs.mouse_heading": "Mouse",
"prefs.mouse_mapping": "Mouse mapping",
"prefs.navigation": "Navigation",
"prefs.navigation.axes_signed": "Axes",
"prefs.navigation.cube_compact": "Cube",
"prefs.navigation.device": "Device",
"prefs.navigation.free": "Free",
"prefs.navigation.orbit_style": "Orbit style",
"prefs.navigation.radial_help": "Hold or flick the right button for a ring of tools.",
"prefs.navigation.turntable": "Turntable",
"prefs.navigation_note": "How the mouse, trackpad and 3D mouse move the view.",
"prefs.navigator_fit": "Fit the model after a navigator view change",
"prefs.navigator_fit_note": "A face, corner or arrow of the view cube, or an axis of the gizmo, also looks at the middle of the model from the one distance that fits it from every side (its bounding sphere). Home uses the same zoom and centre while this is on, so views chosen after it only turn. Off: the view turns in place, keeping its target and zoom.",
@@ -2369,11 +2530,15 @@
"prefs.origin_plane_names_note": "Names shown in the browser, dialogs and lists. {top} is XY, {front} is XZ and {right} is YZ, with Z up. Commands, scripts and saved designs keep XY, XZ and YZ.",
"prefs.overall_speed": "Overall speed",
"prefs.parameter_list_rows": "Parameter list rows",
"prefs.parameter_list_rows_tip": "Down arrow in an empty value field lists the parameters, favourites first, up to this many. Fewer keeps the list light in a large design.",
"prefs.parameter_list_rows_tip": "The most parameters Down arrow lists in an empty expression box.",
"prefs.persistent_ribbon_pressed": "Highlight active ribbon commands",
"prefs.persistent_ribbon_pressed_help": "Highlight ribbon commands while their effects are on. Turn off to keep the previous neutral appearance and command help.",
"prefs.perspective_fov": "Field of view",
"prefs.placement": "Placement",
"prefs.plugins_enabled": "Enabled",
"prefs.plugins_installed": "Installed",
"prefs.plugins_intro": "Sandboxed add-ons.",
"prefs.plugins_show_folder": "Show plugins folder",
"prefs.prefer_free_sketch_points": "Prefer movable sketch points when points overlap",
"prefs.preview_choices": "Preview of your choices",
"prefs.preview_tint_new_faces": "Tint new surface in a preview",
@@ -2431,8 +2596,43 @@
"prefs.shift_add_ctrl_toggle": "Shift adds only; Ctrl / Cmd toggles",
"prefs.shift_add_note": "On: Shift adds, Ctrl / Cmd toggles. Off (default): both toggle.",
"prefs.shortcuts": "Shortcuts",
"prefs.shortcuts_import": "Import",
"prefs.shortcuts_intro": "Keys for each command.",
"prefs.shortcuts_not_assigned": "Not assigned",
"prefs.shortcuts_press": "Press a key...",
"prefs.single_surface_analysis": "Use a single surface analysis",
"prefs.single_surface_analysis_help": "When off, surface shading and Continuity can stay on together. Turning this on keeps the last analysis started or changed.",
"prefs.sketch.align_points": "Line up with other sketch points",
"prefs.sketch.align_points_note": "Drawing lines align with points on both axes at once.",
"prefs.sketch.auto_dimensions": "Dimension new shapes fully",
"prefs.sketch.auto_dimensions_note": "Typed values plus every dimension that fixes the shape.",
"prefs.sketch.auto_locate": "Place from the sketch origin",
"prefs.sketch.auto_locate_note": "With full dimensions.",
"prefs.sketch.center_arc": "Center-point arc follows the cursor",
"prefs.sketch.click_selects": "Click selects",
"prefs.sketch.conflict_colour": "Conflict color",
"prefs.sketch.drawing": "Drawing",
"prefs.sketch.exit_keep": "Keep",
"prefs.sketch.exit_previous": "Return to the view before",
"prefs.sketch.exit_unless_rotated": "Unless turned",
"prefs.sketch.faces_edges": "Faces and edges",
"prefs.sketch.hide_glyphs": "Hide glyphs zoomed out",
"prefs.sketch.marks": "Marks in the sketch",
"prefs.sketch.note": "Drawing, snapping and selecting.",
"prefs.sketch.opening_closing": "Opening and closing",
"prefs.sketch.prefer_free_points": "Prefer movable points where they overlap",
"prefs.sketch.project_options": "Ask for Project options first",
"prefs.sketch.recent_snap": "Snap to where two guides cross",
"prefs.sketch.select_other_hidden": "Select Other reaches hidden geometry",
"prefs.sketch.shortcut_cycles_modes": "Shortcut again switches mode",
"prefs.sketch.shortcut_cycles_modes_tip": "Off: it starts the tool again.",
"prefs.sketch.snapping_guides": "Snapping and guides",
"prefs.sketch.state_icons": "State icons",
"prefs.sketch.tracking_guides": "Center and midpoint guides",
"prefs.sketch.tracking_guides_note": "Temporary horizontal and vertical guides from hovered centers and edge midpoints.",
"prefs.sketch.tracking_points": "Guides from sketch points too",
"prefs.sketch.unless_turned_note": "return if not rotated",
"prefs.sketch.whole_bodies": "Whole bodies",
"prefs.sketch_align_points": "Line up with other sketch points while drawing",
"prefs.sketch_align_points_note": "While drawing, a point lines up horizontally with one sketch point and vertically with another at the same time, with a dotted guide, and the click keeps both alignments. Off: only with the previous point, one axis at a time.",
"prefs.sketch_conflict_colour": "Conflicting geometry in the conflict color",
@@ -2465,11 +2665,11 @@
"prefs.source": "Source",
"prefs.speed": "Speed",
"prefs.stand_up_note": "After a STEP or IGES file opens with Z up, offer a quarter turn about X.",
"prefs.stand_up_unknown": "For files from unknown programs",
"prefs.stand_up_unknown": "Files from unknown programs",
"prefs.stand_up_unknown.ask": "Ask in a dialog",
"prefs.stand_up_unknown.note": "Show a note",
"prefs.stand_up_unknown.off": "Don't offer",
"prefs.stand_up_unknown_note": "How the offer is made when the file's header names no program known to save Y up. A note is shown only when the file seems to lie on its back; Ask in a dialog asks about every such file, as before.",
"prefs.stand_up_unknown_note": "How the offer is made when the file names no known program.",
"prefs.status_bar": "Status bar",
"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 color 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.status_done": "Done",
@@ -2505,6 +2705,10 @@
"prefs.theme_follow_off": "Off",
"prefs.theme_follow_stop": "Stop following",
"prefs.theme_import_hint": "Click to choose a theme file, drop one here, or press Ctrl+V (Cmd+V on a Mac) with a theme copied",
"prefs.theme_notice_dismiss": "Dismiss",
"prefs.theme_option_blur": "Blur behind panels",
"prefs.theme_option_blur_help": "Blur behind translucent panels, in physical pixels. Zero is off.",
"prefs.theme_option_invalid": "Saved value for {option} is invalid; using the theme default. The saved value is kept.",
"prefs.theme_option_opacity": "Opacity",
"prefs.theme_option_opacity_help": "Adjust floating panel opacity. A readability floor protects text over viewport colors.",
"prefs.theme_option_tab_colours": "Tab colors",
@@ -2513,6 +2717,7 @@
"prefs.theme_option_tab_fade_ms_help": "Fade panel and viewport colors when switching tabs. Zero switches instantly.",
"prefs.theme_options_badge": "Options",
"prefs.theme_options_more": "More · {count}",
"prefs.theme_options_newer": "This theme requires a newer SolidCraft. Upgrade, then reimport the original theme to recover its option definitions.",
"prefs.theme_options_reset": "Reset",
"prefs.theme_options_unavailable": "Unavailable on this device",
"prefs.theme_paste": "Paste theme",
@@ -2542,8 +2747,8 @@
"prefs.validation.label": "Full kernel check",
"prefs.validation.off": "Off (experimental)",
"prefs.validation_note": "After every step: checks each sweep, boolean and blend.\nOnce per feature: checks each feature's result once.\nOff (experimental): no check.",
"prefs.value_boxes_in_view": "Show values beside their handles in the view",
"prefs.value_boxes_in_view_tip": "A command's value (Distance, Radius…) in a box next to its handle, over the model. Off: you type it in the command window, which takes the keyboard after a pick, and nothing covers the model.",
"prefs.value_boxes_in_view": "Show values beside their handles",
"prefs.value_boxes_in_view_tip": "Extrude's Distance, Fillet's Radius... as a box in the view.",
"prefs.view": "View behavior",
"prefs.view_confirm_corner": "OK and Cancel in the view's corner",
"prefs.view_confirm_corner_note": "While a dialog or a sketch is open, a check and a cross at the top right of the view do what the dialog's OK and Cancel do, or finish and cancel the sketch. The view navigator moves down below them.",
@@ -3944,11 +4149,11 @@
"select two components": "select two components",
"set by the sketch's lines": "set by the sketch's lines",
"settings.display_quality_note": "Only the display. Designs and exports are not affected.",
"settings.double_click_components": "Double-click a component to activate it, or to edit a linked one in place",
"settings.double_click_components": "Double-click a component to activate it",
"settings.frame_rate_details_note": "Adds a graph of the last frames' times with the triangles drawn over it, a histogram of frame times and what the GPU drew. Click the frame rate in the view to show or hide them.",
"settings.frame_rate_note": "Shows frames per second in the view's bottom-right corner.",
"settings.insert_lists_open_designs": "Insert Design lists the designs open in other tabs first",
"settings.measure_axis_distances": "Show X/Y/Z distances",
"settings.insert_lists_open_designs": "Insert Design lists open designs first",
"settings.measure_axis_distances": "Axis distances in Measure",
"settings.measure_axis_distances_help": "Show world-axis component dimensions alongside the total distance while Measure is open.",
"settings.msaa_applies_on_restart": "Anti-aliasing applies the next time SolidCraft starts",
"settings.msaa_reset": "{setting}: {requested}× isn't supported by this graphics device; it is now {reset}× (supported: {supported}).",
@@ -3958,7 +4163,7 @@
"settings.reset_intro": "These settings don't work on this computer and would keep SolidCraft from starting, so they were changed:",
"settings.reset_title": "Settings were changed",
"settings.supersampling_note": "Draws the view at a higher resolution and filters it down: smoother edges, lines and grid on screens below 4K. It costs GPU time: about 2.25× the pixels at 1.5× and 4× at 2×.",
"settings.update_changed_links": "Update changed linked designs when a tab comes back",
"settings.update_changed_links": "Update changed links when a tab comes back",
"settings.window_system_details": "Details",
"settings.window_system_note_now": "Applies after a restart. Now: {now}.",
"settings.window_system_wayland_limited": "Some features are limited on native Wayland.",
@@ -4360,7 +4565,7 @@
"ui.agent_chat_send_now_tip": "Stop the agent's current step and send this next (Ctrl+Enter)",
"ui.agent_chat_setup_body": "SolidCraft talks to a coding agent installed on this computer. Pick one in Preferences › AI.",
"ui.agent_chat_setup_title": "Set up an assistant",
"ui.agent_chat_side": "Agent chat side",
"ui.agent_chat_side": "Dock the chat",
"ui.agent_chat_signed_in": "signed in",
"ui.agent_chat_state_auth": "Sign-in needed",
"ui.agent_chat_state_busy": "Working…",
@@ -4379,7 +4584,7 @@
"ui.agent_chat_undone": "(undone)",
"ui.agent_chat_view_attached": "View attached",
"ui.agent_chat_waiting": "Waiting for you",
"ui.agent_cursor": "Agent cursor",
"ui.agent_cursor": "Agent pointer",
"ui.agent_cursor_speed": "Agent cursor speed",
"ui.agent_off_thread": "Keep the window responsive while agents run commands",
"ui.agent_off_thread_note": "An agent's slow commands (from the AI panel or an MCP client) compute beside the window: you can keep turning and inspecting the view, and stop one from the chip in the view. Off: each runs on the window's thread and the window waits for it.",
@@ -4425,14 +4630,14 @@
"ui.ai_key_note": "Stored in the system keychain, never in SolidCraft's settings. Leave it empty for a local server.",
"ui.ai_keychain_error": "The system keychain refused: {error}",
"ui.ai_kind": "Kind",
"ui.ai_mode_all": "Allow everything",
"ui.ai_mode_all": "Do everything",
"ui.ai_mode_all_note": "Nothing asks first. Crash reports stay off limits and the folder limits still apply.",
"ui.ai_mode_all_warning": "The agent can save, overwrite, export and replace your design without asking. Use this only with an agent you trust, on designs you have saved.",
"ui.ai_mode_ask": "Ask every time",
"ui.ai_mode_ask": "Ask first",
"ui.ai_mode_ask_note": "Every change to the design, and every file action, waits for your Allow.",
"ui.ai_mode_edits": "Auto-allow model edits",
"ui.ai_mode_edits": "Make edits",
"ui.ai_mode_edits_note": "Modeling commands run without asking; saving, opening, exporting and other file or destructive actions still ask.",
"ui.ai_mode_global": "As set above",
"ui.ai_mode_global": "Same as above",
"ui.ai_model": "Model",
"ui.ai_model_hint": "qwen3:14b",
"ui.ai_modes_scope": "These settings cover SolidCraft's tools. An agent's own tools (its shell, its file edits) follow the agent's own mode, which you pick in the chat panel's header when the agent has modes.",
@@ -4458,7 +4663,7 @@
"ui.ai_save": "Save",
"ui.ai_settings": "AI settings…",
"ui.ai_show_features": "Show AI features",
"ui.ai_show_features_note": "The agent chat: a panel where a coding agent you already use, or a model endpoint you choose, works on the open design. Off: no AI button, shortcut, menu entry or panel, and nothing is looked for or started.",
"ui.ai_show_features_note": "Off hides the Assistant everywhere and starts nothing. Everything below dims.\n\nThe agent chat: a panel where a coding agent you already use, or a model endpoint you choose, works on the open design. Off: no AI button, shortcut, menu entry or panel, and nothing is looked for or started.",
"ui.ai_switch_tip": "Change the agent for this conversation",
"ui.ai_test": "Test",
"ui.ai_test_acp_ok": "Answers: {agent}",
@@ -4492,7 +4697,7 @@
"ui.ai_tool_sum_then": "{first}, then {next}",
"ui.ai_tool_unfold": "Click to show what it returned",
"ui.ai_tool_waiting": "waiting",
"ui.ai_toolbar_button": "Show Assistant button in application bar",
"ui.ai_toolbar_button": "Assistant button in the application bar",
"ui.ai_toolbar_button_note": "The Assistant button in the application bar. Ctrl+Shift+A and the View menu still open the panel.",
"ui.ai_undo_tip": "Undo everything the agent changed for this message, as one step",
"ui.ai_url_hint": "https://example.com/v1",
@@ -4639,6 +4844,7 @@
"ui.default_modelling_orientation": "Default modeling orientation",
"ui.delete": "Delete",
"ui.delete_select_first": "Select what to delete first: features, bodies, components or sketch geometry",
"ui.dependents_select_first": "Select what to list the dependents of first",
"ui.depth": "Depth",
"ui.design": "Design",
"ui.design_properties.author": "Author",
@@ -4764,7 +4970,7 @@
"ui.extract_done": "The component is now its own design, linked here.",
"ui.extrude": "Extrude",
"ui.extrude_auto_to_object": "Extrude to a picked target",
"ui.extrude_auto_to_object_tip": "In Extrude, with a profile picked, picking a face, vertex or parallel plane off its plane switches the extent to To Object",
"ui.extrude_auto_to_object_tip": "A face, vertex or parallel plane switches the extent to To Object.",
"ui.factor": "Factor",
"ui.fade": "Fade",
"ui.fade_length": "Fade Length",
@@ -4796,6 +5002,10 @@
"ui.fillet_setback_least": "More than {least}",
"ui.fillet_setback_tip": "Where the smooth corner patch starts: how far along each edge from the corner. The default is {factor} × the radius. It must be more than the least setback, where the rounds meet without one: the radius where the faces are square to each other.",
"ui.fillet_smooth_radius": "Smooth radius change",
"ui.fillet_turn": "Turns",
"ui.fillet_turn_mitre": "Mitre",
"ui.fillet_turn_swept": "Swept",
"ui.fillet_turn_tip": "Where two rounds turn round a sharp corner edge between them, such as the rounds along a boss's base at its upright edges.\nMitre: the rounds run on until they meet in a mitre.\nSwept: the rounds stop at the corner edge and turn round it, the corner a torus tangent to both. Only for rounds of one radius round a straight, convex corner edge.",
"ui.fillet_type_choices_tip": "Constant radius: one radius all along the edge.\nChord length: the width across the round stays the same, whatever angle the faces meet at.\nVariable radius: a radius at each end, and more along the edge if you add them.\nCurvature continuous: a section without a kink in reflections where it meets flat faces (G2).\nSpline: the radius along the edge follows control radii you set.",
"ui.fillet_type_curvature": "Curvature continuous",
"ui.fillet_type_tip": "The section across the edge. Curvature continuous: a curvature-continuous (G2) section between planar faces, so reflections run from the faces into the round without a kink.",
@@ -4805,6 +5015,7 @@
"ui.fine": "Fine",
"ui.finest": "Finest",
"ui.finish_sketch": "Finish Sketch",
"ui.first_angle": "First angle",
"ui.fit": "Fit",
"ui.fix_unfix": "Fix/Unfix",
"ui.flat": "Flat",
@@ -4908,7 +5119,7 @@
"ui.hidePlanes": "Hide Planes",
"ui.hideProfile": "Hide Profile",
"ui.hide_component_history": "Hide its History",
"ui.hide_origin_by_default": "Hide origin by default",
"ui.hide_origin_by_default": "Hide the Origin folder in new designs",
"ui.hide_origin_note": "New designs start with the Origin folder hidden. A design keeps its own Origin setting once you show or hide it.",
"ui.history_back": "Go back one step",
"ui.history_below_browser": "Below browser",
@@ -5068,7 +5279,7 @@
"ui.pitch": "Pitch",
"ui.placement": "Placement",
"ui.placement_handles": "Placement handles",
"ui.placement_handles_tip": "Handles on the preview of a Rest, Boss or Snap Fit: drag the marker to move it across the model, turn it by its ring",
"ui.placement_handles_tip": "A marker to drag the feature across the model and a ring to turn it.",
"ui.plane": "Plane",
"ui.plane_at_angle": "Plane at Angle",
"ui.plugins": "Plugins",
@@ -5109,6 +5320,7 @@
"ui.profiles": "Profiles",
"ui.project": "Project",
"ui.project_face_edges_sketch": "Project face edges into new sketches",
"ui.projection_of_new_drawings": "Projection of new drawings",
"ui.properties": "Properties",
"ui.properties.copy_matrix": "Copy matrix",
"ui.properties.first_moments": "First moments",
@@ -5266,8 +5478,8 @@
"ui.start": "Start",
"ui.start_over": "Start over",
"ui.start_page": "Start page",
"ui.step_values": "Step values with the arrow keys and the wheel",
"ui.step_values_tip": "In a value field you are typing in, Up/Down change a plain number by 1 (Shift: 10, Alt: 0.1), and so does the mouse wheel over the field. Expressions are left as typed.",
"ui.step_values": "Step values with arrows and wheel",
"ui.step_values_tip": "Up/Down and the wheel over a value field change its number.",
"ui.stitch_tolerance_hint": "tight",
"ui.stretch": "Stretch",
"ui.supersampling": "Supersampling (SSAA)",
@@ -5294,6 +5506,7 @@
"ui.text": "Text",
"ui.theme": "Theme",
"ui.thickness": "Thickness",
"ui.third_angle": "Third angle",
"ui.thread": "Thread",
"ui.tie_ends": "Tie",
"ui.tie_ends_tip": "Constrain all untied ends",
+1
View File
@@ -1358,6 +1358,7 @@
"document.read_only_save": "Este diseño se abrió en solo lectura: guárdalo como un archivo nuevo",
"document.save_changes": "¿Guardar los cambios de {name}?",
"document.save_copy": "Guardar una copia…",
"document.tab_unsaved_changes": "Cambios sin guardar",
"document.unsaved_changes": "{name} tiene cambios sin guardar. Si no los guardas, se perderán.",
"drive.rigid": "{joint} es una junta rígida: mantiene sus partes quietas, así que no tiene nada que impulsar.",
"drive.stopped_by_contact": "Detenido en {value}: {a} atravesaría {b}",
+126
View File
@@ -556,6 +556,7 @@
"Section": "Coupe",
"Section (optional)": "Section (facultative)",
"Section position": "Position de la section",
"Sections": "Sections",
"Sections (one sketch each)": "Sections (une esquisse chacune)",
"Select Other": "Sélectionner un autre",
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "Sélectionner un autre élément : maintenir le bouton gauche immobile sur le modèle (ou clic droit › Sélectionner un autre…) pour lister tout ce qui se trouve sous le curseur ; cliquer de nouveau au même endroit pour sélectionner l'élément suivant. Double-cliquer avec le bouton du milieu pour revenir à l'accueil (Préférences › Navigation).",
@@ -793,6 +794,13 @@
"browser.sketch_attachment_lost": "{name} (attache perdue)",
"browser.sketch_editing": "{name} (en cours de modification)",
"browser.sketch_name": "Esquisse{number}",
"browser.sketches_to_parts_already_in_part": "déjà dans leur pièce",
"browser.sketches_to_parts_design_body": "sur un corps de la conception elle-même",
"browser.sketches_to_parts_moved": "{moved} esquisses déplacées vers la pièce sur laquelle elles se trouvent",
"browser.sketches_to_parts_no_exact_attachment": "sur une face sans rattachement exact",
"browser.sketches_to_parts_not_on_face": "pas sur la face d'une pièce",
"browser.sketches_to_parts_part_missing": "leur pièce n'existe plus",
"browser.sketches_to_parts_result": "{moved} esquisses déplacées vers la pièce sur laquelle elles se trouvent ; {stayed} sont restées en place : {why}",
"browser.units": "Unités : {units}",
"cam.flat": "Plateau",
"cam.follower": "Suiveur",
@@ -852,6 +860,7 @@
"cmd.component_extract": "Extraire dans un fichier",
"cmd.component_finish_edit": "Terminer la modification",
"cmd.component_from_bodies": "Créer des composants à partir des corps",
"cmd.component_hide_history": "Masquer l'historique du composant",
"cmd.component_insert_design": "Insérer une conception",
"cmd.component_links": "Lister les liens",
"cmd.component_list": "Lister les composants",
@@ -860,6 +869,7 @@
"cmd.component_move_bodies": "Déplacer les corps vers le composant",
"cmd.component_move_features": "Déplacer les fonctions vers le composant",
"cmd.component_move_sketches": "Déplacer les esquisses vers le composant",
"cmd.component_move_sketches_to_parts": "Déplacer les esquisses vers leurs pièces",
"cmd.component_paste_from_design": "Coller depuis une autre conception",
"cmd.component_paste_new": "Coller comme nouveau",
"cmd.component_properties": "Propriétés physiques du composant",
@@ -867,6 +877,7 @@
"cmd.component_rename": "Renommer le composant",
"cmd.component_replace_with_link": "Remplacer par un lien",
"cmd.component_revert_position": "Rétablir la position",
"cmd.component_show_history": "Afficher l'historique du composant",
"cmd.component_unlock": "Déverrouiller le contenu",
"cmd.component_update_links": "Mettre à jour les liens",
"cmd.config_activate": "Activer la configuration",
@@ -877,6 +888,7 @@
"cmd.config_rule": "Règle de configuration",
"cmd.config_rules": "Règles de configuration",
"cmd.config_table": "Afficher la table des configurations",
"cmd.construct.plane.axis_point": "Plan passant par un axe et un point",
"cmd.construct_axis_edge": "Axe passant par une arête",
"cmd.construct_axis_normal_to_face": "Axe perpendiculaire à une face",
"cmd.construct_axis_through_cylinder": "Axe d'un cylindre/cône/tore",
@@ -925,6 +937,11 @@
"cmd.explode_show": "Afficher la vue éclatée",
"cmd.expr_evaluate": "Évaluer une expression",
"cmd.face_delete": "Supprimer la face",
"cmd.file.export_3mf": "Exporter en 3MF",
"cmd.file.export_obj": "Exporter en OBJ",
"cmd.file.export_step": "Exporter en STEP",
"cmd.file.export_stl": "Exporter en STL",
"cmd.file.export_urdf": "Exporter en URDF",
"cmd.file_export": "Exporter",
"cmd.file_insert_mesh": "Insérer un maillage",
"cmd.file_insert_step": "Insérer un STEP",
@@ -942,6 +959,7 @@
"cmd.inspect_colored_components": "Activer les couleurs des composants",
"cmd.inspect_colored_components_disable": "Désactiver les couleurs des composants",
"cmd.inspect_compare": "Comparer les corps",
"cmd.inspect_compare_picture": "Comparer les images",
"cmd.inspect_continuity": "Analyse de continuité",
"cmd.inspect_curvature_comb": "Analyse par peigne de courbure",
"cmd.inspect_curvature_map": "Analyse par carte de courbure",
@@ -1032,6 +1050,9 @@
"cmd.pointcloud_edit": "Modifier le nuage de points",
"cmd.pointcloud_insert": "Insérer un nuage de points",
"cmd.pointcloud_list": "Nuages de points",
"cmd.render.compare.divider": "Déplacer le séparateur de comparaison des rendus",
"cmd.render.result.discard": "Écarter le résultat de rendu",
"cmd.render.result.select": "Sélectionner le résultat de rendu",
"cmd.render_aging_describe": "Vieillissement de surface en couches",
"cmd.render_backends": "Moteurs de rendu",
"cmd.render_batch": "Rendre toutes les prises de vue",
@@ -1246,6 +1267,7 @@
"cmd.timeline_rollback": "Déplacer le marqueur de chronologie",
"cmd.timeline_stop": "Arrêter l'historique",
"cmd.timeline_suppress": "Désactiver la fonction",
"cmd.ui.assistant": "Afficher/masquer l'assistant",
"cmd.view.render_browser": "Navigateur de rendu",
"cmd.view.render_dock": "Déplacer le panneau de rendu",
"cmd.view.render_focus": "Vue de rendu ciblée",
@@ -1280,9 +1302,11 @@
"cmd.view_tool": "Outil de navigation",
"cmd.view_top": "Vue de dessus",
"cmd.view_visual_style": "Style visuel",
"color.blue": "Bleu",
"color.copy": "Cliquer pour copier les valeurs de couleur",
"color.edit": "Cliquer pour modifier la couleur",
"color.gamma": "Valeurs de couleur affichées en espace gamma 0-255",
"color.green": "Vert",
"color.hex": "Hex",
"color.hex_help": "Saisissez #rrggbb ou #rrggbbaa ; six chiffres conservent l'opacité",
"color.hex_hint": "#rrggbb",
@@ -1290,6 +1314,7 @@
"color.linear": "Valeurs de couleur affichées en espace linéaire 0-1",
"color.recent": "Couleurs récentes",
"color.recent_help": "Les couleurs choisies dans n'importe quel champ de couleur apparaissent ici",
"color.red": "Rouge",
"color.saturation": "Saturation (pour cent)",
"color.saturation_value": "Saturation et valeur",
"color.selected": "Couleur sélectionnée",
@@ -1297,6 +1322,8 @@
"color.step_up": "Augmenter d'un pas",
"color.value": "Valeur, la luminosité (pour cent)",
"config.column_filter_empty": "Aucune colonne correspondante",
"config.rule_parameter_columns": "Seuls les paramètres ayant une colonne dans la table sont listés. Ajouter une colonne avec Ajouter une colonne (+) pour l'utiliser ici.",
"config.rule_suppression_columns": "Seules les fonctions ayant une colonne Désactiver dans la table sont listées. Ajouter une colonne avec Ajouter une colonne (+) pour la piloter ici.",
"contact.components_count": "{count} composants",
"contact.enabled_label": "activé",
"coupling.gear_1": "Engrenage 1",
@@ -1345,7 +1372,11 @@
"dialog.pattern_sketch_not_object": "Choisissez des corps ou des fonctions : une esquisse n'est ni l'un ni l'autre. Répétez ses courbes avec la Répétition ou la Symétrie de l'esquisse, ou extrudez-la d'abord.",
"dialog.select": "↖ Sélectionner",
"dialog.selected_count": "↖ {count} sélectionné(s)",
"dialog.selection_constraint": "Contrainte {id}",
"dialog.selection_external_curve": "{sketch} · courbe {id}",
"dialog.selection_guidance": "Sélectionner une entrée pour la modifier. Cliquer sur un objet sélectionné pour le retirer ; Ctrl/Cmd-clic le bascule aussi. Vérifier l'aperçu, puis appuyer sur OK.",
"dialog.selection_midpoint": "Point milieu · {id}",
"dialog.selection_profile": "{sketch} · profil {number}",
"dialog.selection_step": "{step} sur {total} : sélectionner {input}",
"dialog.step": "Étape",
"document.changed_on_disk": "{name} a été modifié sur le disque",
@@ -1360,6 +1391,7 @@
"document.read_only_save": "Cette conception a été ouverte en lecture seule : enregistrez-la dans un nouveau fichier",
"document.save_changes": "Enregistrer les modifications de {name} ?",
"document.save_copy": "Enregistrer une copie…",
"document.tab_unsaved_changes": "Modifications non enregistrées",
"document.unsaved_changes": "{name} contient des modifications non enregistrées. Si vous n'enregistrez pas, elles seront perdues.",
"drive.rigid": "{joint} est une liaison rigide : elle maintient ses pièces immobiles, il n'y a donc rien à piloter. Pour les laisser bouger, changer son type dans Modifier la liaison : Revolute pour tourner, Slider pour coulisser, Cylindrical pour les deux.",
"drive.stopped_by_contact": "Arrêté à {value} : {a} traverserait {b}",
@@ -1428,6 +1460,7 @@
"help.construct.axis.two_points": "Crée un axe de construction passant par deux points, sommets ou points d'esquisse. À utiliser pour un axe qui relie deux positions connues.",
"help.construct.plane.along_path": "Crée un plan de construction perpendiculaire à une arête ou une courbe, à une position choisie le long de celle-ci. À utiliser pour esquisser le profil d'un balayage ou une coupe d'une trajectoire.",
"help.construct.plane.at_angle": "Crée un plan de construction tourné d'un angle par rapport à un plan ou une face, autour d'un axe ou d'une arête. À utiliser pour des esquisses inclinées, par exemple une équerre à angle.",
"help.construct.plane.axis_point": "Crée un plan de construction contenant un axe et un point situé hors de cet axe. Sélectionner une origine ou un axe de construction, une arête rectiligne ou une ligne d'esquisse, puis un sommet, un point d'esquisse ou un point de construction. Un point situé sur l'axe ne définit pas un plan unique.",
"help.construct.plane.midplane": "Crée un plan de construction à mi-distance entre deux plans ou faces planes, ou bissecteur lorsqu'ils se rencontrent en formant un angle. À utiliser comme plan de symétrie.",
"help.construct.plane.offset": "Crée un plan de construction parallèle à un plan ou une face plane, à une distance donnée ou passant par un point. À utiliser pour esquisser à une hauteur précise au-dessus d'une face.",
"help.construct.plane.perpendicular": "Crée un plan de construction qui contient une droite ou une arête et qui est perpendiculaire à un plan ou une face. À utiliser pour esquisser à la verticale sur un modèle, le long d'une arête.",
@@ -1804,6 +1837,8 @@
"joint_move.blocked_hint": "Vérifiez le type de liaison (par exemple rigide), la fixation de la pièce, les limites de la liaison et les valeurs pilotées par des paramètres.",
"joint_move.blocked_title": "Impossible de déplacer cette pièce",
"joint_move.coarse": "Pas de rotation près de l’axe (°)",
"joint_move.conflict": "Une liaison de cette pièce est en conflit avec les autres : {joint}.",
"joint_move.conflict_hint": "Modifier, désactiver ou supprimer la liaison en conflit pour que les autres liaisons puissent placer la pièce.",
"joint_move.distance": "Distance depuis le départ",
"joint_move.done": "Terminé",
"joint_move.fine": "Pas de rotation loin de l’axe (°)",
@@ -1903,6 +1938,8 @@
"motion.track_name": "Piste {number}",
"nav.alias.help": "Maj+Alt+glisser gauche fait orbiter, Maj+Alt+milieu fait un panoramique, Maj+Alt+droit zoome ; glisser avec le milieu fait aussi un panoramique et glisser avec le droit fait orbiter",
"nav.blender.help": "Glisser avec le milieu fait orbiter, Maj+milieu fait un panoramique, Ctrl+milieu zoome ; glisser avec le droit ne touche pas la caméra",
"nav.creo.help": "Glisser avec le milieu fait orbiter, Maj+milieu fait un panoramique, Ctrl+glisser avec le milieu zoome verticalement (vers le bas pour zoomer) ; la molette zoome",
"nav.creo.label": "Creo",
"nav.free_orbit.help": "La souris et la souris 3D tournent en passant par les deux pôles et conservent le roulis. L'alignement des axes avec Maj est désactivé.",
"nav.free_orbit.label": "Orbite libre",
"nav.free_orbit.snap_help": "La souris et la souris 3D tournent en passant par les deux pôles et conservent le roulis. Maintenir Maj pendant un glissement d'orbite pour aligner la direction de vue et le roulis.",
@@ -1912,6 +1949,7 @@
"nav.hfss.help": "Alt+glisser gauche fait un panoramique, Maj+Alt+gauche zoome, Ctrl+glisser gauche sur un espace vide fait orbiter",
"nav.inventor.help": "Glisser avec le milieu fait un panoramique, Maj+milieu fait orbiter, Ctrl+milieu zoome, glisser avec le droit fait orbiter",
"nav.maya.help": "Alt+glisser gauche fait orbiter, Alt+milieu fait un panoramique, Alt+droit zoome ; glisser avec le milieu fait aussi un panoramique",
"nav.nx.help": "Glisser avec le milieu fait orbiter, Maj+milieu fait un panoramique (milieu + droit dans NX), Ctrl+milieu zoome (milieu + gauche dans NX) ; glisser avec le droit ne touche pas la caméra",
"nav.openscad.help": "Glisser avec le gauche dans le vide fait orbiter, Maj+glisser gauche sélectionne par rectangle, glisser la géométrie d'esquisse la modifie ; glisser avec le droit ou le milieu fait un panoramique, Maj+droit ou Maj+milieu zoome",
"nav.perspective.sketch": "La modification d'une esquisse utilise la vue orthographique. Terminer l'esquisse pour passer en perspective.",
"nav.right_drag_orbits.help": "Désactivé : glisser avec le droit ne touche pas la caméra et le bouton droit ouvre seulement le menu contextuel. Choisir l'affectation des boutons de la souris SolidWorks le désactive.",
@@ -2130,6 +2168,8 @@
"prefs.button_left": "Glisser gauche",
"prefs.button_middle": "Glisser milieu",
"prefs.button_right": "Glisser droit",
"prefs.carousel_next": "Suivant",
"prefs.carousel_previous": "Précédent",
"prefs.choose_font": "Choisir un fichier de police…",
"prefs.click": "Clic",
"prefs.contact_shading": "Ombrage de contact doux",
@@ -2142,6 +2182,10 @@
"prefs.ctrl_cmd": "Ctrl / Cmd",
"prefs.curved_surfaces": "Surfaces courbes",
"prefs.curves": "Surfaces courbes",
"prefs.cut_preview_tint": "Afficher en rouge la matière retirée",
"prefs.cut_preview_tint_help": "Un aperçu remplit la matière qu'une fonction retire, et la forme qu'elle remplace, d'un fantôme rouge translucide. Désactivée, ces remplissages sont entièrement transparents et seuls leurs contours sont tracés.",
"prefs.dark_parts_shading": "Conserver la forme des pièces sombres",
"prefs.dark_parts_shading_help": "Ombrer la vue en lumière linéaire avec un léger éclairage de ciel, pour que les faces sombres gardent un dégradé au lieu de devenir noires",
"prefs.dead_zone_note": "La bande hachurée est la zone morte : les mouvements à l'intérieur sont ignorés.",
"prefs.default": "par défaut",
"prefs.design": "Valeurs par défaut des conceptions",
@@ -2156,6 +2200,8 @@
"prefs.edges": "Arêtes et ombrage",
"prefs.extend": "Étendre",
"prefs.extra_navigation": "Ctrl+glisser gauche dans le vide fait orbiter ; maintenir Espace pour un panoramique.",
"prefs.extrude_view_isometric": "Passer la vue en isométrique à l'ouverture de l'extrusion",
"prefs.extrude_view_return": "Revenir à la vue précédente à la fermeture de l'extrusion",
"prefs.follows_os": "Suit le système",
"prefs.font": "Police",
"prefs.font_fallback": "Les caractères absents de la police choisie utilisent la police intégrée, pour qu'aucun libellé n'affiche de case vide.",
@@ -2171,6 +2217,7 @@
"prefs.graphics_card_auto": "Automatique",
"prefs.graphics_card_missing": "La carte graphique enregistrée n'est pas disponible ; Automatique est utilisé.",
"prefs.graphics_card_restart": "Prend effet après le redémarrage de SolidCraft. WGPU_ADAPTER_NAME prévaut sur ce choix.",
"prefs.graphics_card_unused": "La carte graphique enregistrée n'a pas été utilisée pour cette fenêtre ; la carte utilisée est indiquée dans Aide › À propos et dans la barre d'état. Essayer Automatique, ou définir WGPU_ADAPTER_NAME avec une partie unique du nom de la carte.",
"prefs.graphics_note": "Compromis entre vitesse et fluidité. Chaque choix montre son effet sur un exemple.",
"prefs.graphics_presets_note": "Les préréglages définissent ensemble les choix ci-dessous.",
"prefs.hide_constraint_glyphs_zoomed_out": "Masquer les symboles de contraintes en cas de zoom arrière",
@@ -2185,6 +2232,8 @@
"prefs.history_sketches_note": "Avec Esquisses réglé sur Sous leur fonction, l'historique de gauche liste chaque esquisse un niveau en retrait, sous la première fonction construite à partir d'elle, dès qu'il est assez large pour les noms. La chronologie en bas garde toujours l'ordre de création.",
"prefs.history_sketches_under_feature": "Sous leur fonction",
"prefs.hold_stop_snapping": "Maintenir pour désactiver l'accrochage",
"prefs.home_fit_size": "Taille de Accueil et Cadrer",
"prefs.home_fit_size_note": "Part de la vue que remplit le modèle quand Accueil, Cadrer, l'ouverture d'une conception ou une vue du repère de navigation recadre la vue, que le réglage ci-dessous soit activé ou non. 100 % : remplit la vue entre les panneaux, la boussole et la barre de navigation.",
"prefs.icons_only": "Icônes seules",
"prefs.imported_file": "Fichier importé",
"prefs.in_browser": "Dans le navigateur",
@@ -2193,15 +2242,22 @@
"prefs.interface_language": "Langue de l'interface",
"prefs.interface_note": "L'apparence de SolidCraft et la place de ses éléments.",
"prefs.interval_minutes": "{n} min",
"prefs.invert_curved_roll_arrows": "Inverser les flèches courbes de roulis",
"prefs.invert_curved_roll_arrows_note": "Les deux flèches courbes au coin du cube font rouler la vue dans le plan de l'écran. Activé : la flèche horaire fait rouler dans le sens antihoraire et la flèche antihoraire dans le sens horaire. Désactivé : chaque flèche fait rouler la vue dans le sens de sa courbure.",
"prefs.invert_navigator_arrows": "Inverser les flèches de rotation du cube de vue",
"prefs.invert_navigator_arrows_note": "Les flèches autour du cube font orbiter la caméra dans le sens où elles pointent, comme dans Fusion et Inventor : la flèche de gauche pointe vers la droite et fait pivoter la caméra vers la droite. Désactivé : une flèche fait tourner le cube vers le côté où elle se trouve, donc la flèche de gauche fait pivoter la caméra vers la gauche. Les flèches courbes de roulis ne sont pas concernées.",
"prefs.keep_work_on_tab_switch": "Garder dialogues et outils ouverts en changeant de conception",
"prefs.keep_work_on_tab_switch_note": "Chaque onglet de conception garde son dialogue ou son outil ouvert, avec ce que vous y avez sélectionné et saisi, ainsi que son onglet de barre d'outils. Vous les retrouvez en revenant à la conception. Désactivé : changer de conception les annule et affiche l'onglet Modèle.",
"prefs.labels": "Libellés",
"prefs.language_system": "Système",
"prefs.layout": "Disposition",
"prefs.layout_note": "Compacte : ancre les panneaux aux bords de la fenêtre. La transparence du navigateur est disponible en mode Flottante. Changez le côté de l'assistant dans la page Assistant.",
"prefs.left": "Gauche",
"prefs.left_click": "Clic gauche",
"prefs.line_drag_draws_one": "Appuyer et glisser trace une seule ligne",
"prefs.line_drag_draws_one_note": "Dans l'outil Ligne, appuyer, faire glisser puis relâcher trace une seule ligne, du point d'appui au point de relâchement, et le tracé s'arrête là. Les clics tracent toujours une chaîne. Désactivé : un glissement place un point là où il est relâché, comme un clic.",
"prefs.line_hover_tangent_arc": "S'arrêter sur le dernier point d'une ligne pour passer à un arc tangent",
"prefs.line_hover_tangent_arc_note": "Dans l'outil Ligne, une fois un segment tracé, éloigner le pointeur du dernier point de la chaîne, revenir et le maintenir immobile dessus : le clic suivant termine un arc tangent, et le même geste revient aux lignes. Désactivé : s'arrêter là ne fait rien.",
"prefs.linked_designs": "Conceptions liées",
"prefs.live_input": "Saisie en direct",
"prefs.live_preview": "Aperçu en direct",
@@ -2225,6 +2281,8 @@
"prefs.mouse_mapping": "Affectation des boutons de la souris",
"prefs.navigation": "Navigation",
"prefs.navigation_note": "Comment la souris, le pavé tactile et la souris 3D déplacent la vue.",
"prefs.navigator_fit": "Cadrer le modèle après un changement de vue du repère de navigation",
"prefs.navigator_fit_note": "Une face, un coin ou une flèche du cube de vue, ou un axe du gizmo, regarde aussi le milieu du modèle depuis la distance unique qui le cadre sous tous les côtés (sa sphère englobante). Accueil utilise le même zoom et le même centre tant que cette option est activée, de sorte que les vues choisies ensuite ne font que tourner. Désactivé : la vue tourne sur place, en gardant sa cible et son zoom.",
"prefs.network_host_remove": "Retirer",
"prefs.network_locations": "Autoriser les emplacements réseau dans les conceptions",
"prefs.network_locations_note": "Désactivé : les fichiers liés d'une conception situés sur un autre ordinateur (\\\\server\\share) ne sont ouverts que depuis les serveurs listés ici, ou après en avoir autorisé un sur demande.",
@@ -2245,6 +2303,8 @@
"prefs.placement": "Placement",
"prefs.prefer_free_sketch_points": "Privilégier les points d'esquisse déplaçables quand des points se superposent",
"prefs.preview_choices": "Aperçu de vos choix",
"prefs.preview_tint_new_faces": "Teinter la nouvelle surface dans un aperçu",
"prefs.preview_tint_new_faces_help": "L'aperçu d'une fonction qui modifie un corps peut tracer la surface qu'elle ajoute dans la couleur d'aperçu. Désactivée, le corps est tracé tel quel.",
"prefs.print_face_highlight": "Mise en évidence de la face d’impression",
"prefs.print_face_highlight_tip": "Intensité de la teinte et du contour de la face d’impression d’un corps dans la vue. 0 % n’affiche aucune mise en évidence.",
"prefs.profile_active": "Profil actif",
@@ -2273,6 +2333,8 @@
"prefs.render_previous_geometry_budget": "Utiliser l'ancien budget de géométrie de l'aperçu Rendu",
"prefs.render_previous_geometry_budget_help": "Rétablir l'ancienne limite fixe de géométrie (512 Mio sur un ordinateur 64 bits, 128 Mio ailleurs) au lieu du budget basé sur l'appareil.",
"prefs.reports_saved": "Enregistrés : {n}",
"prefs.ribbon_click_ends_tool": "Cliquer sur le bouton de l'outil en cours le termine",
"prefs.ribbon_click_ends_tool_note": "Un clic sur le bouton allumé du ruban de l'outil d'esquisse en cours termine l'outil, et les clics dans la vue sélectionnent de nouveau. Désactivé : le clic relance l'outil.",
"prefs.ribbon_reveal": "Montrer les résultats de recherche dans le ruban",
"prefs.ribbon_reveal_note": "Survoler un résultat dans Rechercher des commandes montre où se trouve la commande dans le ruban : son onglet, son bouton ou sa ligne dans le menu déroulant d'un panneau. Une commande lancée depuis la recherche y clignote, et une commande en cours issue d'un menu déroulant signale son panneau.",
"prefs.right_click": "Clic droit",
@@ -2302,6 +2364,8 @@
"prefs.sketch_exit_view.previous": "Revenir à la vue précédente",
"prefs.sketch_exit_view.unless_rotated": "Revenir, sauf si la vue a été tournée",
"prefs.sketch_exit_view_note": "Où va la vue après Terminer l'esquisse, Annuler ou un double Échap.",
"prefs.sketch_face_activates_part": "Esquisser sur la face d'une pièce active cette pièce",
"prefs.sketch_face_activates_part_note": "Une esquisse commencée sur une face du corps d'un autre composant active d'abord ce composant, de sorte que l'esquisse lui appartient et reste avec cette pièce dans le navigateur. La pièce reste ensuite active. Désactivé : l'esquisse appartient au composant actif.",
"prefs.sketch_open_look_at": "Se placer face à l'esquisse à son ouverture",
"prefs.sketch_open_look_at_note": "Désactivé : créer ou modifier une esquisse laisse la vue telle quelle. Vue normale place la vue face à l'esquisse quand vous le souhaitez.",
"prefs.sketch_recent_snap": "Accrocher automatiquement aux centres et points milieux récents",
@@ -2333,7 +2397,24 @@
"prefs.status_error": "Erreur",
"prefs.status_selected": "Sélectionné",
"prefs.status_warning": "Avertissement",
"prefs.structure.appearance": "Apparence",
"prefs.structure.appearance_note": "Couleurs, texte et accessibilité.",
"prefs.structure.in_profile": "Dans le profil",
"prefs.structure.layout": "Disposition",
"prefs.structure.layout_note": "Où se placent les parties de la fenêtre.",
"prefs.structure.look": "Aspect",
"prefs.structure.measure_pointer": "Mesure et pointeur",
"prefs.structure.mouse3d": "Souris 3D",
"prefs.structure.mouse3d_note": "SpaceMouse et SpaceNavigator.",
"prefs.structure.navigation_note": "Souris et pavé tactile. La souris 3D a sa propre page.",
"prefs.structure.profile": "Profil",
"prefs.structure.profile_edited": "Modifié depuis l'enregistrement",
"prefs.structure.profile_hover": "Enregistré dans les profils de réglages",
"prefs.structure.scale_percent": "{percent} %",
"prefs.structure.sketch": "Esquisse",
"prefs.structure.tools": "Outils",
"prefs.structure.tools_note": "Comportement des outils de modélisation.",
"prefs.structure.work": "Travail",
"prefs.supersampling_brief": "Dessine la vue en plus grand puis la réduit par filtrage : très net, mais plus lourd.",
"prefs.tab_accent_note": "La couleur d'accent suit l'onglet actif",
"prefs.theme_file": "fichier de thème",
@@ -2521,6 +2602,10 @@
"render.base_color": "Couleur de base · sRVB",
"render.base_metallic": "Fraction métallique",
"render.base_opaque_only": "Cet éditeur conserve la sémantique des corps opaques. Utilisez les commandes de verre dédiées pour la transmission optique.",
"render.batch_all_shots": "Toutes les prises de vue ({count})",
"render.batch_empty": "Enregistrer une prise de vue avant de rendre toutes les prises de vue.",
"render.batch_limit": "Le rendu de toutes les prises de vue est limité à 32 prises de vue.",
"render.batch_status": "{running} en cours · {waiting} en attente",
"render.bloom_enable": "Halo lumineux (bloom) à l'affichage",
"render.bloom_help": "Lueur créative appliquée à la sortie seulement, à partir des hautes lumières linéaires positives, étalée par trois passes de flou normalisées. Le rayon est en pixels de sortie. Ajoutée en lumière d'affichage linéaire avant sRVB ; ce n'est ni un effet d'objectif, ni un effet volumétrique, ni une amélioration de la convergence. L'EXR brut est inchangé.",
"render.bloom_opaque": "Le halo lumineux nécessite une sortie opaque. Un PNG transparent est refusé, pas aplati en silence.",
@@ -2631,6 +2716,13 @@
"render.choose_look": "Choisissez d'abord un aspect dans Apparences",
"render.chromatic_offset": "Décalage chromatique des bords",
"render.clear_stage": "Supprimer le décor enregistré",
"render.compare_divider": "Séparateur de comparaison",
"render.compare_live": "En direct",
"render.compare_live_preview": "Aperçu en direct",
"render.compare_render": "Rendu {number}",
"render.compare_side": "Rendu {number} · {shot}",
"render.compare_summary": "{shot} · {width} × {height} · PNG · {seconds} s · {renderer} · capturé à la révision {revision}",
"render.compare_swap": "Inverser",
"render.copy_request": "Copier le JSON",
"render.cpu_aa_help": "Échantillons spatiaux : plus d'échantillons améliorent les bords et allongent le rendu.",
"render.cpu_area_add": "Ajouter une lumière CPU rectangulaire",
@@ -3380,6 +3472,10 @@
"render.result_retained": "Affichage de la dernière image terminée ; un rendu plus récent ne l'a pas remplacée.",
"render.result_sequence": "Point de restauration",
"render.result_title": "Résultat du rendu",
"render.results_choose": "Résultats conservés",
"render.results_label": "Rendu {number} · {shot} · {seconds} s",
"render.results_stale_label": "{result} [obsolète]",
"render.results_unnamed": "Image",
"render.return_design": "Revenir à la conception",
"render.review_stale": "Cet examen est périmé. Résolvez ou vérifiez à nouveau avant d'appliquer.",
"render.ribbon_aging": "Vieillissement",
@@ -3831,6 +3927,7 @@
"sketch_text.formula": "Commencez par = pour composer le texte avec des paramètres : ='L' + number(Largeur)",
"sketch_text.italic": "Italique",
"sketch_text.loading_fonts": "Recherche des polices installées…",
"sketch_text.no_bold": "La police intégrée n'a pas de graisse grasse : choisir une police installée",
"sketch_text.on_path": "Le texte suit sa trajectoire : la courbe le positionne",
"sketch_text.path_clear": "Détacher le texte de sa courbe",
"sketch_text.path_hint": "Une courbe de l'esquisse que le texte suit : cliquer ici, puis sur la courbe",
@@ -4045,11 +4142,13 @@
"ui.agent_chat_command": "Commande de l'agent",
"ui.agent_chat_connect": "Connecter",
"ui.agent_chat_copy": "Copier",
"ui.agent_chat_copy_message": "Copier le message",
"ui.agent_chat_ctx_bodies": "Corps",
"ui.agent_chat_ctx_elements": "Éléments",
"ui.agent_chat_ctx_features": "Fonctions",
"ui.agent_chat_ctx_more": "+{n} de plus",
"ui.agent_chat_ctx_other": "Autre",
"ui.agent_chat_custom_heading": "Modèle personnalisé",
"ui.agent_chat_custom_hint": "Tout programme qui parle l'Agent Client Protocol sur stdin et stdout. Aucun shell ne le lance.",
"ui.agent_chat_denied": "Refusé",
"ui.agent_chat_deny": "Refuser",
@@ -4093,8 +4192,16 @@
"ui.agent_chat_mode_all": "Tout",
"ui.agent_chat_mode_ask": "Demander",
"ui.agent_chat_mode_edits": "Édition",
"ui.agent_chat_model_custom": "Utiliser le modèle personnalisé : {model}",
"ui.agent_chat_model_no_tools": "{model} ne peut pas utiliser les outils de SolidCraft : il peut seulement discuter. Choisissez un modèle qui prend en charge les outils.",
"ui.agent_chat_model_rejected": "L'agent n'a pas accepté {model} : {error}",
"ui.agent_chat_model_search": "Rechercher des modèles…",
"ui.agent_chat_model_tip": "Le modèle utilisé par l'agent. Votre choix est mémorisé pour cet agent.",
"ui.agent_chat_models_failed": "Impossible de lister les modèles : {error}",
"ui.agent_chat_models_heading": "Modèles disponibles",
"ui.agent_chat_models_listing": "Liste des modèles en cours…",
"ui.agent_chat_models_none": "Le point d'accès ne liste aucun modèle",
"ui.agent_chat_models_not_a_list": "La réponse n'était pas une liste de modèles",
"ui.agent_chat_new": "Nouveau chat",
"ui.agent_chat_new_confirm": "Démarrer un nouveau chat ? Le chat actuel sera effacé.",
"ui.agent_chat_next_picture": "Image suivante",
@@ -4230,6 +4337,9 @@
"ui.ai_permissions": "Autorisations",
"ui.ai_preset": "Préréglage",
"ui.ai_protocol": "Protocole",
"ui.ai_reasoning_effort": "Effort de raisonnement",
"ui.ai_reasoning_effort_field_hint": "vide = valeur par défaut du fournisseur",
"ui.ai_reasoning_effort_hint": "Facultatif. Envoyé au point d'accès sous le nom `reasoning_effort` ; les valeurs acceptées varient selon le fournisseur (par exemple `low`, `high` ou `max`). Une valeur saisie ici n'est jamais remplacée par SolidCraft. Laisser vide pour ne rien envoyer et garder la valeur par défaut du fournisseur.",
"ui.ai_remove": "Retirer",
"ui.ai_request_timed_out": "Le modèle a mis plus de {seconds} s. Vous pouvez augmenter le délai d'expiration des requêtes de ce point d'accès dans Préférences › Assistant.",
"ui.ai_request_timeout": "Délai d'expiration des requêtes",
@@ -4365,9 +4475,11 @@
"ui.construction.contact_hint": "Tangente : sélectionnez un cercle/arc ou une spline/conique ouverte, puis le point de contact (courbes libres : extrémités)",
"ui.construction.external_hint": "Tangente : sélectionnez un cercle ou un arc, un point extérieur, puis la solution souhaitée",
"ui.construction.free_hint": "Droite de construction : cliquez sur deux points ; clic droit pour recommencer",
"ui.construction.normal_needs_length": "Une ligne infinie est toujours de construction : saisir une longueur pour tracer une ligne normale",
"ui.construction.point_hint": "Droite de construction : cliquez sur le point de passage",
"ui.construction.reference_hint": "Droite de construction : sélectionnez une ligne droite, puis un point ; clic droit pour changer la référence",
"ui.construction.ribbon_label": "Lignes aux.",
"ui.construction.side_hint": "Ligne de construction : cliquer du côté vers lequel la ligne s'étend ; clic droit pour recommencer",
"ui.context_menu": "Menu contextuel",
"ui.continuity_crease": "Angle vif",
"ui.continuity_curvature": "Courbure",
@@ -4400,6 +4512,7 @@
"ui.dark": "Sombre",
"ui.default_modelling_orientation": "Orientation de modélisation par défaut",
"ui.delete": "Supprimer",
"ui.delete_select_first": "Sélectionner d'abord ce qu'il faut supprimer : fonctions, corps, composants ou géométrie d'esquisse",
"ui.depth": "Profondeur",
"ui.design": "Conception",
"ui.design_properties.author": "Auteur",
@@ -4492,6 +4605,7 @@
"ui.edit_in_place_saved_elsewhere": "La pièce a été enregistrée dans un autre fichier ; l'assemblage reste lié à {name}",
"ui.edit_in_place_wait": "Attendez la fin de ce qui s'exécute dans la conception",
"ui.edit_section_analysis": "Modifier l'analyse de coupe",
"ui.edit_sketch_empty": "Aucune esquisse à modifier",
"ui.ellipse": "Ellipse",
"ui.emboss_wrap_note": "Les profils de lignes orthogonales tangents à un cylindre pris en charge s'enroulent automatiquement. Choisir une paroi pour définir une cible.",
"ui.end_radius": "Rayon final",
@@ -4502,7 +4616,9 @@
"ui.equation_explicit": "Explicite",
"ui.equation_form": "Forme",
"ui.equation_note": "La variable est un simple nombre : sin(t) la prend en radians. Un résultat sans unité est dans l'unité de la conception.",
"ui.equation_note_range": "La variable est une longueur, comme sa plage : la combiner avec des paramètres de longueur, et la diviser par une longueur pour sin(). Un résultat sans unité est dans l'unité de la conception.",
"ui.equation_parametric": "Paramétrique",
"ui.equation_variable_unit": "La variable a l'unité de la plage",
"ui.equation_x_of_t": "x(t)",
"ui.equation_y_of_t": "y(t)",
"ui.equation_y_of_x": "y(x)",
@@ -4634,6 +4750,7 @@
"ui.hideOrigin": "Masquer l'origine",
"ui.hidePlanes": "Masquer les plans",
"ui.hideProfile": "Masquer le profil",
"ui.hide_component_history": "Masquer son historique",
"ui.hide_origin_by_default": "Masquer l'origine par défaut",
"ui.hide_origin_note": "Les nouvelles conceptions démarrent avec le dossier Origine masqué. Une conception garde son propre réglage d'Origine dès que vous l'affichez ou la masquez.",
"ui.history_back": "Reculer d’une étape",
@@ -4641,6 +4758,7 @@
"ui.history_edge_hint": "double-cliquer sur le bord",
"ui.history_first": "Aller au début",
"ui.history_forward": "Avancer d’une étape",
"ui.history_hide_hidden": "Masquer les composants masqués",
"ui.history_hide_names": "Masquer les noms",
"ui.history_last": "Aller à la fin",
"ui.history_marker_tip": "Marqueur d'historique : faire glisser pour ramener le modèle en arrière ; les nouvelles fonctions s'insèrent ici",
@@ -4650,6 +4768,7 @@
"ui.history_play_button_show": "Afficher le bouton Lire",
"ui.history_play_finish_action": "Terminez l'action en cours avant de lire l'historique",
"ui.history_play_roll_back": "Reculez le marqueur d'historique avant de lire",
"ui.history_show_hidden": "Afficher les composants masqués",
"ui.history_show_names": "Afficher les noms",
"ui.history_sketch_listed_under": "Listé sous {feature}",
"ui.history_title": "Historique",
@@ -4678,12 +4797,14 @@
"ui.joints": "Liaisons",
"ui.keyboard_shortcuts": "Raccourcis clavier",
"ui.language": "Langue",
"ui.lasso_select": "Lasso (faire glisser une boucle libre : prend ce qui est entièrement à l'intérieur)",
"ui.layout_compact": "Compacte",
"ui.layout_floating": "Flottante",
"ui.length": "Longueur",
"ui.length_decimals": "Décimales des longueurs",
"ui.light": "Clair",
"ui.line": "Ligne",
"ui.line_arc_hint": "Arc tangent : cliquer sur l'extrémité de l'arc. S'arrêter de nouveau sur le dernier point pour revenir aux lignes ; clic droit ou Échap pour terminer",
"ui.link_copies": "Lier les copies identiques",
"ui.link_copies_keep": "Conserver",
"ui.link_copies_link": "Lier",
@@ -4932,6 +5053,7 @@
"ui.showDimension": "Afficher la cote",
"ui.showOrigin": "Afficher l'origine",
"ui.showPlanes": "Afficher les plans",
"ui.show_component_history": "Afficher son historique",
"ui.show_constraints": "Contraintes",
"ui.show_construction_geometries": "Géométries de construction",
"ui.show_dimensions": "Cotes",
@@ -5048,12 +5170,15 @@
"ui.view_angles": "Angles de vue",
"ui.view_angles_convention": "Degrés autour de l'axe vertical. Lacet 0° = vue de face, 90° = vue de gauche, −90° = vue de droite, 180° = vue arrière ; tangage 90° = vue de dessus ; un roulis positif fait tourner le modèle dans le sens horaire.",
"ui.view_angles_help": "Affiche le tangage, le lacet et le roulis de la vue en degrés, ainsi que sa projection, sous le gizmo d'orientation. Cliquez sur l'indicateur pour saisir des angles exacts.",
"ui.view_angles_lock": "Verrouiller {axis}",
"ui.view_angles_ortho": "Ortho",
"ui.view_angles_persp": "Persp.",
"ui.view_angles_pitch": "Tangage",
"ui.view_angles_readout": "Tangage {pitch} · Lacet {yaw} · Roulis {roll}",
"ui.view_angles_roll": "Roulis",
"ui.view_angles_unlock": "Déverrouiller {axis}",
"ui.view_angles_yaw": "Lacet",
"ui.view_angles_zero": "Mettre {axis} à 0°",
"ui.view_centre": "Centre de la vue",
"ui.viewport_status_clear": "Désactiver {name}",
"ui.viewport_status_clear_hint": "Désactiver {name} · Échap",
@@ -5155,6 +5280,7 @@
"web.open_link": "Ouvrir la conception liée ?",
"web.open_link_confirm": "Il y a des modifications non enregistrées. Ouvrir la conception liée dans un nouvel onglet ?",
"↑ ↓ choose · Enter / Tab complete · Esc dismiss": "↑ ↓ choisir · Entrée / Tab compléter · Échap fermer",
"↑ ↓ choose · Enter applies this value": "↑ ↓ choisir · Entrée applique cette valeur",
"↑ ↓ choose · Enter applies what you typed · Esc dismiss": "↑ ↓ choisir · Entrée applique la saisie · Échap fermer",
"⌘Enter continues on GitHub.": "⌘Entrée continue sur GitHub."
}
+230 -54
View File
@@ -379,7 +379,7 @@
"Move Type": "סוג הזזה",
"Move down": "הזזה למטה",
"Move earlier": "הקדמה",
"Move holes by clicking or dragging": "הזזת חורים בלחיצה או בגרירה",
"Move holes by clicking or dragging": "הזזה בלחיצה או בגרירה",
"Move later": "דחייה",
"Move up": "הזזה למעלה",
"Name": "שם",
@@ -585,7 +585,7 @@
"Show ghost": "הצגת צללית",
"Show in folder": "הצגה בתיקייה",
"Show the pointer an AI agent moves (#35)": "הצגת הסמן שסוכן הבינה המלאכותית מזיז (#35)",
"Show tool at cursor": "הצגת הכלי ליד הסמן",
"Show tool at cursor": "כלי ליד הסמן",
"Signed axes": "צירים עם סימן",
"Size": "גודל",
"Sketch constraints": "אילוצי סקיצה",
@@ -596,7 +596,7 @@
"Sketch points": "נקודות סקיצה",
"Slider": "הזזה",
"Slot width": "רוחב חריץ",
"Snap to face and edge centres": "הצמדה למרכזי פאות ומקצועות",
"Snap to face and edge centres": "הצמדה למרכזים",
"SolidCraft cannot submit reports directly. Submit on GitHub; no token is needed in the app.": "אין אפשרות לשלוח דיווחים ישירות מתוך SolidCraft. יש לשלוח אותם ב-GitHub; אין צורך באסימון גישה באפליקציה.",
"SolidCraft closed before these designs were saved. Their last autosave can be opened.": "SolidCraft נסגרה לפני שתכנים אלה נשמרו. אפשר לפתוח את השמירה האוטומטית האחרונה שלהם.",
"Sort A to Z": "מיון בסדר עולה",
@@ -739,36 +739,36 @@
"a picked edge's body is gone": "הגוף של המקצוע שנבחר כבר לא קיים",
"add a key for a joint first": "יש להוסיף תחילה מפתח למפרק",
"ai.connected_toast": "SolidCraft זמין עכשיו ב־{clients}. יש להפעיל אותם מחדש כדי להשתמש בו (הגדרות > עוזר).",
"ai.prefs.allow_control": "התרת חיבור לסוכני בינה מלאכותית (ערוץ בקרה / MCP)",
"ai.prefs.allow_control_note": "פותח ערוץ בקרה במחשב זה בלבד (127.0.0.1, פורט 27878 או הפנוי הבא). תוכנה שמתחברת צריכה גם להציג אסימון סודי שרק חשבון המשתמש שלך יכול לקרוא, ונוצר מחדש בכל הפעלה. ייכנס לתוקף בהפעלה הבאה של SolidCraft. כבוי: שום דבר אינו מאזין, אלא אם SolidCraft מופעל עם --control.",
"ai.prefs.availability": "פקדי העוזר",
"ai.prefs.background_commands": "הרצת פקודות הסוכן ברקע",
"ai.prefs.connect_apps": "הנגשת SolidCraft ליישומי בינה מלאכותית במחשב זה",
"ai.prefs.connect_apps_note": "בהפעלת SolidCraft הוא מתווסף כשרת MCP ליישומי הבינה המלאכותית שנמצאו במחשב זה (OpenCode, Claude Desktop, Claude Code, Cursor, Codex, Gemini CLI, Copilot CLI, Windsurf, Zed, VS Code), כך שסוכן יכול להשתמש בו בלי שום הגדרה. רק הרשומה solidcraft נוספת או שהנתיב שלה מתעדכן; רשומה ששינית נשארת כפי שהיא, ולפני כן נשמר גיבוי של הקובץ. כבוי: הרשומות שנוספו מוסרות. הפקודה עזרה > חיבור עוזרי בינה מלאכותית… עושה זאת ידנית.",
"ai.prefs.connect_at_start": "התחברות לסוכן האחרון שבו השתמשת בהפעלת SolidCraft",
"ai.prefs.allow_control": "לאפשר לסוכני AI להתחבר",
"ai.prefs.allow_control_note": "ערוץ מקומי (MCP). שום דבר לא יוצא מהמחשב הזה.\n\nפותח ערוץ בקרה במחשב זה בלבד (127.0.0.1, פורט 27878 או הפנוי הבא). תוכנה שמתחברת צריכה גם להציג אסימון סודי שרק חשבון המשתמש שלך יכול לקרוא, ונוצר מחדש בכל הפעלה. ייכנס לתוקף בהפעלה הבאה של SolidCraft. כבוי: שום דבר אינו מאזין, אלא אם SolidCraft מופעל עם --control.",
"ai.prefs.availability": "זמינות",
"ai.prefs.background_commands": "להפעיל פקודות איטיות ברקע",
"ai.prefs.connect_apps": "להוסיף את SolidCraft ליישומי AI",
"ai.prefs.connect_apps_note": "כיבוי מסיר שוב את הרשומות.\n\nבהפעלת SolidCraft הוא מתווסף כשרת MCP ליישומי הבינה המלאכותית שנמצאו במחשב זה (OpenCode, Claude Desktop, Claude Code, Cursor, Codex, Gemini CLI, Copilot CLI, Windsurf, Zed, VS Code), כך שסוכן יכול להשתמש בו בלי שום הגדרה. רק הרשומה solidcraft נוספת או שהנתיב שלה מתעדכן; רשומה ששינית נשארת כפי שהיא, ולפני כן נשמר גיבוי של הקובץ. כבוי: הרשומות שנוספו מוסרות. הפקודה עזרה > חיבור עוזרי בינה מלאכותית… עושה זאת ידנית.",
"ai.prefs.connect_at_start": "לחבר את הסוכן האחרון בהפעלה",
"ai.prefs.connect_at_start_note": "מפעיל ברקע את הסוכן שאליו התחברת לאחרונה (או את סוכן ברירת המחדל) בכל הפעלה של SolidCraft, כך שהוא מוכן לשיחה. לא כש-SolidCraft מופעל עם --control. אם ההתחברות נכשלת, העוזר מציג „ההתחברות נכשלה” ולחצן „ניסיון חוזר”.",
"ai.prefs.follow_camera": "הפניית המצלמה אל סקיצות",
"ai.prefs.follow_camera": "לעקוב אחר המצלמה",
"ai.prefs.follow_camera_off": "מעקב המצלמה האוטומטי כבוי.",
"ai.prefs.follow_camera_on": "המצלמה פונה אל הסקיצות שהסוכן יוצר.",
"ai.prefs.intro": "סוכנים, הרשאות ואופן הצגת העבודה שלהם.",
"ai.prefs.make_default": "שימוש כברירת מחדל",
"ai.prefs.intro": "סוכני AI ב־SolidCraft.",
"ai.prefs.make_default": "לקבוע כברירת מחדל",
"ai.prefs.no_agents": "אין עדיין סוכנים. אפשר להוסיף סוכן או לרענן כדי למצוא סוכנים מותקנים.",
"ai.prefs.no_saved_approvals": "אין אישורים שמורים.",
"ai.prefs.open_at_start": "פתיחת חלונית העוזר בהפעלת SolidCraft",
"ai.prefs.open_at_start": "לפתוח את הצ'אט בהפעלה",
"ai.prefs.open_at_start_note": "מציג את חלונית העוזר (או את החלון הנפרד שלה) בכל הפעלה של SolidCraft. לא כש-SolidCraft מופעל עם --control.",
"ai.prefs.override_all_warning": "הסוכן הזה עשוי לשמור, לדרוס, לייצא או להחליף את התכן בלי לשאול.",
"ai.prefs.permission_all": "התרת הכול",
"ai.prefs.permission_all_summary": "כלי SolidCraft הזמינים פועלים בלי לשאול.",
"ai.prefs.permission_ask": "שאלה לפני שינויים",
"ai.prefs.permission_ask_summary": "אישור כל שינוי בתכן וכל פעולה על קבצים.",
"ai.prefs.permission_edits": "התרת עריכות מודל",
"ai.prefs.permission_edits_summary": "עריכות מודל מתבצעות; פעולות קבצים ופעולות הרסניות עדיין דורשות אישור.",
"ai.prefs.permission_all": "לעשות הכול",
"ai.prefs.permission_all_summary": "אף פעם לא שואל",
"ai.prefs.permission_ask": "לשאול קודם",
"ai.prefs.permission_ask_summary": "שואל על כל שינוי",
"ai.prefs.permission_edits": "לבצע עריכות",
"ai.prefs.permission_edits_summary": "עורך בחופשיות, שואל על השאר",
"ai.prefs.permission_scope": "לכלי SolidCraft בלבד. פקודות המעטפת ועריכות הקבצים של הסוכן כפופות להרשאות שלו.",
"ai.prefs.pointer_preview_hidden": "הסמן מוסתר",
"ai.prefs.pointer_preview_speed": "סמן הסוכן · {speed}",
"ai.prefs.pointer_speed": "מהירות התנועה",
"ai.prefs.pointer_speed": "מהירות",
"ai.prefs.privacy_note": "רק הסוכן או נקודת הקצה שבחרת מקבלים את השיחה. מפתחות נקודות הקצה נשמרים במחזיק המפתחות של המערכת.",
"ai.prefs.show_pointer": "הצגת סמן הסוכן",
"ai.prefs.show_pointer": "להציג את מצביע הסוכן",
"ai.prefs.turn_limit_hint": "מספר מרבי של קריאות לכלים שמשנים את התכן שהעוזר רשאי לבצע להודעה אחת.",
"angle, height": "זווית, גובה",
"appearance.dielectric_ior": "מקדם שבירה דיאלקטרי",
@@ -997,6 +997,11 @@
"cmd.joint_screw": "בורג",
"cmd.joint_solve": "פתרון מפרקים",
"cmd.joint_sweep": "סריקת טווח מפרק",
"cmd.layout.apply": "החלת פריסה",
"cmd.layout.get": "בדיקת פריסה",
"cmd.layout.reset": "איפוס פריסה",
"cmd.layout.save": "שמירת פריסה",
"cmd.layout.workspace.select": "בחירת סביבת עבודה לפריסה",
"cmd.library_apply": "החלת פריט מהספרייה המקומית",
"cmd.library_export": "ייצוא ספריית התכן",
"cmd.library_inspect": "בדיקת הספרייה המקומית",
@@ -1030,6 +1035,21 @@
"cmd.occurrence_move": "הזזת מופע",
"cmd.occurrence_properties": "תכונות פיזיקליות של מופע",
"cmd.occurrence_reparent": "העברה לרכיב",
"cmd.panel.activate": "הפעלת לשונית חלונית",
"cmd.panel.close": "סגירת חלונית",
"cmd.panel.collapse": "כיווץ מקטע חלונית",
"cmd.panel.dismiss": "סגירת תצוגה זמנית של חלונית",
"cmd.panel.float": "הצפת חלונית",
"cmd.panel.focus": "מיקוד חלונית",
"cmd.panel.list": "הצגת רשימת חלוניות",
"cmd.panel.move": "העברת חלונית",
"cmd.panel.native_close": "עיגון מחדש של מקטע חלונית מחלון נפרד",
"cmd.panel.open": "פתיחת חלונית",
"cmd.panel.peek": "הצגה זמנית של חלונית מכווצת",
"cmd.panel.popout": "העברת חלונית לחלון נפרד",
"cmd.panel.reset_pair": "איפוס מפריד חלוניות",
"cmd.panel.resize": "שינוי גודל מקטע חלונית",
"cmd.panel.resize_pair": "הזזת מפריד חלוניות",
"cmd.parameters_add": "הוספת פרמטר משתמש",
"cmd.parameters_change": "שינוי פרמטרים",
"cmd.parameters_comment": "הערה לפרמטר",
@@ -1376,6 +1396,9 @@
"dialog.custom_axis": "↖ ציר מותאם",
"dialog.dimension_insert_hint": "יש ללחוץ על מידה מניעה אחרת כדי להוסיף את שמה",
"dialog.dimension_name": "מידה: {name}",
"dialog.dimension_step_down": "הפחתה ב־{step}",
"dialog.dimension_step_expression": "רק מספר משתנה בצעדים; ביטוי נשאר כפי שנכתב",
"dialog.dimension_step_up": "הוספה של {step}",
"dialog.edit_title": "עריכת {title}",
"dialog.no_shown_body": "אין גוף מוצג לשינוי: הציגו גוף, או בחרו „גוף חדש”",
"dialog.pattern_sketch_not_object": "בחרו גופים או פיצ'רים: סקיצה אינה אף אחד מהם. חזרו על העקומות שלה בעזרת המערך או השיקוף של הסקיצה עצמה, או בצעו לה שליפה קודם.",
@@ -1383,6 +1406,7 @@
"dialog.selected_count": "↖ {count} נבחרו",
"dialog.selection_constraint": "אילוץ {id}",
"dialog.selection_external_curve": "{sketch} · עקומה {id}",
"dialog.selection_external_point": "{point} · {sketch}",
"dialog.selection_guidance": "יש לבחור קלט כדי לשנות אותו. לחיצה על אובייקט שנבחר מסירה אותו; גם Ctrl/Cmd+לחיצה מחליפה את מצבו. לאחר בדיקת התצוגה המקדימה יש ללחוץ על אישור.",
"dialog.selection_midpoint": "נקודת אמצע · {id}",
"dialog.selection_profile": "{sketch} · פרופיל {number}",
@@ -1400,6 +1424,7 @@
"document.read_only_save": "התכן הזה נפתח לקריאה בלבד: יש לשמור אותו כקובץ חדש",
"document.save_changes": "לשמור את השינויים ב־{name}?",
"document.save_copy": "שמירת עותק…",
"document.tab_unsaved_changes": "שינויים שלא נשמרו",
"document.unsaved_changes": "ב־{name} יש שינויים שלא נשמרו. ללא שמירה, הם יאבדו.",
"drive.rigid": "{joint} הוא מפרק קשיח: הוא מחזיק את החלקים שלו במקומם, ולכן אין מה להניע. כדי שהחלקים יזוזו, שנו את הסוג שלו ב״עריכת מפרק״: Revolute לסיבוב, Slider להחלקה, Cylindrical לשניהם.",
"drive.stopped_by_contact": "נעצר ב־{value}: {a} היה עובר דרך {b}",
@@ -1536,6 +1561,11 @@
"help.joint.reach": "הזזת נקודה על חלק לעבר יעד על ידי שינוי המפרקים בשרשרת שלה, בתוך גבולותיהם. כך קובעים תנוחה של מנגנון לפי המיקום הרצוי של קצהו, כגון קצה זרוע רובוט.",
"help.joint.rigid_group": "נעילת החלקים שנבחרו יחד כפי שהם, כך שינועו כיחידה אחת. מתאים לחלקים השייכים יחד ואינם נעים זה ביחס לזה.",
"help.joint.screw": "קישור סיבוב והחלקה לפי פסיעה, כך שכל סיבוב מקדם באותו מרחק. מתאים לבורגי הנעה, לברגים ולכושים בעלי תבריג.",
"help.layout.apply": "החלת פריסה שמורה על סביבת העבודה הנוכחית. העורכים הקיימים שומרים על הטיוטות שלהם.",
"help.layout.get": "קריאת הפריסה שאושרה והצגת סביבות העבודה הרשומות.",
"help.layout.reset": "שחזור פריסת ברירת המחדל הרשומה של סביבת העבודה הנוכחית. הגדרות המשתמש השמורות נשארות זמינות.",
"help.layout.save": "שמירת הפריסה המלאה בשם. החלפת הגדרה שמורה של משתמש מחייבת הרשאה מפורשת לדריסה.",
"help.layout.workspace.select": "בחירת סביבת עבודה רשומה לפריסה ושמירת הפריסה שממנה יוצאים.",
"help.library.apply": "החלת חומר או מראה מקובץ ספרייה מקומי על גופים, פאות או רכיבים. מתאים לשימוש חוזר בחומרים ששמרת מתכן אחר.",
"help.library.export": "שמירת החומרים והמראות של התכן בקובץ ספרייה. מתאים לשיתוף או לשימוש חוזר בתכן אחר.",
"help.library.inspect": "בדיקת קובץ ספרייה והצגת החומרים והמראות שבו, בלי לשנות את התכן. מתאים כדי לראות מה הספרייה מכילה לפני החלתה.",
@@ -1549,6 +1579,21 @@
"help.notice": "NOTICE",
"help.occurrence.ground": "עיגון חלק במקומו או שחרורו מחדש, כך שהמפרקים מניעים את החלקים האחרים סביבו. מתאים למסגרת או לבסיס שעליהם נבנית שאר ההרכבה.",
"help.occurrence.move": "מזיז חלק בהרכבה רק בכיוונים שהמפרקים שלו מאפשרים. בחרו פאה של החלק, ואז גררו חץ או קשת זמינים.",
"help.panel.activate": "בחירת לשונית חלונית פתוחה בלי לבקש מיקוד מקלדת.",
"help.panel.close": "סגירת חלונית דרך בדיקת מחזור החיים של התוכן שלה. המיקום שלה נשמר.",
"help.panel.collapse": "כיווץ או הרחבה של מקטע חלונית תוך שמירת המידות המבוקשות.",
"help.panel.dismiss": "סגירת התצוגה הזמנית של חלונית מכווצת.",
"help.panel.float": "העברת חלונית למקטע צף שמיקומו מוגדר במפורש.",
"help.panel.focus": "מיקוד חלונית, פתיחה מחדש של מצבה השמור והרחבת המקטע שלה לפי הצורך.",
"help.panel.list": "קריאת סוגי החלוניות הרשומים, המופעים שאושרו, הלשוניות הפעילות, הזמינות ויכולות המארח.",
"help.panel.move": "העברת חלונית למקטע חדש או צירופה לקבוצת לשוניות קיימת.",
"help.panel.native_close": "החזרת מקטע חלונית מחלון נפרד למיקום העיגון השמור בלי לבטל את התוכן שלו.",
"help.panel.open": "פתיחת סוג חלונית עם מזהה מופע מפורש. מופע חדש דורש מיקום.",
"help.panel.peek": "הצגה זמנית של חלונית מכווצת בלי לשנות את הגודל השמור או את מצב הכיווץ שלה.",
"help.panel.popout": "העברת חלונית לחלון נפרד כאשר יש תמיכה בכך. במארח דפדפן נעשה שימוש במקטע צף.",
"help.panel.reset_pair": "שחזור יחס הגדלים המוגדר כברירת מחדל לשני מקטעי עיגון סמוכים, במסגרת הגבולות המשותפים שלהם.",
"help.panel.resize": "הגדרת הרוחב והגובה החיצוניים של מקטע חלונית במסגרת המגבלות של כל הלשוניות.",
"help.panel.resize_pair": "שינוי גודלם של מקטעי עיגון סמוכים יחד תוך שמירה על האורך הכולל המשותף שלהם.",
"help.parameters.change": "פתיחת הטבלה של ערכים בעלי שם וביטויים שקובעים את מידות התכנון. כך משנים מידה במקום אחד וכל הפיצ'רים שמשתמשים בה מתעדכנים.",
"help.parameters.export": "שמירת הפרמטרים של התכן כ־CSV או JSON. מתאים לעריכתם בגיליון אלקטרוני או להעברתם לתכן אחר.",
"help.parameters.graph": "הצגת אילו פרמטרים ופיצ'רים משתמשים באילו אחרים, ואילו לולאות או שגיאות יש ביניהם. מתאים כדי לראות על מה ישפיע שינוי בפרמטר.",
@@ -1885,21 +1930,21 @@
"joint.rigid_group_select_components": "יש לבחור שני רכיבים או יותר.",
"joint_move.blocked_hint": "בדקו את סוג המפרק (למשל מפרק קשיח), את קיבוע החלק, את מגבלות המפרק ואת הערכים הנשלטים על ידי פרמטרים.",
"joint_move.blocked_title": "אי אפשר להזיז את החלק הזה",
"joint_move.coarse": "צעד סיבוב קרוב לציר (°)",
"joint_move.coarse": "צעד בקרבת הציר",
"joint_move.conflict": "אחד המפרקים של החלק הזה מתנגש עם האחרים: {joint}.",
"joint_move.conflict_hint": "ערכו, השביתו או מחקו את המפרק המתנגש כדי שהמפרקים האחרים יוכלו למקם את החלק.",
"joint_move.distance": "מרחק מנקודת ההתחלה",
"joint_move.done": "סיום",
"joint_move.fine": "צעד סיבוב רחוק מהציר (°)",
"joint_move.fine": "צעד הרחק מהציר",
"joint_move.fixed": "לחלק הזה אין ציר מפרק שניתן להזיז.",
"joint_move.help": "הזזה — גררו חץ או קשת. Esc מבטל את הגרירה הנוכחית.",
"joint_move.legacy_hint": "כבו כדי להשתמש בחלונית ההזזה הקודמת.",
"joint_move.precision": "משכו אל מחוץ למנחה המקווקו כדי לכוון את הסיבוב בעדינות.",
"joint_move.legacy_hint": "כבוי: חלונית ההזזה הקודמת.",
"joint_move.precision": "לצעד העדין יש למשוך מחוץ לקו העזר המקווקו.",
"joint_move.rotation": "סיבוב מנקודת ההתחלה",
"joint_move.select": "בחרו פאה או חלק אחד בהרכבה, ואז בחרו הזזה.",
"joint_move.settings": "הזזה בהרכבה",
"joint_move.step": "צעד",
"joint_move.use_handles": "שימוש בידיות תנועת מפרקים להזזה בהרכבה",
"joint_move.use_handles": "שימוש בידיות תנועת מפרקים להזזה",
"keymap.command_id": "מזהה פקודה",
"keymap.empty": "אין פקודות תואמות",
"keymap.use_anyway": "שימוש ב־{key} כאן בכל זאת",
@@ -1959,6 +2004,7 @@
"material.selection.mixed": "חומרים שונים",
"measure.at": "מיקום",
"measure.item": "פריט {number}",
"measure.line_to_centre": "מהקו למרכז",
"measure.quick_copied": "המדידה הועתקה",
"measure.quick_copy": "לחיצה מעתיקה את המדידה",
"measure.quick_edges": "{count} מקצועות",
@@ -2017,6 +2063,12 @@
"navbar.assistant": "עוזר",
"navbar.custom.add_slot": "הוספת {slot}",
"navbar.custom.always": "תמיד מוצג",
"navbar.custom.anchor.bottom_centre": "למטה במרכז",
"navbar.custom.anchor.bottom_left": "למטה משמאל",
"navbar.custom.anchor.bottom_right": "למטה מימין",
"navbar.custom.anchor.top_centre": "למעלה במרכז",
"navbar.custom.anchor.top_left": "למעלה משמאל",
"navbar.custom.anchor.top_right": "למעלה מימין",
"navbar.custom.available": "{available} · גררו אל הסרגל או לחצו על הוספה",
"navbar.custom.buttons": "לחצנים",
"navbar.custom.compact": "דחוס",
@@ -2053,6 +2105,7 @@
"navbar.custom.reset_default": "איפוס לברירת מחדל",
"navbar.custom.right": "הזזה ימינה",
"navbar.custom.select": "לא נבחרה משבצת. בחרו משבצת בסרגל כדי להזיז אותה, להסיר אותה או לשנות את המראה שלה.",
"navbar.custom.separator_hint": "מפריד בין קבוצות כפתורים. גררו אותו לסרגל או לחצו על הוספה; אפשר להשתמש בכמה מפרידים שתרצו.",
"navbar.custom.separators_full": "בסרגל כבר יש ארבעה מפרידים.",
"navbar.custom.show": "הצגת סרגל הניווט",
"navbar.custom.shows": "מציג",
@@ -2206,14 +2259,30 @@
"prefs.appearance.viewport_logo": "חלון התצוגה והלוגו",
"prefs.applies_on_restart": "ייכנס לתוקף בהפעלה מחדש",
"prefs.assistant": "עוזר",
"prefs.assistant_connecting": "חיבור",
"prefs.assistant_follow_help": "פעולות Look At בסקיצות מזיזות את התצוגה עם המצביע.",
"prefs.assistant_limit": "מגבלה",
"prefs.assistant_limited": "מוגבל",
"prefs.assistant_pointer_speed": "מהירות המצביע",
"prefs.assistant_seconds": "שניות",
"prefs.assistant_seconds_aria": "שניות",
"prefs.assistant_use_care": "להשתמש בזהירות",
"prefs.auto_operation": "בחירת הפעולה באופן אוטומטי",
"prefs.auto_operation_note": "חלונות הפיצ'רים בוחרים „איחוד”, „גוף חדש” או „הסרה” לפי מה שהחומר החדש נוגע בו, עד שתבחרו פעולה בעצמכם. כבוי: הם שומרים על הפעולה שאיתה נפתחו.",
"prefs.auto_operation_note": "איחוד, גוף חדש או חיתוך, לפי מה שהחומר נוגע בו.",
"prefs.axes": "צירים",
"prefs.axis": "ציר",
"prefs.blur_cost": "עלות עיבוד יחסית. יותר פסים מציינים בדרך כלל יותר עבודה.",
"prefs.blur_device": "לא זמין בהתקן הגרפי או בנתיב העיבוד הזה",
"prefs.blur_guard": "איכות הטשטוש הופחתה כדי לשמור על תגובתיות הפריימים. הרדיוס לא השתנה.",
"prefs.blur_off": "כבוי",
"prefs.blur_reduce_transparency": "מושבת עקב הגדרת הפחתת השקיפות של המערכת",
"prefs.blur_software": "לא זמין עם עיבוד גרפי בתוכנה (gpu-software)",
"prefs.blur_source": "לא זמין בתצוגת סביבת העבודה הזו",
"prefs.blur_web": "לא זמין בנתיב העיבוד הזה בדפדפן",
"prefs.bodies_folders": "תיקיות גופים",
"prefs.bodies_folders_always": "תמיד",
"prefs.bodies_folders_never": "אף פעם",
"prefs.bodies_folders_several": "רק כשיש כמה גופים",
"prefs.bodies_folders_several": "כמה",
"prefs.bottom": "למטה",
"prefs.bottom_centre": "למטה במרכז",
"prefs.bottom_right": "למטה מימין",
@@ -2249,14 +2318,32 @@
"prefs.dark_parts_shading_help": "הצללת התצוגה באור ליניארי עם תאורת שמיים רכה, כך שפאות כהות שומרות על מעבר גוונים במקום להפוך לשחורות",
"prefs.dead_zone_note": "הפס המקווקו הוא האזור המת: תנועה בתוכו לא נלקחת בחשבון.",
"prefs.default": "ברירת מחדל",
"prefs.design": "ברירות מחדל לתכן",
"prefs.design_note": "ההגדרות שתכנים חדשים מתחילים איתן. תכנים פתוחים שומרים על ההגדרות שלהם.",
"prefs.design": "תכנון",
"prefs.design_note": "במה מתחילים תכנונים חדשים.",
"prefs.design_tools.ask": "שאלה",
"prefs.design_tools.degrees": "מעלות",
"prefs.design_tools.double_click_help": "או לעריכת רכיב מקושר במקומו.",
"prefs.design_tools.ghost": "הצגת המיקום הקודם של חלקים שהוזזו כרפאים",
"prefs.design_tools.hole_moves_help": "בחירה חוזרת בפאה מזיזה את החור במקום להסירו.",
"prefs.design_tools.hole_snaps_help": "אמצע הפאה ומרכזי הקצוות העגולים שלה.",
"prefs.design_tools.imported_files": "קבצים מיובאים",
"prefs.design_tools.note": "הערה",
"prefs.design_tools.off": "כבוי",
"prefs.design_tools.placement_group": "משען, בליטה, חיבור נצמד",
"prefs.design_tools.rows": "שורות",
"prefs.devices": "עכבר תלת־ממדי",
"prefs.dialog_buttons": "לחצני תיבות דו־שיח",
"prefs.dialogs": "תיבות דו־שיח",
"prefs.dialogs_edge": "בקצה התצוגה",
"prefs.dialogs_left_column": "בעמודה השמאלית",
"prefs.dialogs_note": "היכן נפתחות תיבות הדו־שיח של הפקודות: מעוגנות בקצה התצוגה, או בעמודה השמאלית במקום הדפדפן וההיסטוריה, שחוזרים כפי שהיו כשהתיבה נסגרת.",
"prefs.dimension_step_angle": "מה שהחצים בחלון, למעלה/למטה והגלגלת מוסיפים לזווית",
"prefs.dimension_step_length": "מה שהחצים בחלון, למעלה/למטה והגלגלת מוסיפים לאורך",
"prefs.dimension_steps": "צעדי החלון",
"prefs.dimension_values": "ערכי מידות",
"prefs.dimension_values_note": "היכן מקלידים את ערך המידה. לחלון יש חצים שמשנים אותו בצעדים; כלי המידה מציב קודם את הטקסט.",
"prefs.dimension_values_view": "בתצוגה",
"prefs.dimension_values_window": "בחלון",
"prefs.display": "תצוגה",
"prefs.edges": "מקצועות והצללה",
"prefs.extend": "הרחבה",
@@ -2292,6 +2379,23 @@
"prefs.graphics_card_restart": "ייכנס לתוקף אחרי הפעלה מחדש של SolidCraft. WGPU_ADAPTER_NAME גובר על בחירה זו.",
"prefs.graphics_card_unused": "כרטיס המסך השמור לא שימש לחלון זה; הכרטיס שבשימוש מוצג בעזרה › אודות ובשורת המצב. נסו אוטומטי, או הגדירו את WGPU_ADAPTER_NAME לחלק ייחודי משם הכרטיס.",
"prefs.graphics_note": "איזון בין מהירות לחלקות. כל בחירה מציגה את השפעתה על דוגמה.",
"prefs.graphics_page.current_view": "התצוגה הנוכחית",
"prefs.graphics_page.fastest": "המהיר ביותר",
"prefs.graphics_page.fold_items": "קיפול פריטים",
"prefs.graphics_page.in_view": "בתצוגה",
"prefs.graphics_page.live_details": "פרטים בזמן אמת",
"prefs.graphics_page.own_mix": "התמהיל שלך",
"prefs.graphics_page.quick_measure": "מדידה מהירה",
"prefs.graphics_page.reference_geometry_through_bodies": "גאומטריית ייחוס דרך גופים",
"prefs.graphics_page.restart": "נדרשת הפעלה מחדש",
"prefs.graphics_page.sharpest": "החד ביותר",
"prefs.graphics_page.single_surface_analysis": "ניתוח משטח יחיד",
"prefs.graphics_page.status_chips": "תגיות מצב",
"prefs.graphics_page.subtitle": "איזון בין מהירות לחלקות.",
"prefs.graphics_page.supersampling": "דגימת־על",
"prefs.graphics_page.supersampling_adaptive": "רק כל עוד התצוגה נייחת",
"prefs.graphics_page.surfaces_edges": "משטחים וקצוות",
"prefs.graphics_page.view_bar": "גם בסרגל התצוגה",
"prefs.graphics_presets_note": "הגדרות קבועות מראש קובעות יחד את הבחירות שלמטה.",
"prefs.hide_constraint_glyphs_zoomed_out": "הסתרת סמלי אילוצים בהקטנה",
"prefs.hide_constraint_glyphs_zoomed_out_note": "סמלי האילוצים נעלמים כשהשרטוט מוקטן מספיק כדי שלא יעמיסו על התצוגה. כבוי: הם נשארים בגודל מסך קבוע בכל זום.",
@@ -2300,7 +2404,7 @@
"prefs.history_origin_bodies_note": "ההיסטוריה משמאל או מתחת לדפדפן מתחילה בתיקיית הגופים של הרכיב הפעיל ובראשית, עם סמלי העין והתפריטים כמו בדפדפן. ההיסטוריה למטה לא משתנה.",
"prefs.history_play_button": "כפתור הפעלת ציר הזמן בפקדי ציר הזמן",
"prefs.history_play_button_note": "מוסיף את הפעלת ציר הזמן בין צעד אחד אחורה לצעד אחד קדימה בפקדי ציר הזמן. אפשר גם ללחוץ לחיצה ימנית על הפקדים כדי להציג או להסתיר אותו.",
"prefs.history_sketches": "סקיצות",
"prefs.history_sketches": "סקיצות בציר הזמן",
"prefs.history_sketches_in_order": "לפי הסדר",
"prefs.history_sketches_note": "כשהסקיצות מוגדרות ל„מתחת לפיצ'ר שלהן”, ציר הזמן משמאל מציג כל סקיצה רמה אחת פנימה, מתחת לפיצ'ר הראשון שנבנה ממנה, בכל פעם שהוא רחב מספיק להצגת שמות. ציר הזמן בתחתית שומר תמיד על סדר היצירה.",
"prefs.history_sketches_under_feature": "מתחת לפיצ'ר שלהן",
@@ -2325,6 +2429,22 @@
"prefs.language_system": "מערכת",
"prefs.layout": "פריסה",
"prefs.layout_note": "הפריסה הדחוסה מצמידה חלוניות לשולי החלון. שקיפות עץ התכן זמינה בפריסה הצפה. את צד העוזר אפשר לשנות בעמוד העוזר.",
"prefs.layout_page.active_commands": "הדגשת פקודות פעילות",
"prefs.layout_page.always": "תמיד",
"prefs.layout_page.card": "כרטיס",
"prefs.layout_page.column": "עמודה",
"prefs.layout_page.hover": "מעבר עכבר",
"prefs.layout_page.icons_labels": "סמלים + תוויות",
"prefs.layout_page.ok_cancel": "אישור וביטול",
"prefs.layout_page.on_hover": "בעת מעבר עכבר",
"prefs.layout_page.open": "פתיחה",
"prefs.layout_page.origin_bodies": "הראשית והגופים למעלה",
"prefs.layout_page.panels": "חלוניות",
"prefs.layout_page.pinned_menus": "השמטת פקודות מוצמדות מהתפריטים",
"prefs.layout_page.play_history": "כפתור הפעלת ציר הזמן",
"prefs.layout_page.slot_count": "{count} מתוך {max} משבצות",
"prefs.layout_page.view_corner": "גם בפינת התצוגה",
"prefs.layout_page.window": "חלון",
"prefs.left": "שמאל",
"prefs.left_click": "לחיצה שמאלית",
"prefs.line_drag_draws_one": "לחיצה וגרירה משרטטות קו אחד",
@@ -2353,6 +2473,13 @@
"prefs.mouse_heading": "עכבר",
"prefs.mouse_mapping": "מיפוי העכבר",
"prefs.navigation": "ניווט",
"prefs.navigation.axes_signed": "צירים",
"prefs.navigation.cube_compact": "קובייה",
"prefs.navigation.device": "התקן",
"prefs.navigation.free": "חופשי",
"prefs.navigation.orbit_style": "סגנון סיבוב",
"prefs.navigation.radial_help": "יש להחזיק או להניע במהירות את הלחצן הימני להצגת טבעת כלים.",
"prefs.navigation.turntable": "שולחן מסתובב",
"prefs.navigation_note": "איך העכבר, משטח המגע והעכבר התלת־ממדי מזיזים את המבט.",
"prefs.navigator_fit": "התאמת המודל לאחר שינוי תצוגה בניווט התצוגה",
"prefs.navigator_fit_note": "לחיצה על פאה, פינה או חץ בקובייה, או על ציר בגיזמו, גם מביטה אל מרכז המודל מהמרחק האחד שמכיל אותו מכל צד (הכדור החוסם שלו). תצוגת בית משתמשת באותו זום ובאותו מרכז כל עוד זה פעיל, כך שתצוגות שנבחרות אחריה רק מסתובבות. כבוי: התצוגה מסתובבת במקומה ושומרת על היעד ועל הזום.",
@@ -2369,11 +2496,15 @@
"prefs.origin_plane_names_note": "השמות המוצגים בדפדפן, בתיבות הדו־שיח וברשימות. {top} הוא XY, {front} הוא XZ ו־{right} הוא YZ, כש־Z למעלה. פקודות, סקריפטים ותכנונים שמורים שומרים על XY, XZ ו־YZ.",
"prefs.overall_speed": "מהירות כללית",
"prefs.parameter_list_rows": "שורות ברשימת הפרמטרים",
"prefs.parameter_list_rows_tip": "חץ למטה בשדה ערך ריק מציג את הפרמטרים, המועדפים תחילה, עד מספר זה. מספר קטן יותר שומר על רשימה קלה בתכנון גדול.",
"prefs.parameter_list_rows_tip": "מספר הפרמטרים המרבי שמקש חץ למטה מציג בתיבת ביטוי ריקה.",
"prefs.persistent_ribbon_pressed": "הדגשת פקודות פעילות ברצועת הכלים",
"prefs.persistent_ribbon_pressed_help": "הדגשת פקודות ברצועת הכלים כל עוד ההשפעות שלהן פעילות. כבו אפשרות זו כדי לשמור על המראה הניטרלי הקודם ועל עזרת הפקודות.",
"prefs.perspective_fov": "שדה ראייה",
"prefs.placement": "מיקום",
"prefs.plugins_enabled": "מופעל",
"prefs.plugins_installed": "מותקנים",
"prefs.plugins_intro": "תוספים מבודדים.",
"prefs.plugins_show_folder": "להציג את תיקיית התוספים",
"prefs.prefer_free_sketch_points": "העדפת נקודות סקיצה ניתנות להזזה כשנקודות חופפות",
"prefs.preview_choices": "תצוגה מקדימה של הבחירות שלך",
"prefs.preview_tint_new_faces": "צביעת משטח חדש בתצוגה מקדימה",
@@ -2431,8 +2562,43 @@
"prefs.shift_add_ctrl_toggle": "Shift מוסיף בלבד; Ctrl / Cmd מחליף מצב",
"prefs.shift_add_note": "כשפועל: Shift מוסיף, Ctrl / Cmd מחליף מצב. כשכבוי (ברירת מחדל): שניהם מחליפים מצב.",
"prefs.shortcuts": "קיצורים",
"prefs.shortcuts_import": "ייבוא",
"prefs.shortcuts_intro": "מקשים לכל פקודה.",
"prefs.shortcuts_not_assigned": "לא הוקצה",
"prefs.shortcuts_press": "יש ללחוץ על מקש...",
"prefs.single_surface_analysis": "שימוש בניתוח משטח יחיד",
"prefs.single_surface_analysis_help": "כשהאפשרות כבויה, הצללת המשטח וניתוח הרציפות יכולים להישאר פעילים יחד. הפעלתה משאירה רק את הניתוח האחרון שהופעל או שונה.",
"prefs.sketch.align_points": "יישור עם נקודות אחרות בסקיצה",
"prefs.sketch.align_points_note": "קווים בשרטוט מיושרים עם נקודות בשני הצירים בו־זמנית.",
"prefs.sketch.auto_dimensions": "הוספת כל המידות לצורות חדשות",
"prefs.sketch.auto_dimensions_note": "ערכים מוקלדים וכל מידה שמקבעת את הצורה.",
"prefs.sketch.auto_locate": "מיקום ביחס לראשית הסקיצה",
"prefs.sketch.auto_locate_note": "עם כל המידות.",
"prefs.sketch.center_arc": "קשת מנקודת מרכז עוקבת אחר הסמן",
"prefs.sketch.click_selects": "לחיצה בוחרת",
"prefs.sketch.conflict_colour": "צבע התנגשות",
"prefs.sketch.drawing": "שרטוט",
"prefs.sketch.exit_keep": "שמירה",
"prefs.sketch.exit_previous": "חזרה לתצוגה הקודמת",
"prefs.sketch.exit_unless_rotated": "אלא אם סובבה",
"prefs.sketch.faces_edges": "פאות וקצוות",
"prefs.sketch.hide_glyphs": "הסתרת סמלים בהתרחקות",
"prefs.sketch.marks": "סימונים בסקיצה",
"prefs.sketch.note": "שרטוט, הצמדה ובחירה.",
"prefs.sketch.opening_closing": "פתיחה וסגירה",
"prefs.sketch.prefer_free_points": "העדפת נקודות ניתנות להזזה כשהן חופפות",
"prefs.sketch.project_options": "בקשת אפשרויות ההיטל תחילה",
"prefs.sketch.recent_snap": "הצמדה למפגש שני קווי עזר",
"prefs.sketch.select_other_hidden": "בחירת אחר כוללת גאומטריה מוסתרת",
"prefs.sketch.shortcut_cycles_modes": "לחיצה חוזרת על קיצור מחליפה מצב",
"prefs.sketch.shortcut_cycles_modes_tip": "כבוי: הכלי מתחיל מחדש.",
"prefs.sketch.snapping_guides": "הצמדה וקווי עזר",
"prefs.sketch.state_icons": "סמלי מצב",
"prefs.sketch.tracking_guides": "קווי עזר למרכזים ולנקודות אמצע",
"prefs.sketch.tracking_guides_note": "קווי עזר אופקיים ואנכיים זמניים ממרכזים ומנקודות אמצע של קצוות בעת ריחוף.",
"prefs.sketch.tracking_points": "קווי עזר גם מנקודות הסקיצה",
"prefs.sketch.unless_turned_note": "חזרה אם לא סובבה",
"prefs.sketch.whole_bodies": "גופים שלמים",
"prefs.sketch_align_points": "יישור לנקודות סקיצה אחרות בזמן השרטוט",
"prefs.sketch_align_points_note": "בזמן השרטוט, נקודה מתיישרת אופקית עם נקודת סקיצה אחת ואנכית עם אחרת בו-זמנית, עם קו עזר מנוקד, והלחיצה שומרת את שני היישורים. כבוי: רק עם הנקודה הקודמת, ציר אחד בכל פעם.",
"prefs.sketch_conflict_colour": "גאומטריה סותרת בצבע הסתירה",
@@ -2465,11 +2631,11 @@
"prefs.source": "מקור",
"prefs.speed": "מהירות",
"prefs.stand_up_note": "אחרי שקובץ STEP או IGES נפתח כש־Z למעלה, להציע רבע סיבוב סביב X.",
"prefs.stand_up_unknown": "לקבצים מתוכנות לא מוכרות",
"prefs.stand_up_unknown": "קבצים מתוכנות לא מוכרות",
"prefs.stand_up_unknown.ask": "לשאול בתיבת דו־שיח",
"prefs.stand_up_unknown.note": "להציג הערה",
"prefs.stand_up_unknown.off": "לא להציע",
"prefs.stand_up_unknown_note": "איך ההצעה מוצגת כשכותרת הקובץ לא מציינת תוכנה שידוע שהיא שומרת עם Y למעלה. הערה מוצגת רק כשנראה שהקובץ שוכב על גבו; ״לשאול בתיבת דו־שיח״ שואל על כל קובץ כזה, כמו קודם.",
"prefs.stand_up_unknown_note": "כיצד מוצעת האפשרות כשהקובץ אינו מציין תוכנה מוכרת.",
"prefs.status_bar": "שורת מצב",
"prefs.status_bar_note": "פס דק לאורך הקצה התחתון של החלון מציג מה נערך, את מצב הסקיצה הפתוחה בצבע שלו ואת יחידות התכנון; לחיצה על היחידות משנה אותן. מצב הסקיצה לא מוצג אז עוד בפינת התצוגה.",
"prefs.status_done": "הושלם",
@@ -2505,6 +2671,10 @@
"prefs.theme_follow_off": "כבוי",
"prefs.theme_follow_stop": "הפסקת המעקב",
"prefs.theme_import_hint": "לחצו כדי לבחור קובץ ערכת נושא, שחררו קובץ כאן, או הקישו Ctrl+V ‏(Cmd+V ב-Mac) אחרי שהעתקתם ערכת נושא",
"prefs.theme_notice_dismiss": "הסתרה",
"prefs.theme_option_blur": "טשטוש מאחורי לוחות",
"prefs.theme_option_blur_help": "טשטוש מאחורי לוחות שקופים למחצה, בפיקסלים פיזיים. אפס מכבה את הטשטוש.",
"prefs.theme_option_invalid": "הערך השמור עבור {option} אינו תקין; נעשה שימוש בברירת המחדל של ערכת הנושא. הערך השמור נשמר ללא שינוי.",
"prefs.theme_option_opacity": "אטימות",
"prefs.theme_option_opacity_help": "כוונון אטימות הלוחות הצפים. רמת אטימות מינימלית שומרת על קריאות הטקסט על רקע צבעי חלון התצוגה.",
"prefs.theme_option_tab_colours": "צבעי כרטיסיות",
@@ -2513,6 +2683,7 @@
"prefs.theme_option_tab_fade_ms_help": "שנה בהדרגה את צבעי הלוח ואזור התצוגה בעת מעבר בין כרטיסיות. ערך אפס מבצע את המעבר מיד.",
"prefs.theme_options_badge": "אפשרויות",
"prefs.theme_options_more": "עוד · {count}",
"prefs.theme_options_newer": "ערכת נושא זו דורשת גרסה חדשה יותר של SolidCraft. לאחר השדרוג יש לייבא שוב את ערכת הנושא המקורית כדי לשחזר את הגדרות האפשרויות שלה.",
"prefs.theme_options_reset": "איפוס",
"prefs.theme_options_unavailable": "לא זמין במכשיר זה",
"prefs.theme_paste": "הדבקת ערכת נושא",
@@ -2542,8 +2713,8 @@
"prefs.validation.label": "הבדיקה המלאה של הקרנל",
"prefs.validation.off": "כבוי (ניסיוני)",
"prefs.validation_note": "אחרי כל שלב: בודק כל סחיפה, פעולה בוליאנית ועיגול.\nפעם אחת לכל תכונה: בודק את התוצאה של כל תכונה פעם אחת.\nכבוי (ניסיוני): אין בדיקה.",
"prefs.value_boxes_in_view": "הצגת ערכים ליד הידיות שלהם בתצוגה",
"prefs.value_boxes_in_view_tip": "ערך הפקודה (מרחק, רדיוס…) בתיבה ליד הידית שלו, מעל המודל. כבוי: מקלידים אותו בחלון הפקודה, שמקבל את המקלדת אחרי בחירה, ושום דבר לא מסתיר את המודל.",
"prefs.value_boxes_in_view": "הצגת ערכים לצד הידיות שלהם",
"prefs.value_boxes_in_view_tip": "מרחק ההבלטה, רדיוס העיגול... בתיבה בתצוגה.",
"prefs.view": "התנהגות המבט",
"prefs.view_confirm_corner": "אישור וביטול בפינת התצוגה",
"prefs.view_confirm_corner_note": "כשתיבת דו־שיח או סקיצה פתוחות, סימן וי וסימן איקס בפינה הימנית העליונה של התצוגה עושים את מה שעושים אישור וביטול של תיבת הדו־שיח, או מסיימים ומבטלים את הסקיצה. ניווט התצוגה זז אל מתחתיהם.",
@@ -3944,11 +4115,11 @@
"select two components": "יש לבחור שני רכיבים",
"set by the sketch's lines": "נקבע לפי קווי הסקיצה",
"settings.display_quality_note": "ברמות עדינות יותר, פאות ומקצועות מעוקלים מוצגים חלקים יותר ויצירת הרשת אורכת זמן רב יותר. רק אזור התצוגה משתנה, לא התכן ולא קובצי הייצוא.",
"settings.double_click_components": "לחיצה כפולה על רכיב מפעילה אותו, או עורכת רכיב מקושר במקומו",
"settings.double_click_components": "לחיצה כפולה על רכיב להפעלתו",
"settings.frame_rate_details_note": "מוסיף גרף של זמני התמונות האחרונות עם המשולשים שצוירו מעליו, היסטוגרמה של זמני התמונות ואת מה שה־GPU צייר. לחיצה על קצב התמונות בתצוגה מציגה או מסתירה אותם.",
"settings.frame_rate_note": "הצגת מספר הפריימים לשנייה בפינה הימנית התחתונה של התצוגה. כל עוד האפשרות פעילה, התצוגה מצוירת מחדש ברציפות.",
"settings.insert_lists_open_designs": "הוספת תכן מציגה קודם את התכנים הפתוחים בלשוניות אחרות",
"settings.measure_axis_distances": "הצגת מרחקי X/Y/Z",
"settings.insert_lists_open_designs": "הוספת תכנון מציגה תחילה תכנונים פתוחים",
"settings.measure_axis_distances": "מרחקי צירים במדידה",
"settings.measure_axis_distances_help": "הצגת המרחקים לאורך צירי העולם לצד המרחק הכולל כשכלי המדידה פתוח.",
"settings.msaa_applies_on_restart": "החלקת הקצוות תחול בהפעלה הבאה של SolidCraft",
"settings.msaa_reset": "{setting}: {requested}× אינו נתמך בהתקן הגרפי הזה; הערך הוא כעת {reset}× (נתמכים: {supported}).",
@@ -3958,7 +4129,7 @@
"settings.reset_intro": "ההגדרות האלה לא פועלות במחשב הזה והיו מונעות מ־SolidCraft לעלות, ולכן הן שונו:",
"settings.reset_title": "ההגדרות שונו",
"settings.supersampling_note": "התצוגה מצוירת ברזולוציה גבוהה יותר ומסוננת כלפי מטה: מקצוות, קווים ורשת חלקים יותר במסכים שמתחת ל־4K. הדבר צורך זמן GPU: בערך 2.25× פיקסלים ב־1.5×, ו־4× ב־2×.",
"settings.update_changed_links": "עדכון תכנים מקושרים שהשתנו כשלשונית מוצגת שוב",
"settings.update_changed_links": "עדכון קישורים שהשתנו בחזרה ללשונית",
"settings.window_system_details": "פרטים",
"settings.window_system_note_now": "ייכנס לתוקף אחרי הפעלה מחדש. כעת: {now}.",
"settings.window_system_wayland_limited": "חלק מהתכונות מוגבלות ב־Wayland מקורי.",
@@ -4360,7 +4531,7 @@
"ui.agent_chat_send_now_tip": "עצירת הצעד הנוכחי של הסוכן ושליחת הודעה זו כבאה בתור (Ctrl+Enter)",
"ui.agent_chat_setup_body": "SolidCraft מדבר עם סוכן קוד שמותקן במחשב הזה. אפשר לבחור אחד בהעדפות › AI.",
"ui.agent_chat_setup_title": "הגדרת עוזר",
"ui.agent_chat_side": "צד הצ'אט עם הסוכן",
"ui.agent_chat_side": "לעגן את הצ'אט",
"ui.agent_chat_signed_in": "מחובר",
"ui.agent_chat_state_auth": "נדרשת התחברות",
"ui.agent_chat_state_busy": "עובד…",
@@ -4379,7 +4550,7 @@
"ui.agent_chat_undone": "(בוטל)",
"ui.agent_chat_view_attached": "התצוגה צורפה",
"ui.agent_chat_waiting": "ממתין לך",
"ui.agent_cursor": "סמן הסוכן",
"ui.agent_cursor": "מצביע הסוכן",
"ui.agent_cursor_speed": "מהירות סמן הסוכן",
"ui.agent_off_thread": "שמירה על תגובתיות החלון בזמן שסוכנים מריצים פקודות",
"ui.agent_off_thread_note": "פקודות איטיות של סוכן (מחלונית ה־AI או מלקוח MCP) מחושבות לצד החלון: אפשר להמשיך לסובב ולבחון את התצוגה, ולעצור פקודה מהתג שבתצוגה. כבוי: כל פקודה רצה בתהליכון של החלון, והחלון ממתין לה.",
@@ -4425,14 +4596,14 @@
"ui.ai_key_note": "נשמר במחזיק המפתחות של המערכת, לעולם לא בהגדרות של SolidCraft. יש להשאיר ריק עבור שרת מקומי.",
"ui.ai_keychain_error": "מחזיק המפתחות של המערכת סירב: {error}",
"ui.ai_kind": "סוג",
"ui.ai_mode_all": "התרת הכול",
"ui.ai_mode_all": "לעשות הכול",
"ui.ai_mode_all_note": "שום דבר לא שואל קודם. דוחות קריסה נשארים חסומים ומגבלות התיקיות עדיין חלות.",
"ui.ai_mode_all_warning": "הסוכן יכול לשמור, לדרוס, לייצא ולהחליף את התכן בלי לשאול. יש להשתמש בזה רק עם סוכן אמין, ועל תכנים שנשמרו.",
"ui.ai_mode_ask": "שאלה בכל פעם",
"ui.ai_mode_ask": "לשאול קודם",
"ui.ai_mode_ask_note": "כל שינוי בתכן, וכל פעולת קובץ, ממתינים לאישור התרה.",
"ui.ai_mode_edits": "התרה אוטומטית של עריכות מודל",
"ui.ai_mode_edits": "לבצע עריכות",
"ui.ai_mode_edits_note": "פקודות מידול רצות בלי לשאול; שמירה, פתיחה, ייצוא ופעולות קובץ או פעולות הרסניות אחרות עדיין שואלות.",
"ui.ai_mode_global": "כפי שהוגדר למעלה",
"ui.ai_mode_global": "כמו למעלה",
"ui.ai_model": "מודל",
"ui.ai_model_hint": "qwen3:14b",
"ui.ai_modes_scope": "ההגדרות האלה חלות על הכלים של SolidCraft. הכלים של הסוכן עצמו (המעטפת שלו, עריכת הקבצים שלו) כפופים למצב של הסוכן, שנבחר בכותרת חלונית הצ'אט כשלסוכן יש מצבים.",
@@ -4458,7 +4629,7 @@
"ui.ai_save": "שמירה",
"ui.ai_settings": "הגדרות AI…",
"ui.ai_show_features": "הצגת תכונות AI",
"ui.ai_show_features_note": "הצ'אט עם הסוכן: חלונית שבה סוכן קוד שכבר נמצא בשימוש, או נקודת קצה של מודל לבחירתך, עובדים על התכן הפתוח. כבוי: אין לחצן AI, קיצור דרך, פריט תפריט או חלונית, ושום דבר לא מחופש או מופעל.",
"ui.ai_show_features_note": "כיבוי מסתיר את העוזר בכל מקום ולא מפעיל דבר. כל מה שמתחת מעומעם.\n\nהצ'אט עם הסוכן: חלונית שבה סוכן קוד שכבר נמצא בשימוש, או נקודת קצה של מודל לבחירתך, עובדים על התכן הפתוח. כבוי: אין לחצן AI, קיצור דרך, פריט תפריט או חלונית, ושום דבר לא מחופש או מופעל.",
"ui.ai_switch_tip": "החלפת הסוכן לשיחה הזו",
"ui.ai_test": "בדיקה",
"ui.ai_test_acp_ok": "עונה: {agent}",
@@ -4492,7 +4663,7 @@
"ui.ai_tool_sum_then": "{first}, ואז {next}",
"ui.ai_tool_unfold": "יש ללחוץ כדי להציג מה הוחזר",
"ui.ai_tool_waiting": "ממתין",
"ui.ai_toolbar_button": "הצגת לחצן העוזר בסרגל היישום",
"ui.ai_toolbar_button": "כפתור העוזר בסרגל היישום",
"ui.ai_toolbar_button_note": "לחצן העוזר בסרגל היישום. Ctrl+Shift+A ותפריט התצוגה עדיין פותחים את החלונית.",
"ui.ai_undo_tip": "ביטול כל מה שהסוכן שינה בהודעה הזו, כצעד אחד",
"ui.ai_url_hint": "https://example.com/v1",
@@ -4639,6 +4810,7 @@
"ui.default_modelling_orientation": "כיוון מידול ברירת מחדל",
"ui.delete": "מחיקה",
"ui.delete_select_first": "בחרו קודם מה למחוק: תכונות, גופים, רכיבים או גאומטריית סקיצה",
"ui.dependents_select_first": "בחרו קודם מה להציג את התלויים שלו",
"ui.depth": "עומק",
"ui.design": "תכן",
"ui.design_properties.author": "מחבר",
@@ -4764,7 +4936,7 @@
"ui.extract_done": "הרכיב הוא עכשיו תכן נפרד, מקושר לכאן.",
"ui.extrude": "שליפה",
"ui.extrude_auto_to_object": "שליפה עד יעד שנבחר",
"ui.extrude_auto_to_object_tip": "בשליפה, כשנבחר פרופיל, בחירת פאה, קודקוד או מישור מקביל מחוץ למישור שלו מעבירה את התחום ל\"עד אובייקט\"",
"ui.extrude_auto_to_object_tip": "פאה, קודקוד או מישור מקביל מעבירים את ההיקף למצב עד אובייקט.",
"ui.factor": "מקדם",
"ui.fade": "דעיכה",
"ui.fade_length": "אורך דעיכה",
@@ -4796,6 +4968,10 @@
"ui.fillet_setback_least": "יותר מ-{least}",
"ui.fillet_setback_tip": "היכן מתחיל טלאי הפינה החלק: המרחק לאורך כל קצה מהפינה. ברירת המחדל היא פי {factor} מהרדיוס. עליה להיות גדולה מהנסיגה המזערית, שבה העיגולים נפגשים בלעדיה: הרדיוס כשהפאות ניצבות זו לזו.",
"ui.fillet_smooth_radius": "שינוי רדיוס חלק",
"ui.fillet_turn": "פניות",
"ui.fillet_turn_mitre": "מיטר",
"ui.fillet_turn_swept": "מסובב",
"ui.fillet_turn_tip": "היכן ששני עיגולים מסתובבים סביב קצה פינה חד ביניהם, כמו העיגולים בבסיס בליטה ליד הקצוות הזקופים שלה.\nמיטר: העיגולים נמשכים עד שהם נפגשים במיטר.\nמסובב: העיגולים נעצרים בקצה הפינה ומסתובבים סביבו, והפינה היא טורוס המשיק לשניהם. רק לעיגולים ברדיוס אחד סביב קצה פינה ישר וקמור.",
"ui.fillet_type_choices_tip": "רדיוס קבוע: רדיוס אחד לכל אורך הקצה.\nאורך מיתר: רוחב העיגול נשאר זהה, בכל זווית שבה הפאות נפגשות.\nרדיוס משתנה: רדיוס בכל קצה, ועוד לאורך הקצה אם מוסיפים.\nרציף בעקמומיות: חתך בלי שבר בהשתקפויות היכן שהוא פוגש פאות מישוריות (G2).\nספליין: הרדיוס לאורך הקצה עוקב אחרי רדיוסי בקרה שאתם קובעים.",
"ui.fillet_type_curvature": "רציף בעקמומיות",
"ui.fillet_type_tip": "החתך לרוחב הקצה. רציף בעקמומיות: חתך רציף בעקמומיות (G2) בין פאות מישוריות, כך שההשתקפויות עוברות מהפאות אל העיגול בלי שבר.",
@@ -4908,7 +5084,7 @@
"ui.hidePlanes": "הסתרת מישורים",
"ui.hideProfile": "הסתרת פרופיל",
"ui.hide_component_history": "הסתר את ההיסטוריה שלו",
"ui.hide_origin_by_default": "הסתרת הראשית כברירת מחדל",
"ui.hide_origin_by_default": "הסתרת תיקיית הראשית בתכנונים חדשים",
"ui.hide_origin_note": "תכנים חדשים מתחילים כשתיקיית הראשית מוסתרת. כל תכן שומר על הגדרת הראשית שלו אחרי שמציגים או מסתירים אותה.",
"ui.history_back": "צעד אחד אחורה",
"ui.history_below_browser": "מתחת לעץ התכן",
@@ -5068,7 +5244,7 @@
"ui.pitch": "פסיעה",
"ui.placement": "מיקום",
"ui.placement_handles": "ידיות מיקום",
"ui.placement_handles_tip": "ידיות בתצוגה המקדימה של משען, בליטה או חיבור הצמדה: יש לגרור את הסמן כדי להזיז על פני המודל, ולסובב בעזרת הטבעת",
"ui.placement_handles_tip": "סמן לגרירת התכונה על המודל וטבעת לסיבובה.",
"ui.plane": "מישור",
"ui.plane_at_angle": "מישור בזווית",
"ui.plugins": "תוספים",
@@ -5266,8 +5442,8 @@
"ui.start": "התחלה",
"ui.start_over": "התחלה מחדש",
"ui.start_page": "דף פתיחה",
"ui.step_values": "שינוי ערכים בעזרת מקשי החצים והגלגלת",
"ui.step_values_tip": "בשדה ערך שמקלידים בו, החצים למעלה ולמטה משנים מספר רגיל ב־1 (Shift: 10, Alt: 0.1), וכך גם גלגלת העכבר מעל השדה. ביטויים נשארים כפי שהוקלדו.",
"ui.step_values": "שינוי ערכים בחצים ובגלגלת",
"ui.step_values_tip": "מקשי מעלה/מטה והגלגלת מעל שדה ערך משנים את המספר בו.",
"ui.stitch_tolerance_hint": "הדוקה",
"ui.stretch": "מתיחה",
"ui.supersampling": "דגימת־על (SSAA)",
+1
View File
@@ -1392,6 +1392,7 @@
"document.read_only_save": "Questa progettazione è stata aperta in sola lettura: salvala come nuovo file",
"document.save_changes": "Salvare le modifiche a {name}?",
"document.save_copy": "Salva una copia…",
"document.tab_unsaved_changes": "Modifiche non salvate",
"document.unsaved_changes": "{name} contiene modifiche non salvate. Se non salvi, andranno perse.",
"drive.rigid": "{joint} è un giunto rigido: tiene ferme le sue parti, quindi non c'è nulla da azionare. Per farle muovere, cambia il suo tipo in Modifica giunto: Rotante per ruotare, Scorrevole per scorrere, Cilindrico per entrambi.",
"drive.stopped_by_contact": "Arrestato a {value}: {a} attraverserebbe {b}",
+265 -52
View File
@@ -379,7 +379,7 @@
"Move Type": "移動タイプ",
"Move down": "下へ移動",
"Move earlier": "前へ移動",
"Move holes by clicking or dragging": "クリックやドラッグで穴を移動",
"Move holes by clicking or dragging": "クリックまたはドラッグで移動",
"Move later": "後へ移動",
"Move up": "上へ移動",
"Name": "名前",
@@ -585,7 +585,7 @@
"Show ghost": "ゴーストを表示",
"Show in folder": "フォルダに表示",
"Show the pointer an AI agent moves (#35)": "エージェントが操作するポインターを表示 (#35)",
"Show tool at cursor": "カーソルの横にツールを表示",
"Show tool at cursor": "カーソル位置にツールを表示",
"Signed axes": "符号付き座標軸",
"Size": "サイズ",
"Sketch constraints": "スケッチ拘束",
@@ -596,7 +596,7 @@
"Sketch points": "スケッチ点",
"Slider": "スライダ",
"Slot width": "長穴の幅",
"Snap to face and edge centres": "面とエッジの中心にスナップ",
"Snap to face and edge centres": "中心にスナップ",
"SolidCraft cannot submit reports directly. Submit on GitHub; no token is needed in the app.": "SolidCraftからレポートを直接送信することはできません。GitHubで送信してください。アプリにトークンは必要ありません。",
"SolidCraft closed before these designs were saved. Their last autosave can be opened.": "これらのデザインが保存される前にSolidCraftが終了しました。最後の自動保存ファイルを開くことができます。",
"Sort A to Z": "昇順で並べ替え",
@@ -739,34 +739,34 @@
"a picked edge's body is gone": "選択したエッジのボディが存在しなくなりました",
"add a key for a joint first": "先にジョイントのキーを追加してください",
"ai.connected_toast": "SolidCraft が {clients} で使えるようになりました。使うには再起動してください(設定 > アシスタント)。",
"ai.prefs.allow_control": "AI エージェントの接続を許可(コントロールチャンネル / MCP)",
"ai.prefs.allow_control_note": "このコンピューター内だけのコントロールチャンネル(127.0.0.1、ポート 27878 または次に空いているポート)を開きます。接続するプログラムは、起動のたびに作られ、あなたのユーザーアカウントだけが読める秘密のトークンも提示する必要があります。次に SolidCraft を起動したときから有効です。オフ:--control を付けて起動しない限り、何も待ち受けません。",
"ai.prefs.availability": "アシスタントの操作",
"ai.prefs.background_commands": "エージェントのコマンドをバックグラウンドで実行",
"ai.prefs.connect_apps": "このコンピューターの AI アプリで SolidCraft を使えるようにする",
"ai.prefs.connect_apps_note": "SolidCraft の起動時に、このコンピューターで見つかった AI アプリ(OpenCode、Claude Desktop、Claude Code、Cursor、Codex、Gemini CLI、Copilot CLI、Windsurf、Zed、VS Code)へ MCP サーバーとして追加します。追加または更新するのは solidcraft のエントリのみで、変更済みのエントリには触れず、ファイルは先にバックアップされます。オフにすると追加したエントリを削除します。",
"ai.prefs.connect_at_start": "SolidCraft の起動時に前回使用したエージェントに接続",
"ai.prefs.allow_control": "AIエージェントの接続を許可",
"ai.prefs.allow_control_note": "ローカルチャネル(MCP)です。このコンピューターから外には何も送信しません。\n\nこのコンピューター内だけのコントロールチャンネル(127.0.0.1、ポート 27878 または次に空いているポート)を開きます。接続するプログラムは、起動のたびに作られ、あなたのユーザーアカウントだけが読める秘密のトークンも提示する必要があります。次に SolidCraft を起動したときから有効です。オフ:--control を付けて起動しない限り、何も待ち受けません。",
"ai.prefs.availability": "利用設定",
"ai.prefs.background_commands": "時間のかかるコマンドをバックグラウンドで実行",
"ai.prefs.connect_apps": "SolidCraftをAIアプリに追加",
"ai.prefs.connect_apps_note": "オフにすると追加した項目を削除します。\n\nSolidCraft の起動時に、このコンピューターで見つかった AI アプリ(OpenCode、Claude Desktop、Claude Code、Cursor、Codex、Gemini CLI、Copilot CLI、Windsurf、Zed、VS Code)へ MCP サーバーとして追加します。追加または更新するのは solidcraft のエントリのみで、変更済みのエントリには触れず、ファイルは先にバックアップされます。オフにすると追加したエントリを削除します。",
"ai.prefs.connect_at_start": "起動時に前回のエージェントに接続",
"ai.prefs.connect_at_start_note": "SolidCraft を起動するたびに、前回接続したエージェント (またはデフォルトのエージェント) をバックグラウンドで起動し、すぐにチャットできるようにします。--control を付けて起動した場合は行いません。接続できない場合、アシスタントに「接続に失敗しました」と「再試行」ボタンが表示されます。",
"ai.prefs.follow_camera": "カメラをスケッチに向ける",
"ai.prefs.follow_camera": "カメラを追従",
"ai.prefs.follow_camera_off": "カメラの自動追従はオフです。",
"ai.prefs.follow_camera_on": "エージェントが作成するスケッチにカメラを向けます。",
"ai.prefs.intro": "エージェント、権限、作業の表示方法を設定します。",
"ai.prefs.intro": "SolidCraftのAIエージェント。",
"ai.prefs.make_default": "既定にする",
"ai.prefs.no_agents": "エージェントはまだありません。追加するか、更新してインストール済みのエージェントを検索してください。",
"ai.prefs.no_saved_approvals": "保存された許可はありません。",
"ai.prefs.open_at_start": "SolidCraft の起動時にアシスタントパネルを開く",
"ai.prefs.open_at_start": "起動時にチャットを開く",
"ai.prefs.open_at_start_note": "SolidCraft を起動するたびにアシスタントパネル (または独立したウィンドウ) を表示します。--control を付けて起動した場合は行いません。",
"ai.prefs.override_all_warning": "このエージェントは確認せずに設計を保存、上書き、書き出し、置換できます。",
"ai.prefs.permission_all": "すべて許可",
"ai.prefs.permission_all_summary": "利用可能なSolidCraftツールを確認せず実行します。",
"ai.prefs.permission_ask": "変更前に確認",
"ai.prefs.permission_ask_summary": "設計の変更とファイル操作を毎回確認します。",
"ai.prefs.permission_edits": "モデル編集を許可",
"ai.prefs.permission_edits_summary": "モデル編集は自動実行。ファイル操作や破壊的な操作は確認します。",
"ai.prefs.permission_all": "すべて実行",
"ai.prefs.permission_all_summary": "確認しない",
"ai.prefs.permission_ask": "先に確認",
"ai.prefs.permission_ask_summary": "変更ごとに確認",
"ai.prefs.permission_edits": "編集を許可",
"ai.prefs.permission_edits_summary": "自由に編集し、その他は確認",
"ai.prefs.permission_scope": "SolidCraftツールのみに適用されます。エージェントのシェルやファイル編集には、そのエージェント自身の権限設定が適用されます。",
"ai.prefs.pointer_preview_hidden": "ポインターは非表示",
"ai.prefs.pointer_preview_speed": "エージェントのポインター · {speed}",
"ai.prefs.pointer_speed": "移動速度",
"ai.prefs.pointer_speed": "速度",
"ai.prefs.privacy_note": "会話は選択したエージェントまたはエンドポイントにのみ送信されます。エンドポイントのキーはシステムのキーチェーンに保存されます。",
"ai.prefs.show_pointer": "エージェントのポインターを表示",
"ai.prefs.turn_limit_hint": "アシスタントが1件のメッセージで行える、設計を変更するツール呼び出しの上限です。",
@@ -833,6 +833,8 @@
"cmd.appearance_pick_up": "外観を取得",
"cmd.appearance_replace": "外観を置換",
"cmd.assembly_bom": "部品表",
"cmd.assembly_bom_item_set": "項目番号の変更",
"cmd.assembly_bom_renumber": "項目番号の振り直し",
"cmd.assembly_mass_properties": "質量特性",
"cmd.body.print_face": "造形面",
"cmd.body_rename": "ボディの名前を変更",
@@ -932,6 +934,36 @@
"cmd.document_metadata_set": "デザインのプロパティを変更",
"cmd.document_revision": "デザイン リビジョン",
"cmd.document_units": "デザインの単位を変更",
"cmd.drawing_balloon_add": "バルーンを追加",
"cmd.drawing_balloon_edit": "バルーンを編集",
"cmd.drawing_balloon_inspect": "バルーンを確認",
"cmd.drawing_close": "図面を閉じる",
"cmd.drawing_create": "図面を作成",
"cmd.drawing_delete": "図面を削除",
"cmd.drawing_dimension_add": "図面寸法を追加",
"cmd.drawing_dimension_edit": "図面寸法を編集",
"cmd.drawing_dimension_inspect": "図面寸法を確認",
"cmd.drawing_export": "シートを書き出す",
"cmd.drawing_inspect": "図面を確認",
"cmd.drawing_list": "図面を一覧表示",
"cmd.drawing_open": "図面を開く",
"cmd.drawing_properties": "図面のプロパティ",
"cmd.drawing_properties_remove": "図面のプロパティを削除",
"cmd.drawing_properties_set": "図面のプロパティを変更",
"cmd.drawing_revision_add": "リビジョンを追加",
"cmd.drawing_revision_delete": "リビジョンを削除",
"cmd.drawing_revision_edit": "リビジョンを編集",
"cmd.drawing_scene": "シートを描画",
"cmd.drawing_set": "図面設定",
"cmd.drawing_sheet_add": "シートを追加",
"cmd.drawing_sheet_delete": "シートを削除",
"cmd.drawing_sheet_reorder": "シートを並べ替え",
"cmd.drawing_sheet_set": "シート設定",
"cmd.drawing_view_copy_to_sheet": "ビューをシートにコピー",
"cmd.drawing_view_edit": "図面ビューを編集",
"cmd.drawing_view_inspect": "図面ビューを確認",
"cmd.drawing_view_move_to_sheet": "ビューをシートに移動",
"cmd.drawing_view_snapshot": "図面ビューのスナップショット",
"cmd.edit_checkpoint": "チェックポイント",
"cmd.edit_repeat": "最後のコマンドを繰り返す",
"cmd.edit_restore_checkpoint": "チェックポイントを復元",
@@ -997,6 +1029,11 @@
"cmd.joint_screw": "ねじ",
"cmd.joint_solve": "ジョイントを解決",
"cmd.joint_sweep": "ジョイントをスイープ",
"cmd.layout.apply": "レイアウトを適用",
"cmd.layout.get": "レイアウトを確認",
"cmd.layout.reset": "レイアウトをリセット",
"cmd.layout.save": "レイアウトを保存",
"cmd.layout.workspace.select": "レイアウトのワークスペースを選択",
"cmd.library_apply": "ローカルライブラリ項目を適用",
"cmd.library_export": "デザインライブラリを書き出す",
"cmd.library_inspect": "ローカルライブラリを確認",
@@ -1024,12 +1061,28 @@
"cmd.motion_trace_edit": "モーション トレースを編集",
"cmd.motion_trace_keep": "モーション トレースを保持",
"cmd.occurrence_align": "オカレンスを整列",
"cmd.occurrence_bom_type": "部品表の種類",
"cmd.occurrence_copy": "貼り付け(インスタンス)",
"cmd.occurrence_drag": "オカレンスをドラッグ",
"cmd.occurrence_ground": "空間固定",
"cmd.occurrence_move": "オカレンスを移動",
"cmd.occurrence_properties": "オカレンスの物理的プロパティ",
"cmd.occurrence_reparent": "コンポーネントへ移動",
"cmd.panel.activate": "パネルのタブをアクティブ化",
"cmd.panel.close": "パネルを閉じる",
"cmd.panel.collapse": "パネルのセクションを折りたたむ",
"cmd.panel.dismiss": "パネルの一時表示を閉じる",
"cmd.panel.float": "パネルをフロート表示",
"cmd.panel.focus": "パネルにフォーカス",
"cmd.panel.list": "パネルを一覧表示",
"cmd.panel.move": "パネルを移動",
"cmd.panel.native_close": "別ウィンドウのパネルを再ドッキング",
"cmd.panel.open": "パネルを開く",
"cmd.panel.peek": "折りたたんだパネルを一時表示",
"cmd.panel.popout": "パネルを別ウィンドウに移動",
"cmd.panel.reset_pair": "パネルの区切りをリセット",
"cmd.panel.resize": "パネルのセクションのサイズを変更",
"cmd.panel.resize_pair": "パネルの区切りを移動",
"cmd.parameters_add": "ユーザー パラメータを追加",
"cmd.parameters_change": "パラメータを変更",
"cmd.parameters_comment": "パラメータにコメントを付与",
@@ -1376,6 +1429,9 @@
"dialog.custom_axis": "↖ カスタム軸",
"dialog.dimension_insert_hint": "別の駆動寸法をクリックして名前を挿入",
"dialog.dimension_name": "寸法: {name}",
"dialog.dimension_step_down": "{step} 減らす",
"dialog.dimension_step_expression": "増減できるのは数値だけです。式はそのまま残ります",
"dialog.dimension_step_up": "{step} 増やす",
"dialog.edit_title": "{title}を編集",
"dialog.no_shown_body": "変更できる表示中のボディがありません。ボディを表示するか、新規ボディを選択してください",
"dialog.pattern_sketch_not_object": "ボディかフィーチャーを選んでください。スケッチはどちらでもありません。曲線はスケッチ内のパターンかミラーで繰り返すか、先に押し出してください。",
@@ -1383,6 +1439,7 @@
"dialog.selected_count": "↖ {count} 個選択中",
"dialog.selection_constraint": "拘束 {id}",
"dialog.selection_external_curve": "{sketch} · 曲線 {id}",
"dialog.selection_external_point": "{point} · {sketch}",
"dialog.selection_guidance": "入力欄を選んで変更します。選択済みのオブジェクトをクリックすると解除できます。Ctrl/Cmd を押しながらクリックしても切り替えられます。プレビューを確認してから OK を押してください。",
"dialog.selection_midpoint": "中点 · {id}",
"dialog.selection_profile": "{sketch} · プロファイル {number}",
@@ -1400,6 +1457,7 @@
"document.read_only_save": "このデザインは読み取り専用で開かれています:新しいファイルとして保存してください",
"document.save_changes": "{name} への変更を保存しますか?",
"document.save_copy": "コピーを保存…",
"document.tab_unsaved_changes": "未保存の変更",
"document.unsaved_changes": "{name} には保存されていない変更があります。保存しない場合、変更内容は失われます。",
"drive.rigid": "{joint} は剛体ジョイントです。部品をその場に固定するため、駆動するものがありません。動かすには「ジョイントを編集」で種類を変更してください: 回転させるなら Revolute、スライドさせるなら Slider、両方なら Cylindrical。",
"drive.stopped_by_contact": "{value} で停止: {a} が {b} を通り抜けてしまいます",
@@ -1536,6 +1594,11 @@
"help.joint.reach": "部品上の点を目標に向けて動かし、そのチェーン上のジョイントを制限の範囲内で変化させます。ロボット アームの先端のように、先端の位置から機構の姿勢を決めるときに使います。",
"help.joint.rigid_group": "選択した部品を現在の配置のまま固定し、一体として動くようにします。互いに動きのない、ひとまとまりの部品に使います。",
"help.joint.screw": "回転とスライドをピッチで連動させ、1回転ごとにその距離だけ進むようにします。送りねじ、ボルト、ねじ軸に使います。",
"help.layout.apply": "保存したレイアウトを現在のワークスペースに適用します。保持されているエディターの下書きは維持されます。",
"help.layout.get": "確定したレイアウトと登録済みのワークスペースを確認します。",
"help.layout.reset": "現在のワークスペースに登録された既定のレイアウトを復元します。ユーザープリセットは保持されます。",
"help.layout.save": "レイアウト全体に名前を付けて保存します。ユーザープリセットの置き換えには明示的な上書き許可が必要です。",
"help.layout.workspace.select": "登録済みのレイアウト用ワークスペースを選択し、移動前のレイアウトを記憶します。",
"help.library.apply": "ローカルのライブラリファイルから材料または外観を、ボディ、面、コンポーネントに適用します。別のデザインで保存した材料を再利用するときに使います。",
"help.library.export": "デザインの材料と外観をライブラリファイルに保存します。共有したり、ほかのデザインで再利用したりするときに使います。",
"help.library.inspect": "デザインを変更せずに、ライブラリファイルを確認して材料と外観を一覧にします。適用する前にライブラリの中身を確認するときに使います。",
@@ -1549,6 +1612,21 @@
"help.notice": "NOTICE",
"help.occurrence.ground": "部品をその場に固定するか、固定を解除します。ジョイントはほかの部品をこの部品の周りで動かします。アセンブリの残りが組み付けられるフレームやベースに使います。",
"help.occurrence.move": "アセンブリ部品をジョイントが許す自由度だけで動かします。部品の面を選択してから、操作可能な矢印または円弧をドラッグしてください。",
"help.panel.activate": "キーボードフォーカスを要求せずに、開いているパネルのタブを選択します。",
"help.panel.close": "内容のライフサイクルの確認を経てパネルを閉じます。配置は記憶されます。",
"help.panel.collapse": "要求された寸法を保持したまま、パネルのセクションを折りたたむか展開します。",
"help.panel.dismiss": "折りたたんだパネルの一時表示を閉じます。",
"help.panel.float": "パネルを明示的に指定した位置のフローティングセクションに移動します。",
"help.panel.focus": "パネルにフォーカスします。必要に応じて保持された状態を再度開き、セクションを展開します。",
"help.panel.list": "登録済みのパネル種類、確定したインスタンス、アクティブなタブ、利用可否、ホストの機能を確認します。",
"help.panel.move": "パネルを新しいセクションに移動するか、既存のタブグループに追加します。",
"help.panel.native_close": "別ウィンドウのパネルセクションを、内容をキャンセルせずに記憶されたドックに戻します。",
"help.panel.open": "パネルの種類と明示的なインスタンス識別子を指定して開きます。新しいインスタンスには配置を指定します。",
"help.panel.peek": "保存されたサイズや折りたたみ状態を変更せずに、折りたたんだパネルを一時表示します。",
"help.panel.popout": "対応している場合はパネルを別ウィンドウに移動します。ブラウザーではフローティングセクションを使用します。",
"help.panel.reset_pair": "隣接するドックのペアを、共通の制約内で既定のサイズ比率に戻します。",
"help.panel.resize": "すべてのタブの制約内で、パネルセクションの外側の幅と高さを設定します。",
"help.panel.resize_pair": "共有する全体の長さを維持しながら、隣接するドックセクションのサイズを同時に変更します。",
"help.parameters.change": "デザインの寸法を決める名前付きの値と式の表を開きます。1か所で寸法を変え、それを使うすべてのフィーチャに反映させるときに使います。",
"help.parameters.export": "デザインのパラメータをCSVまたはJSONで保存します。表計算ソフトで編集したり、別のデザインに移したりするときに使います。",
"help.parameters.graph": "どのパラメータとフィーチャーがほかのどれを使っているか、その間のループやエラーも表示します。パラメータを変更すると何に影響するかを確認するときに使います。",
@@ -1885,21 +1963,21 @@
"joint.rigid_group_select_components": "2 つ以上のコンポーネントを選択してください。",
"joint_move.blocked_hint": "Rigid合致、部品の固定状態、ジョイントの制限値、パラメーターでの拘束を確認してください。",
"joint_move.blocked_title": "この部品は移動できません",
"joint_move.coarse": "軸に近い位置の回転刻み(°)",
"joint_move.coarse": "軸の近くのステップ",
"joint_move.conflict": "この部品のジョイントが他のジョイントと競合しています: {joint}。",
"joint_move.conflict_hint": "競合しているジョイントを編集、抑制、または削除して、他のジョイントで部品を配置できるようにしてください。",
"joint_move.distance": "開始位置からの移動距離",
"joint_move.done": "完了",
"joint_move.fine": "軸から遠い位置の回転刻み(°)",
"joint_move.fine": "軸から離れたステップ",
"joint_move.fixed": "この部品には操作できるジョイントの自由度がありません。",
"joint_move.help": "移動 — 矢印または円弧をドラッグ。Escでドラッグを取消。",
"joint_move.legacy_hint": "オフにすると従来の移動パネルを使います。",
"joint_move.precision": "破線ガイドの外側へ引くと回転を微調整できます。",
"joint_move.legacy_hint": "オフ:従来の移動パネル。",
"joint_move.precision": "破線のガイドの外へ引くと細かいステップになります。",
"joint_move.rotation": "開始位置からの回転角",
"joint_move.select": "面またはアセンブリ部品を1つ選択してから移動を実行してください。",
"joint_move.settings": "アセンブリの移動",
"joint_move.step": "刻み",
"joint_move.use_handles": "アセンブリの移動にジョイントの操作ハンドルを使う",
"joint_move.use_handles": "移動にジョイントのモーションハンドルを使用",
"keymap.command_id": "コマンドID",
"keymap.empty": "一致するコマンドはありません",
"keymap.use_anyway": "ここでは{key}をそのまま使用",
@@ -1959,6 +2037,7 @@
"material.selection.mixed": "異なる材料",
"measure.at": "位置",
"measure.item": "項目 {number}",
"measure.line_to_centre": "線から中心まで",
"measure.quick_copied": "測定値をコピーしました",
"measure.quick_copy": "クリックで測定値をコピー",
"measure.quick_edges": "エッジ {count} 本",
@@ -2017,6 +2096,12 @@
"navbar.assistant": "アシスタント",
"navbar.custom.add_slot": "{slot} を追加",
"navbar.custom.always": "常に表示",
"navbar.custom.anchor.bottom_centre": "下中央",
"navbar.custom.anchor.bottom_left": "左下",
"navbar.custom.anchor.bottom_right": "右下",
"navbar.custom.anchor.top_centre": "上中央",
"navbar.custom.anchor.top_left": "左上",
"navbar.custom.anchor.top_right": "右上",
"navbar.custom.available": "{available} · バーへドラッグ、または「追加」",
"navbar.custom.buttons": "ボタン",
"navbar.custom.compact": "コンパクト",
@@ -2053,6 +2138,7 @@
"navbar.custom.reset_default": "既定値に戻す",
"navbar.custom.right": "右へ移動",
"navbar.custom.select": "スロットが選択されていません。バーのスロットを選択して移動、削除、表示の変更ができます。",
"navbar.custom.separator_hint": "ボタンのグループ間の区切りです。バーにドラッグするか、「追加」を押してください。いくつでも使用できます。",
"navbar.custom.separators_full": "区切りは既に4個あります。",
"navbar.custom.show": "ナビゲーションバーを表示",
"navbar.custom.shows": "表示内容",
@@ -2206,21 +2292,37 @@
"prefs.appearance.viewport_logo": "ビューポートとロゴ",
"prefs.applies_on_restart": "再起動後に適用",
"prefs.assistant": "アシスタント",
"prefs.assistant_connecting": "接続",
"prefs.assistant_follow_help": "スケッチのLook At操作で、ポインターに合わせてビューが移動します。",
"prefs.assistant_limit": "上限",
"prefs.assistant_limited": "制限あり",
"prefs.assistant_pointer_speed": "ポインターの速度",
"prefs.assistant_seconds": "秒",
"prefs.assistant_seconds_aria": "秒",
"prefs.assistant_use_care": "注意して使用",
"prefs.auto_operation": "操作を自動で選択",
"prefs.auto_operation_note": "フィーチャーのウィンドウは、新しい形状が何に接するかから結合・新規ボディ・切り取りを選びます(手動で選ぶまで)。オフ:開いたときの操作のままです。",
"prefs.auto_operation_note": "材料が接触する対象に応じて結合、新規ボディ、切り取りを選びます。",
"prefs.axes": "軸",
"prefs.axis": "軸",
"prefs.blur_cost": "相対的な描画負荷です。バーが多いほど、通常は処理量が増えます。",
"prefs.blur_device": "このグラフィックスデバイスまたは描画経路では利用できません",
"prefs.blur_guard": "画面の応答性を保つため、ぼかしの品質を下げました。半径は変更されていません。",
"prefs.blur_off": "オフ",
"prefs.blur_reduce_transparency": "システムの透明度を下げる設定により無効です",
"prefs.blur_software": "ソフトウェア描画では利用できません (gpu-software)",
"prefs.blur_source": "このワークスペース表示では利用できません",
"prefs.blur_web": "このウェブ描画経路では利用できません",
"prefs.bodies_folders": "ボディフォルダー",
"prefs.bodies_folders_always": "常に表示",
"prefs.bodies_folders_never": "表示しない",
"prefs.bodies_folders_several": "複数ボディのみ",
"prefs.bodies_folders_several": "複数",
"prefs.bottom": "下",
"prefs.bottom_centre": "下中央",
"prefs.bottom_right": "右下",
"prefs.browser": "ブラウザー",
"prefs.browser_components_open": "コンポーネントを最初から展開",
"prefs.browser_components_open_note": "ブラウザーのすべてのコンポーネントを以前と同じく最初から展開して表示します。オフの場合は展開するまで折りたたまれています。",
"prefs.browser_eye_left": "表示切り替えを折りたたみ矢印の隣に表示",
"prefs.browser_eye_left": "折りたたみ矢印の隣の目のアイコン",
"prefs.browser_eye_left_note": "ブラウザーの各行の表示切り替えを、行の右端の列ではなく、以前のように折りたたみ矢印の隣に表示します。",
"prefs.browser_reveal_on_hover": "ホバーで表示",
"prefs.browser_show_note": "「表示」はブラウザーを常に画面に表示します。「ホバーで表示」はポインターがその位置か左端の細いタブに来るまで隠し、ポインターが中にある間やそのメニューが開いている間は表示したままにします。",
@@ -2249,15 +2351,34 @@
"prefs.dark_parts_shading_help": "リニア光と柔らかいスカイライトでビューをシェーディングし、暗い面が黒くつぶれずにグラデーションを保つようにします",
"prefs.dead_zone_note": "斜線の帯はデッドゾーンです。この範囲内の動きは無視されます。",
"prefs.default": "既定",
"prefs.design": "設計の既定値",
"prefs.design_note": "新しいデザインの初期設定です。開いているデザインはそれぞれの設定を保持します。",
"prefs.design": "デザイン",
"prefs.design_note": "新規デザインの初期設定。",
"prefs.design_tools.ask": "確認",
"prefs.design_tools.degrees": "度",
"prefs.design_tools.double_click_help": "リンクされたコンポーネントをその場で編集することもできます。",
"prefs.design_tools.ghost": "移動したパーツの元の位置をゴースト表示",
"prefs.design_tools.hole_moves_help": "面をもう一度選ぶと穴を削除せずに移動します。",
"prefs.design_tools.hole_snaps_help": "面の中央と円形エッジの中心。",
"prefs.design_tools.imported_files": "インポートしたファイル",
"prefs.design_tools.note": "通知",
"prefs.design_tools.off": "オフ",
"prefs.design_tools.placement_group": "レスト、ボス、スナップフィット",
"prefs.design_tools.rows": "行数",
"prefs.devices": "3D マウス",
"prefs.dialog_buttons": "ダイアログのボタン",
"prefs.dialogs": "ダイアログ",
"prefs.dialogs_edge": "ビューの端",
"prefs.dialogs_left_column": "左の列",
"prefs.dialogs_note": "コマンドダイアログを開く場所:ビューの端にドッキングするか、左の列でブラウザーと履歴の代わりに表示します。ダイアログを閉じるとブラウザーと履歴は元どおりに戻ります。",
"prefs.dimension_step_angle": "ウィンドウの矢印、上下キー、ホイールで角度に加える量",
"prefs.dimension_step_length": "ウィンドウの矢印、上下キー、ホイールで長さに加える量",
"prefs.dimension_steps": "ウィンドウの増減量",
"prefs.dimension_values": "寸法値の入力",
"prefs.dimension_values_note": "寸法値を入力する場所です。ウィンドウでは矢印で値を増減でき、寸法ツールは先に文字を配置します。",
"prefs.dimension_values_view": "ビュー内",
"prefs.dimension_values_window": "ウィンドウ",
"prefs.display": "表示",
"prefs.drawings": "図面",
"prefs.edges": "エッジと陰影",
"prefs.extend": "拡張",
"prefs.extra_navigation": "何もない場所でCtrl+左ドラッグすると回転し、Spaceを押しながらドラッグすると移動します。",
@@ -2292,6 +2413,23 @@
"prefs.graphics_card_restart": "SolidCraft の再起動後に有効になります。WGPU_ADAPTER_NAME がこの選択より優先されます。",
"prefs.graphics_card_unused": "保存したグラフィックスカードはこのウィンドウでは使われていません。使用中のカードは「ヘルプ › SolidCraft について」とステータス行に表示されます。自動を試すか、カード名の一意な部分を WGPU_ADAPTER_NAME に設定してください。",
"prefs.graphics_note": "速度と滑らかさのバランスを調整します。各選択肢の効果をサンプルで確認できます。",
"prefs.graphics_page.current_view": "現在のビュー",
"prefs.graphics_page.fastest": "最速",
"prefs.graphics_page.fold_items": "項目を折りたたむ",
"prefs.graphics_page.in_view": "ビュー内",
"prefs.graphics_page.live_details": "リアルタイムの詳細",
"prefs.graphics_page.own_mix": "独自の組み合わせ",
"prefs.graphics_page.quick_measure": "クイック測定",
"prefs.graphics_page.reference_geometry_through_bodies": "ボディ越しの参照ジオメトリ",
"prefs.graphics_page.restart": "再起動が必要",
"prefs.graphics_page.sharpest": "最も鮮明",
"prefs.graphics_page.single_surface_analysis": "単一のサーフェス解析",
"prefs.graphics_page.status_chips": "ステータスチップ",
"prefs.graphics_page.subtitle": "速度と滑らかさのバランスを調整します。",
"prefs.graphics_page.supersampling": "スーパーサンプリング",
"prefs.graphics_page.supersampling_adaptive": "ビューが静止している間のみ",
"prefs.graphics_page.surfaces_edges": "サーフェスとエッジ",
"prefs.graphics_page.view_bar": "ビューバーにも表示",
"prefs.graphics_presets_note": "プリセットは以下の設定をまとめて変更します。",
"prefs.hide_constraint_glyphs_zoomed_out": "ズームアウト時に拘束グリフを隠す",
"prefs.hide_constraint_glyphs_zoomed_out_note": "スケッチを十分にズームアウトすると、拘束グリフは表示されず画面を散らかしません。オフ: ズームに関係なく固定の画面サイズのままです。",
@@ -2300,7 +2438,7 @@
"prefs.history_origin_bodies_note": "左側またはブラウザー下の履歴の先頭に、アクティブなコンポーネントのボディフォルダーと原点を、ブラウザーと同じ表示切り替えとメニュー付きで表示します。下部の履歴は変わりません。",
"prefs.history_play_button": "履歴コントロールに「履歴を再生」ボタンを表示",
"prefs.history_play_button_note": "履歴コントロールの「1ステップ戻る」と「1ステップ進む」の間に「履歴を再生」を追加します。コントロールを右クリックして表示/非表示を切り替えることもできます。",
"prefs.history_sketches": "スケッチ",
"prefs.history_sketches": "履歴のスケッチ",
"prefs.history_sketches_in_order": "作成順",
"prefs.history_sketches_note": "スケッチ:「フィーチャーの下」では、左側の履歴に名前が表示される幅があるとき、スケッチをそれから作成した最初のフィーチャーの下に 1 段字下げして表示します。下部のタイムラインは常に作成順です。",
"prefs.history_sketches_under_feature": "フィーチャーの下",
@@ -2325,6 +2463,22 @@
"prefs.language_system": "システム",
"prefs.layout": "レイアウト",
"prefs.layout_note": "コンパクトではパネルをウィンドウの端に固定します。ブラウザーの半透明表示はフローティングで使用できます。アシスタントの配置はアシスタントページで変更します。",
"prefs.layout_page.active_commands": "アクティブなコマンドを強調",
"prefs.layout_page.always": "常に",
"prefs.layout_page.card": "カード",
"prefs.layout_page.column": "列",
"prefs.layout_page.hover": "ホバー時",
"prefs.layout_page.icons_labels": "アイコン+ラベル",
"prefs.layout_page.ok_cancel": "OK とキャンセル",
"prefs.layout_page.on_hover": "ホバー時",
"prefs.layout_page.open": "開く位置",
"prefs.layout_page.origin_bodies": "原点とボディを上に表示",
"prefs.layout_page.panels": "パネル",
"prefs.layout_page.pinned_menus": "固定コマンドをメニューから除外",
"prefs.layout_page.play_history": "履歴の再生ボタン",
"prefs.layout_page.slot_count": "{max} スロット中 {count}",
"prefs.layout_page.view_corner": "ビューの隅にも表示",
"prefs.layout_page.window": "ウィンドウ",
"prefs.left": "左",
"prefs.left_click": "左クリック",
"prefs.line_drag_draws_one": "押してドラッグすると線を 1 本描く",
@@ -2353,6 +2507,13 @@
"prefs.mouse_heading": "マウス",
"prefs.mouse_mapping": "マウスの割り当て",
"prefs.navigation": "ナビゲーション",
"prefs.navigation.axes_signed": "軸",
"prefs.navigation.cube_compact": "キューブ",
"prefs.navigation.device": "デバイス",
"prefs.navigation.free": "自由",
"prefs.navigation.orbit_style": "回転方式",
"prefs.navigation.radial_help": "右ボタンを長押しするかフリックすると、ツールのリングを表示します。",
"prefs.navigation.turntable": "ターンテーブル",
"prefs.navigation_note": "マウス、トラックパッド、3Dマウスによるビューの操作。",
"prefs.navigator_fit": "ナビゲーターでの視点変更後にモデルを全体表示",
"prefs.navigator_fit_note": "キューブの面・角・矢印やギズモの軸をクリックすると、どの方向からでも全体が収まる一定の距離(外接球)からモデルの中心を見ます。オンの間はホームビューも同じズームと中心を使うため、その後に選んだ視点は向きだけが変わります。オフ: 視点はその場で回転し、注視点とズームを保ちます。",
@@ -2369,11 +2530,15 @@
"prefs.origin_plane_names_note": "ブラウザー、ダイアログ、一覧に表示される名前です。{top} は XY、{front} は XZ、{right} は YZ です (Z が上)。コマンド、スクリプト、保存されたデザインでは XY、XZ、YZ のままです。",
"prefs.overall_speed": "全体の速度",
"prefs.parameter_list_rows": "パラメーター一覧の行数",
"prefs.parameter_list_rows_tip": "空の値フィールドで下矢印を押すと、お気に入りを先頭にパラメーターをこの数まで一覧表示します。少なくすると大きな設計でも一覧が軽くなります。",
"prefs.parameter_list_rows_tip": "空の式入力欄で下矢印キーを押したときに表示するパラメータの最大数。",
"prefs.persistent_ribbon_pressed": "有効なリボンコマンドを強調表示",
"prefs.persistent_ribbon_pressed_help": "効果が有効な間、リボンのコマンドを強調表示します。無効にすると、従来の中立的な表示とコマンドのヘルプを保持します。",
"prefs.perspective_fov": "画角",
"prefs.placement": "配置",
"prefs.plugins_enabled": "有効",
"prefs.plugins_installed": "インストール済み",
"prefs.plugins_intro": "サンドボックス化されたアドオン。",
"prefs.plugins_show_folder": "プラグインフォルダーを表示",
"prefs.prefer_free_sketch_points": "スケッチ点が重なったときは移動可能な点を優先する",
"prefs.preview_choices": "選択した設定のプレビュー",
"prefs.preview_tint_new_faces": "プレビューで新しい面に色を付ける",
@@ -2431,8 +2596,43 @@
"prefs.shift_add_ctrl_toggle": "Shift は追加のみ、Ctrl / Cmd は選択を切り替え",
"prefs.shift_add_note": "オン:Shift は追加、Ctrl / Cmd は選択を切り替えます。オフ(既定):どちらも選択を切り替えます。",
"prefs.shortcuts": "ショートカット",
"prefs.shortcuts_import": "インポート",
"prefs.shortcuts_intro": "各コマンドのキー。",
"prefs.shortcuts_not_assigned": "未割り当て",
"prefs.shortcuts_press": "キーを押してください...",
"prefs.single_surface_analysis": "サーフェス解析を1つだけ使用",
"prefs.single_surface_analysis_help": "オフの場合、サーフェスのシェーディングと連続性解析を同時に有効にできます。オンにすると、最後に開始または変更した解析が残ります。",
"prefs.sketch.align_points": "他のスケッチ点に整列",
"prefs.sketch.align_points_note": "作図中の線を両軸の点に同時に整列します。",
"prefs.sketch.auto_dimensions": "新しい形状にすべての寸法を付ける",
"prefs.sketch.auto_dimensions_note": "入力値と、形状を確定するすべての寸法。",
"prefs.sketch.auto_locate": "スケッチ原点を基準に配置",
"prefs.sketch.auto_locate_note": "すべての寸法を付ける場合。",
"prefs.sketch.center_arc": "中心点指定の円弧がカーソルに追従",
"prefs.sketch.click_selects": "クリックで選択",
"prefs.sketch.conflict_colour": "競合色",
"prefs.sketch.drawing": "作図",
"prefs.sketch.exit_keep": "維持",
"prefs.sketch.exit_previous": "開始前のビューに戻る",
"prefs.sketch.exit_unless_rotated": "回転した場合を除く",
"prefs.sketch.faces_edges": "面とエッジ",
"prefs.sketch.hide_glyphs": "ズームアウト時に記号を非表示",
"prefs.sketch.marks": "スケッチ内のマーク",
"prefs.sketch.note": "作図、スナップ、選択。",
"prefs.sketch.opening_closing": "開始と終了",
"prefs.sketch.prefer_free_points": "重なる場合は移動可能な点を優先",
"prefs.sketch.project_options": "投影オプションを先に確認",
"prefs.sketch.recent_snap": "2本のガイドの交点にスナップ",
"prefs.sketch.select_other_hidden": "他を選択で非表示のジオメトリも対象に",
"prefs.sketch.shortcut_cycles_modes": "ショートカットの再入力でモード切替",
"prefs.sketch.shortcut_cycles_modes_tip": "オフの場合はツールを再起動します。",
"prefs.sketch.snapping_guides": "スナップとガイド",
"prefs.sketch.state_icons": "状態アイコン",
"prefs.sketch.tracking_guides": "中心と中点のガイド",
"prefs.sketch.tracking_guides_note": "ホバーした中心やエッジの中点から、一時的な水平・垂直ガイドを表示します。",
"prefs.sketch.tracking_points": "スケッチ点からもガイドを表示",
"prefs.sketch.unless_turned_note": "回転していなければ戻る",
"prefs.sketch.whole_bodies": "ボディ全体",
"prefs.sketch_align_points": "描画中にほかのスケッチ点に揃える",
"prefs.sketch_align_points_note": "描画中、点が一つのスケッチ点と水平に、別の点と垂直に同時に揃い、点線のガイドが表示されます。クリックすると両方の整列が保持されます。オフ: 直前の点にのみ、一度に一方向だけ揃えます。",
"prefs.sketch_conflict_colour": "競合するジオメトリを競合色で表示",
@@ -2469,7 +2669,7 @@
"prefs.stand_up_unknown.ask": "ダイアログで確認",
"prefs.stand_up_unknown.note": "メモを表示",
"prefs.stand_up_unknown.off": "確認しない",
"prefs.stand_up_unknown_note": "ファイルのヘッダーに Y 上向きで保存することが知られているプログラム名がない場合の確認方法です。メモはファイルが横倒しに見える場合にのみ表示されます。「ダイアログで確認」は従来どおり、このようなファイルすべてについて確認します。",
"prefs.stand_up_unknown_note": "ファイルに既知のプログラム名がない場合の提案方法。",
"prefs.status_bar": "ステータスバー",
"prefs.status_bar_note": "ウィンドウの下端の細いバーに、編集中のもの、開いているスケッチの状態(その色で)、デザインの単位を表示します。単位をクリックすると変更できます。スケッチの状態はビューの隅には表示されなくなります。",
"prefs.status_done": "完了",
@@ -2505,6 +2705,10 @@
"prefs.theme_follow_off": "オフ",
"prefs.theme_follow_stop": "追従を停止",
"prefs.theme_import_hint": "クリックしてテーマファイルを選ぶか、ここにドロップするか、テーマをコピーした状態で Ctrl+V(Mac では Cmd+V)を押します",
"prefs.theme_notice_dismiss": "非表示",
"prefs.theme_option_blur": "パネル背面のぼかし",
"prefs.theme_option_blur_help": "半透明パネルの背面のぼかしを物理ピクセル単位で指定します。0でオフになります。",
"prefs.theme_option_invalid": "{option} の保存値が無効なため、テーマの既定値を使用します。保存値は保持されます。",
"prefs.theme_option_opacity": "不透明度",
"prefs.theme_option_opacity_help": "フローティングパネルの不透明度を調整します。ビューポートの色に重なっても文字が読めるように下限が適用されます。",
"prefs.theme_option_tab_colours": "タブの色",
@@ -2513,6 +2717,7 @@
"prefs.theme_option_tab_fade_ms_help": "タブの切り替え時にパネルとビューポートの色を徐々に変えます。0で即座に切り替えます。",
"prefs.theme_options_badge": "オプション",
"prefs.theme_options_more": "その他 · {count}",
"prefs.theme_options_newer": "このテーマには新しいバージョンの SolidCraft が必要です。更新後、元のテーマを再インポートしてオプション定義を復元してください。",
"prefs.theme_options_reset": "リセット",
"prefs.theme_options_unavailable": "このデバイスでは利用できません",
"prefs.theme_paste": "テーマを貼り付け",
@@ -2542,8 +2747,8 @@
"prefs.validation.label": "カーネルの完全チェック",
"prefs.validation.off": "オフ (試験的)",
"prefs.validation_note": "各ステップの後: スイープ、ブーリアン、ブレンドのたびにチェックします。\nフィーチャーごとに 1 回: 各フィーチャーの結果を 1 回チェックします。\nオフ (試験的): チェックしません。",
"prefs.value_boxes_in_view": "ビューでハンドルの横に値を表示",
"prefs.value_boxes_in_view_tip": "コマンドの値(距離、半径など)をモデル上のハンドルの横のボックスに表示します。オフにすると、値はコマンドウィンドウに入力します。選択後はそのウィンドウにキーボード入力が入り、モデルを覆うものはありません。",
"prefs.value_boxes_in_view": "ハンドルの横に値を表示",
"prefs.value_boxes_in_view_tip": "押し出しの距離、フィレットの半径などをビュー内のボックスに表示します。",
"prefs.view": "ビューの動作",
"prefs.view_confirm_corner": "ビューの隅に OK とキャンセル",
"prefs.view_confirm_corner_note": "ダイアログまたはスケッチを開いている間、ビュー右上のチェックとバツが、ダイアログの OK とキャンセルと同じ操作を行うか、スケッチを終了またはキャンセルします。ビューナビゲーターはその下に移動します。",
@@ -3944,11 +4149,11 @@
"select two components": "2つのコンポーネントを選択してください",
"set by the sketch's lines": "スケッチの線で決まります",
"settings.display_quality_note": "表示だけに影響します。設計や書き出しは変わりません。",
"settings.double_click_components": "コンポーネントをダブルクリックしてアクティブにする、またはリンクされたものをその場で編集する",
"settings.double_click_components": "コンポーネントをダブルクリックしてアクティブ化",
"settings.frame_rate_details_note": "直近のフレーム時間のグラフ(描画した三角形数を重ねて表示)、フレーム時間のヒストグラム、GPU が描画した内容を追加します。ビューのフレームレートをクリックすると表示/非表示を切り替えます。",
"settings.frame_rate_note": "ビューの右下に 1 秒あたりのフレーム数を表示します。",
"settings.insert_lists_open_designs": "デザインを挿入で、ほかのタブで開いているデザインを先に表示する",
"settings.measure_axis_distances": "計測のX・Y・Z距離を表示",
"settings.insert_lists_open_designs": "デザインを挿入で開いているデザインを先に表示",
"settings.measure_axis_distances": "計測で軸方向の距離を表示",
"settings.measure_axis_distances_help": "計測ダイアログを開いている間、2点間距離に加えてワールドX・Y・Z軸方向の寸法線と距離を表示します。",
"settings.msaa_applies_on_restart": "アンチエイリアスは次回 SolidCraft を起動したときに適用されます",
"settings.msaa_reset": "{setting}: {requested}× はこのグラフィックスデバイスではサポートされていないため、{reset}× に変更しました(サポート: {supported})。",
@@ -3958,7 +4163,7 @@
"settings.reset_intro": "次の設定はこのコンピューターでは使用できず、SolidCraft が起動できなくなるため変更されました:",
"settings.reset_title": "設定が変更されました",
"settings.supersampling_note": "ビューを高い解像度で描画してから縮小します。4K 未満の画面でエッジ、線、グリッドが滑らかになります。GPU の負荷が増えます(1.5× で約 2.25 倍、2× で 4 倍のピクセル)。",
"settings.update_changed_links": "タブに戻ったときに変更されたリンク先デザインを更新",
"settings.update_changed_links": "タブに戻ったときに変更されたリンクを更新",
"settings.window_system_details": "詳細",
"settings.window_system_note_now": "再起動後に適用されます。現在: {now}。",
"settings.window_system_wayland_limited": "ネイティブ Wayland では一部の機能が制限されます。",
@@ -4360,7 +4565,7 @@
"ui.agent_chat_send_now_tip": "エージェントの現在のステップを止めて、これを次に送信します(Ctrl+Enter)",
"ui.agent_chat_setup_body": "SolidCraft は、このコンピューターにインストールされたコーディングエージェントと連携します。「環境設定 › AI」で選択してください。",
"ui.agent_chat_setup_title": "アシスタントを設定",
"ui.agent_chat_side": "エージェントチャットの位置",
"ui.agent_chat_side": "チャットをドッキング",
"ui.agent_chat_signed_in": "サインイン済み",
"ui.agent_chat_state_auth": "サインインが必要",
"ui.agent_chat_state_busy": "作業中…",
@@ -4379,7 +4584,7 @@
"ui.agent_chat_undone": "(元に戻しました)",
"ui.agent_chat_view_attached": "ビューを添付済み",
"ui.agent_chat_waiting": "確認待ち",
"ui.agent_cursor": "エージェントカーソル",
"ui.agent_cursor": "エージェントのポインター",
"ui.agent_cursor_speed": "エージェントカーソル速度",
"ui.agent_off_thread": "エージェントのコマンド実行中もウィンドウを操作可能にする",
"ui.agent_off_thread_note": "エージェント(AI パネルまたは MCP クライアント)の時間のかかるコマンドをウィンドウとは別に計算します。実行中もビューの回転や確認ができ、ビュー内の表示から停止できます。オフ:各コマンドはウィンドウのスレッドで実行され、終わるまでウィンドウは待ちます。",
@@ -4425,14 +4630,14 @@
"ui.ai_key_note": "システムのキーチェーンに保存され、SolidCraft の設定には保存されません。ローカルサーバーの場合は空欄のままにします。",
"ui.ai_keychain_error": "システムのキーチェーンが拒否しました: {error}",
"ui.ai_kind": "種類",
"ui.ai_mode_all": "すべて許可",
"ui.ai_mode_all": "すべて実行",
"ui.ai_mode_all_note": "確認なしで実行します。クラッシュレポートは引き続き対象外で、フォルダー制限も有効です。",
"ui.ai_mode_all_warning": "エージェントは確認なしでデザインの保存・上書き・エクスポート・置き換えができます。信頼できるエージェントで、保存済みのデザインに対してのみ使ってください。",
"ui.ai_mode_ask": "毎回確認",
"ui.ai_mode_ask": "先に確認",
"ui.ai_mode_ask_note": "デザインへのすべての変更とファイル操作が、あなたの許可を待ちます。",
"ui.ai_mode_edits": "モデル編集を自動許可",
"ui.ai_mode_edits": "編集を許可",
"ui.ai_mode_edits_note": "モデリングコマンドは確認なしで実行されます。保存・開く・エクスポートなどのファイル操作や破壊的な操作は引き続き確認します。",
"ui.ai_mode_global": "上の設定に従う",
"ui.ai_mode_global": "上の設定と同じ",
"ui.ai_model": "モデル",
"ui.ai_model_hint": "qwen3:14b",
"ui.ai_modes_scope": "これらの設定は SolidCraft のツールに適用されます。エージェント自身のツール(シェル、ファイル編集)は、エージェントにモードがある場合にチャットパネルのヘッダーで選ぶエージェント自身のモードに従います。",
@@ -4458,7 +4663,7 @@
"ui.ai_save": "保存",
"ui.ai_settings": "AI 設定…",
"ui.ai_show_features": "AI 機能を表示",
"ui.ai_show_features_note": "エージェントチャット: 普段使っているコーディングエージェント、または選んだモデルのエンドポイントが、開いているデザインを操作するパネルです。オフ: AI ボタン、ショートカット、メニュー項目、パネルは表示されず、検索も起動も行いません。",
"ui.ai_show_features_note": "オフにするとアシスタントをすべて非表示にし、何も起動しません。以下の項目はすべて無効表示になります。\n\nエージェントチャット: 普段使っているコーディングエージェント、または選んだモデルのエンドポイントが、開いているデザインを操作するパネルです。オフ: AI ボタン、ショートカット、メニュー項目、パネルは表示されず、検索も起動も行いません。",
"ui.ai_switch_tip": "この会話のエージェントを切り替え",
"ui.ai_test": "テスト",
"ui.ai_test_acp_ok": "応答あり: {agent}",
@@ -4492,7 +4697,7 @@
"ui.ai_tool_sum_then": "{first}、続いて {next}",
"ui.ai_tool_unfold": "クリックして結果を表示",
"ui.ai_tool_waiting": "待機中",
"ui.ai_toolbar_button": "アプリケーションバーにアシスタントボタンを表示",
"ui.ai_toolbar_button": "アプリケーションバーのアシスタントボタン",
"ui.ai_toolbar_button_note": "アプリケーションバーのアシスタントボタン。Ctrl+Shift+A と表示メニューからもパネルを開けます。",
"ui.ai_undo_tip": "このメッセージでエージェントが行った変更を、1回でまとめて元に戻します",
"ui.ai_url_hint": "https://example.com/v1",
@@ -4639,6 +4844,7 @@
"ui.default_modelling_orientation": "モデリングのデフォルト方向",
"ui.delete": "削除",
"ui.delete_select_first": "先に削除するものを選択してください: フィーチャ、ボディ、コンポーネント、スケッチ ジオメトリ",
"ui.dependents_select_first": "先に、依存先を表示する対象を選択してください",
"ui.depth": "深さ",
"ui.design": "デザイン",
"ui.design_properties.author": "作成者",
@@ -4764,7 +4970,7 @@
"ui.extract_done": "コンポーネントは独立したデザインになり、ここにリンクされました。",
"ui.extrude": "押し出し",
"ui.extrude_auto_to_object": "選択した対象まで押し出し",
"ui.extrude_auto_to_object_tip": "押し出しでプロファイルを選択した後、その平面外の面・頂点・平行な平面を選ぶと、範囲タイプが「オブジェクトまで」に切り替わります",
"ui.extrude_auto_to_object_tip": "面、頂点、平行な平面を選ぶと範囲が「オブジェクトまで」に切り替わります。",
"ui.factor": "倍率",
"ui.fade": "切り上げ",
"ui.fade_length": "切り上げ長さ",
@@ -4796,6 +5002,10 @@
"ui.fillet_setback_least": "{least} より大きく",
"ui.fillet_setback_tip": "なめらかなコーナーパッチが始まる位置: コーナーから各エッジに沿った距離。既定値は半径の {factor} 倍。最小セットバック (セットバックなしでラウンド同士が交わる位置。面どうしが直角なら半径) より大きくする必要があります。",
"ui.fillet_smooth_radius": "半径をなめらかに変化",
"ui.fillet_turn": "曲がり角",
"ui.fillet_turn_mitre": "留め継ぎ",
"ui.fillet_turn_swept": "回転",
"ui.fillet_turn_tip": "2 つのフィレットが間にある鋭いコーナーエッジを回り込む所(ボスの根元のフィレットが立ち上がりエッジを回る所など)。\n留め継ぎ: フィレットは延びて留め継ぎで出会います。\n回転: フィレットはコーナーエッジで止まり、その周りを回ります。コーナーは両方に接するトーラスです。同じ半径のフィレットが、まっすぐな凸のコーナーエッジを回る場合のみ。",
"ui.fillet_type_choices_tip": "一定の半径: エッジ全体で一つの半径。\n弦長: 面がどんな角度で交わっても、フィレットの幅が同じ。\n可変半径: 両端の半径と、追加すればエッジ沿いの半径。\n曲率連続: 平面と接する所で映り込みが折れない断面 (G2)。\nスプライン: エッジ沿いの半径が、設定した制御半径に従います。",
"ui.fillet_type_curvature": "曲率連続",
"ui.fillet_type_tip": "エッジを横切る断面。曲率連続: 平面の間の曲率連続 (G2) 断面で、映り込みが面からフィレットへ折れずにつながります。",
@@ -4805,6 +5015,7 @@
"ui.fine": "細かい",
"ui.finest": "最も細かい",
"ui.finish_sketch": "スケッチを終了",
"ui.first_angle": "第一角法",
"ui.fit": "フィット",
"ui.fix_unfix": "固定/固定解除",
"ui.flat": "展開",
@@ -4908,7 +5119,7 @@
"ui.hidePlanes": "平面を非表示",
"ui.hideProfile": "プロファイルを非表示",
"ui.hide_component_history": "このコンポーネントの履歴を非表示",
"ui.hide_origin_by_default": "原点を既定で非表示",
"ui.hide_origin_by_default": "新規デザインで原点フォルダーを非表示",
"ui.hide_origin_note": "新しいデザインは原点フォルダーを非表示にして始まります。原点の表示/非表示を切り替えたデザインは、その設定を保持します。",
"ui.history_back": "1ステップ戻る",
"ui.history_below_browser": "ブラウザーの下",
@@ -5068,7 +5279,7 @@
"ui.pitch": "ピッチ",
"ui.placement": "配置",
"ui.placement_handles": "配置ハンドル",
"ui.placement_handles_tip": "レスト、ボス、スナップ フィットのプレビューに表示されるハンドル: マーカーをドラッグしてモデル上を移動し、リングで回転します",
"ui.placement_handles_tip": "モデル上でフィーチャーをドラッグするマーカーと、回転させるリング。",
"ui.plane": "平面",
"ui.plane_at_angle": "傾斜平面",
"ui.plugins": "プラグイン",
@@ -5109,6 +5320,7 @@
"ui.profiles": "プロファイル",
"ui.project": "投影",
"ui.project_face_edges_sketch": "新規スケッチに面のエッジを自動投影",
"ui.projection_of_new_drawings": "新規図面の投影法",
"ui.properties": "プロパティ",
"ui.properties.copy_matrix": "行列をコピー",
"ui.properties.first_moments": "一次モーメント",
@@ -5266,8 +5478,8 @@
"ui.start": "開始",
"ui.start_over": "最初からやり直す",
"ui.start_page": "スタートページ",
"ui.step_values": "矢印キーとホイールで数値を増減",
"ui.step_values_tip": "入力中の値フィールドでは、上下矢印キーとフィールド上のマウスホイールで数値を 1 ずつ増減します(Shift: 10、Alt: 0.1)。式はそのまま残ります。",
"ui.step_values": "矢印キーとホイールで値を変更",
"ui.step_values_tip": "値入力欄の上で上下矢印キーやホイールを使うと数値が変わります。",
"ui.stitch_tolerance_hint": "厳密",
"ui.stretch": "伸縮",
"ui.supersampling": "スーパーサンプリング (SSAA)",
@@ -5294,6 +5506,7 @@
"ui.text": "テキスト",
"ui.theme": "テーマ",
"ui.thickness": "厚さ",
"ui.third_angle": "第三角法",
"ui.thread": "ねじ",
"ui.tie_ends": "拘束",
"ui.tie_ends_tip": "未拘束をすべて拘束",
File diff suppressed because it is too large Load Diff
+237 -30
View File
@@ -325,6 +325,7 @@
"Move Type": "이동 유형",
"Move down": "아래로 이동",
"Move earlier": "앞으로 이동",
"Move holes by clicking or dragging": "클릭 또는 드래그로 이동",
"Move later": "뒤로 이동",
"Move up": "위로 이동",
"Name": "Name",
@@ -505,7 +506,7 @@
"Show Agent Cursor": "에이전트 커서 표시",
"Show in folder": "폴더에서 보기",
"Show the pointer an AI agent moves (#35)": "AI 에이전트가 움직이는 포인터 표시 (#35)",
"Show tool at cursor": "커서 옆에 도구 표시",
"Show tool at cursor": "커서에 도구 표시",
"Signed axes": "부호 있는 축",
"Size": "크기",
"Sketch constraints": "스케치 구속 조건",
@@ -516,6 +517,7 @@
"Sketch points": "스케치 점",
"Slider": "슬라이더",
"Slot width": "슬롯 폭",
"Snap to face and edge centres": "중심에 스냅",
"SolidCraft closed before these designs were saved. Their last autosave can be opened.": "이 디자인들이 저장되기 전에 SolidCraft가 종료되었습니다. 마지막 자동 저장본을 열 수 있습니다.",
"Spacing 2 must be a non-zero number": "간격 2는 0이 아닌 숫자여야 합니다",
"Spacing must be a non-zero number": "간격은 0이 아닌 숫자여야 합니다",
@@ -641,26 +643,32 @@
"a picked edge is gone": "선택한 모서리가 더 이상 없습니다",
"a picked edge's body is gone": "선택한 모서리의 바디가 더 이상 없습니다",
"add a key for a joint first": "먼저 관절의 키를 추가하세요",
"ai.prefs.availability": "어시스턴트 제어",
"ai.prefs.background_commands": "에이전트 명령을 백그라운드에서 실행",
"ai.prefs.follow_camera": "카메라를 스케치로 돌리기",
"ai.prefs.allow_control": "AI 에이전트 연결 허용",
"ai.prefs.allow_control_note": "로컬 채널(MCP)입니다. 이 컴퓨터 밖으로 아무것도 나가지 않습니다.\n\n이 컴퓨터에서만 제어 채널을 엽니다(127.0.0.1, 포트 27878 또는 다음 빈 포트). 연결하는 프로그램은 시작할 때마다 새로 생성되며 현재 사용자 계정만 읽을 수 있는 비밀 토큰을 제시해야 합니다. SolidCraft를 다음에 시작할 때 적용됩니다. 끄면 --control로 실행하지 않는 한 아무것도 수신하지 않습니다.",
"ai.prefs.availability": "사용 설정",
"ai.prefs.background_commands": "느린 명령을 백그라운드에서 실행",
"ai.prefs.connect_apps": "AI 앱에 SolidCraft 추가",
"ai.prefs.connect_apps_note": "끄면 추가한 항목이 다시 제거됩니다.\n\nSolidCraft 시작 시 이 컴퓨터에서 찾은 AI 앱(OpenCode, Claude Desktop, Claude Code, Cursor, Codex, Gemini CLI, Copilot CLI, Windsurf, Zed, VS Code)에 MCP 서버로 추가합니다. solidcraft 항목만 추가하거나 경로를 갱신하며, 사용자가 변경한 항목은 그대로 두고 파일을 먼저 백업합니다. 끄면 추가했던 항목을 제거합니다. 도움말 > AI 어시스턴트 연결…에서 수동으로 실행할 수도 있습니다.",
"ai.prefs.connect_at_start": "시작할 때 마지막 에이전트 연결",
"ai.prefs.follow_camera": "카메라 따라가기",
"ai.prefs.follow_camera_off": "카메라 자동 따라가기가 꺼져 있습니다.",
"ai.prefs.follow_camera_on": "에이전트가 만드는 스케치를 향해 카메라가 회전합니다.",
"ai.prefs.intro": "에이전트, 권한, 그리고 작업이 표시되는 방식.",
"ai.prefs.make_default": "기본값으로 사용",
"ai.prefs.intro": "SolidCraft의 AI 에이전트.",
"ai.prefs.make_default": "기본값으로 설정",
"ai.prefs.no_agents": "아직 에이전트가 없습니다. 에이전트를 추가하거나 새로 고쳐서 설치된 에이전트를 찾으세요.",
"ai.prefs.no_saved_approvals": "저장된 승인이 없습니다.",
"ai.prefs.open_at_start": "시작할 때 채팅 열기",
"ai.prefs.override_all_warning": "이 에이전트는 묻지 않고 설계를 저장, 덮어쓰기, 내보내기 또는 교체할 수 있습니다.",
"ai.prefs.permission_all": "모두 허용",
"ai.prefs.permission_all_summary": "사용 가능한 SolidCraft 도구가 묻지 않고 실행됩니다.",
"ai.prefs.permission_ask": "변경 전에 묻기",
"ai.prefs.permission_ask_summary": "모든 설계 변경과 파일 작업을 승인합니다.",
"ai.prefs.permission_edits": "모델 편집 허용",
"ai.prefs.permission_edits_summary": "모델 편집은 실행되고, 파일 및 파괴적인 작업은 계속 묻습니다.",
"ai.prefs.permission_all": "모두 실행",
"ai.prefs.permission_all_summary": "묻지 않음",
"ai.prefs.permission_ask": "먼저 묻기",
"ai.prefs.permission_ask_summary": "모든 변경 전에 확인",
"ai.prefs.permission_edits": "편집 허용",
"ai.prefs.permission_edits_summary": "자유롭게 편집하고 나머지는 확인",
"ai.prefs.permission_scope": "SolidCraft 도구에만 적용됩니다. 에이전트의 셸과 파일 편집은 자체 권한을 사용합니다.",
"ai.prefs.pointer_preview_hidden": "포인터 숨김",
"ai.prefs.pointer_preview_speed": "에이전트 포인터 · {speed}",
"ai.prefs.pointer_speed": "동작 속도",
"ai.prefs.pointer_speed": "속도",
"ai.prefs.privacy_note": "선택한 에이전트 또는 엔드포인트만 대화를 받습니다. 엔드포인트 키는 시스템 키체인에 저장됩니다.",
"ai.prefs.show_pointer": "에이전트 포인터 표시",
"ai.prefs.turn_limit_hint": "메시지 하나에 어시스턴트가 수행할 수 있는 설계 변경 도구 호출의 최대 횟수입니다.",
@@ -716,6 +724,8 @@
"cmd.appearance_pick_up": "모양 가져오기",
"cmd.appearance_replace": "모양 바꾸기",
"cmd.assembly_bom": "자재 명세서",
"cmd.assembly_bom_item_set": "항목 번호 변경",
"cmd.assembly_bom_renumber": "항목 번호 다시 매기기",
"cmd.assembly_mass_properties": "질량 특성",
"cmd.body.print_face": "출력 바닥면",
"cmd.body_rename": "바디 이름 변경",
@@ -856,6 +866,11 @@
"cmd.joint_screw": "나사",
"cmd.joint_solve": "관절 해석",
"cmd.joint_sweep": "관절 스윕",
"cmd.layout.apply": "레이아웃 적용",
"cmd.layout.get": "레이아웃 확인",
"cmd.layout.reset": "레이아웃 초기화",
"cmd.layout.save": "레이아웃 저장",
"cmd.layout.workspace.select": "레이아웃 작업 공간 선택",
"cmd.library_apply": "로컬 라이브러리 항목 적용",
"cmd.library_export": "설계 라이브러리 내보내기",
"cmd.library_inspect": "로컬 라이브러리 확인",
@@ -881,10 +896,26 @@
"cmd.motion_study": "운동 연구",
"cmd.motion_trace": "모션 궤적",
"cmd.motion_trace_keep": "모션 궤적 유지",
"cmd.occurrence_bom_type": "자재 명세서 유형",
"cmd.occurrence_copy": "붙여넣기(인스턴스)",
"cmd.occurrence_ground": "고정",
"cmd.occurrence_move": "인스턴스 이동",
"cmd.occurrence_properties": "인스턴스 물리 속성",
"cmd.panel.activate": "패널 탭 활성화",
"cmd.panel.close": "패널 닫기",
"cmd.panel.collapse": "패널 구역 접기",
"cmd.panel.dismiss": "패널 임시 보기 닫기",
"cmd.panel.float": "패널 띄우기",
"cmd.panel.focus": "패널에 초점 맞추기",
"cmd.panel.list": "패널 목록",
"cmd.panel.move": "패널 이동",
"cmd.panel.native_close": "별도 창의 패널 구역 다시 도킹",
"cmd.panel.open": "패널 열기",
"cmd.panel.peek": "접힌 패널 임시 보기",
"cmd.panel.popout": "패널을 별도 창으로 이동",
"cmd.panel.reset_pair": "패널 구분선 초기화",
"cmd.panel.resize": "패널 구역 크기 조정",
"cmd.panel.resize_pair": "패널 구분선 이동",
"cmd.parameters_add": "사용자 매개 변수 추가",
"cmd.parameters_change": "매개 변수 변경",
"cmd.parameters_comment": "매개 변수에 설명 추가",
@@ -1179,6 +1210,9 @@
"dialog.custom_axis": "↖ 사용자 정의 축",
"dialog.dimension_insert_hint": "다른 구동 치수를 두 번 클릭하여 이름 삽입",
"dialog.dimension_name": "치수: {name}",
"dialog.dimension_step_down": "{step} 줄이기",
"dialog.dimension_step_expression": "숫자만 증감할 수 있으며 수식은 입력한 그대로 유지됩니다",
"dialog.dimension_step_up": "{step} 늘리기",
"dialog.edit_title": "{title} 편집",
"dialog.pattern_sketch_not_object": "바디나 피처를 선택하세요. 스케치는 둘 다 아닙니다. 곡선은 스케치 안의 패턴이나 대칭 복사로 반복하거나, 먼저 돌출하세요.",
"dialog.select": "↖ 선택",
@@ -1239,9 +1273,29 @@
"help.construct.plane.axis_point": "축과 축 위에 있지 않은 점을 포함하는 구성 평면을 만듭니다. 원점 축, 구성 축, 직선 모서리 또는 스케치 직선을 선택한 다음 꼭짓점, 스케치 점 또는 구성점을 선택합니다. 축 위의 점으로는 평면을 하나로 결정할 수 없습니다.",
"help.font_details": "인터페이스는 egui의 내장 글꼴을 사용합니다(Ubuntu Font Licence 및 MIT/Bitstream Vera 라이선스의 Ubuntu Light와 Hack, OFL 및 MIT의 Noto Emoji와 이모지 아이콘 글꼴). 스케치 텍스트는 Liberation Sans(SIL OFL 1.1)를 사용합니다. The Crafting Apps의 선택적 글꼴 팩 craft-fonts(BIZ UDPGothic, Shippori Mincho, BIZ UDMincho, Noto Sans CJK SC, Noto Sans Arabic; SIL OFL 1.1)는 SolidCraft 빌드에 포함되지 않습니다.",
"help.fonts": "글꼴",
"help.layout.apply": "저장된 레이아웃을 현재 작업 공간에 적용합니다. 유지 중인 편집기의 초안은 보존됩니다.",
"help.layout.get": "확정된 레이아웃과 등록된 작업 공간을 확인합니다.",
"help.layout.reset": "현재 작업 공간에 등록된 기본 레이아웃을 복원합니다. 사용자 사전 설정은 유지됩니다.",
"help.layout.save": "전체 레이아웃에 이름을 지정해 저장합니다. 사용자 사전 설정을 바꾸려면 명시적인 덮어쓰기 허용이 필요합니다.",
"help.layout.workspace.select": "등록된 레이아웃 작업 공간을 선택하고 이전 레이아웃을 기억합니다.",
"help.licences": "라이선스",
"help.mit": "MIT 라이선스",
"help.notice": "NOTICE",
"help.panel.activate": "키보드 초점을 요청하지 않고 열린 패널 탭을 선택합니다.",
"help.panel.close": "콘텐츠 수명 주기 검사를 거쳐 패널을 닫습니다. 배치는 기억됩니다.",
"help.panel.collapse": "요청한 치수를 유지하면서 패널 구역을 접거나 펼칩니다.",
"help.panel.dismiss": "접힌 패널의 임시 보기를 닫습니다.",
"help.panel.float": "패널을 위치가 명시된 부동 구역으로 이동합니다.",
"help.panel.focus": "패널에 초점을 맞춥니다. 필요하면 유지된 상태를 다시 열고 구역을 펼칩니다.",
"help.panel.list": "등록된 패널 종류, 확정된 인스턴스, 활성 탭, 사용 가능 여부와 호스트 기능을 확인합니다.",
"help.panel.move": "패널을 새 구역으로 이동하거나 기존 탭 그룹에 추가합니다.",
"help.panel.native_close": "콘텐츠를 취소하지 않고 별도 창의 패널 구역을 기억된 도킹 위치로 되돌립니다.",
"help.panel.open": "패널 종류와 명시적인 인스턴스 식별자로 패널을 엽니다. 새 인스턴스에는 배치가 필요합니다.",
"help.panel.peek": "저장된 크기나 접힘 상태를 바꾸지 않고 접힌 패널을 임시로 표시합니다.",
"help.panel.popout": "지원되는 경우 패널을 별도 창으로 이동합니다. 브라우저에서는 부동 구역을 사용합니다.",
"help.panel.reset_pair": "인접한 도킹 구역 쌍을 공통 제약 안에서 기본 크기 비율로 복원합니다.",
"help.panel.resize": "모든 탭의 제약 안에서 패널 구역의 바깥 너비와 높이를 설정합니다.",
"help.panel.resize_pair": "공유하는 전체 길이를 유지하면서 인접한 도킹 구역의 크기를 함께 조정합니다.",
"help.pointcloud.insert": "PLY, XYZ 또는 PTS 스캔을 모델 옆에 포인트 클라우드로 삽입합니다. 측정 데이터를 참고하여 모델링할 때 사용합니다.",
"help.sketch.construction_line": "두 점을 지나는 무한 보조 직선을 만듭니다. 스케치 프로파일을 나누지 않고 작도할 수 있습니다.",
"help.sketch.construction_line.angle": "스케치 X축을 기준으로 지정한 각도의 무한 보조 직선을 만듭니다. 점을 선택한 뒤 각도를 입력하거나 방향을 선택합니다.",
@@ -1360,8 +1414,13 @@
"joint.from_bodies_hint": "선택한 각 바디가 제자리에 그대로 있으면서 자체의 새 구성 요소로 옮겨집니다.",
"joint.one_body": "두 선택이 모두 {body}에 있습니다. 두 번째는 다른 부품에서 선택하세요.",
"joint.one_component": "이 부품들은 한 구성 요소의 바디입니다: {bodies}. 관절은 한 구성 요소를 다른 구성 요소에 대해 움직이므로 부품마다 자체 구성 요소가 필요합니다.",
"joint_move.coarse": "축 근처 단계",
"joint_move.conflict": "이 부품의 조인트가 다른 조인트와 충돌합니다: {joint}.",
"joint_move.conflict_hint": "충돌하는 조인트를 편집, 비활성화 또는 삭제하여 다른 조인트가 부품을 배치할 수 있게 하세요.",
"joint_move.fine": "축에서 먼 단계",
"joint_move.legacy_hint": "끄면 이전 이동 패널을 사용합니다.",
"joint_move.precision": "미세 단계를 사용하려면 점선 가이드 바깥으로 당기세요.",
"joint_move.use_handles": "이동에 조인트 모션 핸들 사용",
"keymap.command_id": "명령 ID",
"keymap.empty": "일치하는 명령이 없습니다",
"keymap.use_anyway": "그래도 여기서 {key} 사용",
@@ -1378,6 +1437,7 @@
"material.selection.mixed": "서로 다른 재질",
"measure.at": "위치",
"measure.item": "항목 {number}",
"measure.line_to_centre": "선에서 중심까지",
"messages.ago_h": "{n}시간 전",
"messages.ago_min": "{n}분 전",
"messages.ago_s": "{n}초 전",
@@ -1431,6 +1491,12 @@
"navbar.assistant": "어시스턴트",
"navbar.custom.add_slot": "{slot} 추가",
"navbar.custom.always": "항상 표시",
"navbar.custom.anchor.bottom_centre": "하단 가운데",
"navbar.custom.anchor.bottom_left": "왼쪽 아래",
"navbar.custom.anchor.bottom_right": "오른쪽 아래",
"navbar.custom.anchor.top_centre": "상단 가운데",
"navbar.custom.anchor.top_left": "왼쪽 위",
"navbar.custom.anchor.top_right": "오른쪽 위",
"navbar.custom.available": "{available} · 막대로 끌어 놓거나 추가를 누르세요",
"navbar.custom.buttons": "버튼",
"navbar.custom.compact": "컴팩트",
@@ -1464,6 +1530,7 @@
"navbar.custom.reset_default": "기본값으로 재설정",
"navbar.custom.right": "오른쪽으로 이동",
"navbar.custom.select": "선택한 칸이 없습니다. 막대에서 칸을 선택하면 이동하거나 제거하거나 모양을 바꿀 수 있습니다.",
"navbar.custom.separator_hint": "버튼 그룹 사이의 구분선입니다. 막대로 끌거나 추가를 누르세요. 원하는 만큼 사용할 수 있습니다.",
"navbar.custom.separators_full": "막대에 구분선이 이미 네 개 있습니다.",
"navbar.custom.show": "탐색 막대 표시",
"navbar.custom.shows": "표시 내용",
@@ -1586,19 +1653,36 @@
"prefs.appearance.viewport_logo": "뷰포트 및 로고",
"prefs.applies_on_restart": "다시 시작하면 적용됩니다",
"prefs.assistant": "어시스턴트",
"prefs.assistant_connecting": "연결",
"prefs.assistant_follow_help": "스케치의 Look At 동작이 포인터와 함께 보기를 이동합니다.",
"prefs.assistant_limit": "제한",
"prefs.assistant_limited": "제한됨",
"prefs.assistant_pointer_speed": "포인터 속도",
"prefs.assistant_seconds": "초",
"prefs.assistant_seconds_aria": "초",
"prefs.assistant_use_care": "주의해서 사용",
"prefs.auto_operation_note": "재료가 닿는 대상에 따라 결합, 새 바디 또는 자르기를 선택합니다.",
"prefs.axes": "축",
"prefs.axis": "축",
"prefs.blur_cost": "상대적인 렌더링 비용입니다. 막대가 많을수록 일반적으로 더 많은 작업이 필요합니다.",
"prefs.blur_device": "이 그래픽 장치 또는 렌더링 경로에서는 사용할 수 없습니다",
"prefs.blur_guard": "화면의 원활한 응답을 위해 흐림 품질을 낮췄습니다. 반경은 변경되지 않습니다.",
"prefs.blur_off": "끄기",
"prefs.blur_reduce_transparency": "시스템의 투명도 줄이기 설정에 의해 비활성화됨",
"prefs.blur_software": "소프트웨어 그래픽에서는 사용할 수 없습니다 (gpu-software)",
"prefs.blur_source": "이 작업 공간 보기에서는 사용할 수 없습니다",
"prefs.blur_web": "이 웹 렌더링 경로에서는 사용할 수 없습니다",
"prefs.bodies_folders": "바디 폴더",
"prefs.bodies_folders_always": "항상 표시",
"prefs.bodies_folders_never": "표시 안 함",
"prefs.bodies_folders_several": "바디가 여러 개일 때만",
"prefs.bodies_folders_several": "여러 개",
"prefs.bottom": "아래쪽",
"prefs.bottom_centre": "하단 가운데",
"prefs.bottom_right": "오른쪽 아래",
"prefs.browser": "브라우저",
"prefs.browser_components_open": "구성요소를 펼친 상태로 시작",
"prefs.browser_components_open_note": "브라우저의 모든 구성요소가 이전처럼 펼쳐진 상태로 시작합니다. 끄면 직접 펼칠 때까지 접혀 있습니다.",
"prefs.browser_eye_left": "표시 아이콘을 접기 화살표 옆에 표시",
"prefs.browser_eye_left": "접기 화살표 옆의 눈 아이콘",
"prefs.browser_eye_left_note": "브라우저 행의 표시 아이콘을 행 오른쪽 끝의 열이 아니라 이전처럼 접기 화살표 옆에 표시합니다.",
"prefs.built_in": "기본 제공",
"prefs.button_left": "왼쪽 드래그",
@@ -1624,15 +1708,34 @@
"prefs.dark_parts_shading_help": "선형 조명과 부드러운 하늘광으로 뷰를 음영 처리하여 어두운 면이 검게 뭉개지지 않고 그라데이션을 유지합니다",
"prefs.dead_zone_note": "빗금 친 영역이 데드존입니다. 그 안의 움직임은 무시됩니다.",
"prefs.default": "기본값",
"prefs.design": "설계 기본값",
"prefs.design_note": "새 설계가 시작할 때의 설정입니다. 열려 있는 설계는 자체 설정을 유지합니다.",
"prefs.design": "디자인",
"prefs.design_note": "새 디자인의 시작 설정입니다.",
"prefs.design_tools.ask": "묻기",
"prefs.design_tools.degrees": "도",
"prefs.design_tools.double_click_help": "연결된 구성요소를 제자리에서 편집할 수도 있습니다.",
"prefs.design_tools.ghost": "이동한 부품의 이전 위치를 고스트로 표시",
"prefs.design_tools.hole_moves_help": "면을 다시 선택하면 구멍을 제거하지 않고 이동합니다.",
"prefs.design_tools.hole_snaps_help": "면의 가운데와 원형 모서리의 중심입니다.",
"prefs.design_tools.imported_files": "가져온 파일",
"prefs.design_tools.note": "알림",
"prefs.design_tools.off": "끄기",
"prefs.design_tools.placement_group": "레스트, 보스, 스냅 핏",
"prefs.design_tools.rows": "행",
"prefs.devices": "3D 마우스",
"prefs.dialog_buttons": "대화 상자 버튼",
"prefs.dialogs": "대화 상자",
"prefs.dialogs_edge": "뷰 가장자리",
"prefs.dialogs_left_column": "왼쪽 열",
"prefs.dialogs_note": "명령 대화 상자가 열리는 위치: 뷰 가장자리에 고정하거나, 왼쪽 열에서 브라우저와 이력 대신 표시합니다. 대화 상자를 닫으면 브라우저와 이력이 그대로 돌아옵니다.",
"prefs.dimension_step_angle": "창의 화살표, 위/아래 키와 휠이 각도에 더하는 값",
"prefs.dimension_step_length": "창의 화살표, 위/아래 키와 휠이 길이에 더하는 값",
"prefs.dimension_steps": "창의 증감 단위",
"prefs.dimension_values": "치수 값",
"prefs.dimension_values_note": "치수 값을 입력하는 위치입니다. 창에는 값을 증감하는 화살표가 있으며, 치수 도구는 텍스트를 먼저 배치합니다.",
"prefs.dimension_values_view": "뷰에서",
"prefs.dimension_values_window": "창에서",
"prefs.display": "표시",
"prefs.drawings": "도면",
"prefs.edges": "모서리 및 음영",
"prefs.extend": "확장",
"prefs.extra_navigation": "Ctrl+왼쪽 드래그로 빈 공간에서 회전하고, Space를 누른 채 이동합니다.",
@@ -1660,6 +1763,23 @@
"prefs.gesture_zoom": "확대/축소",
"prefs.graphics": "그래픽",
"prefs.graphics_note": "속도와 부드러움 중에서 선택합니다. 각 선택의 효과가 샘플에 표시됩니다.",
"prefs.graphics_page.current_view": "현재 뷰",
"prefs.graphics_page.fastest": "가장 빠르게",
"prefs.graphics_page.fold_items": "항목 접기",
"prefs.graphics_page.in_view": "뷰 내부",
"prefs.graphics_page.live_details": "실시간 세부 정보",
"prefs.graphics_page.own_mix": "사용자 조합",
"prefs.graphics_page.quick_measure": "빠른 측정",
"prefs.graphics_page.reference_geometry_through_bodies": "바디를 통한 참조 형상",
"prefs.graphics_page.restart": "다시 시작 필요",
"prefs.graphics_page.sharpest": "가장 선명하게",
"prefs.graphics_page.single_surface_analysis": "단일 표면 분석",
"prefs.graphics_page.status_chips": "상태 칩",
"prefs.graphics_page.subtitle": "속도와 부드러움의 균형을 조절합니다.",
"prefs.graphics_page.supersampling": "슈퍼샘플링",
"prefs.graphics_page.supersampling_adaptive": "뷰가 멈춰 있는 동안만",
"prefs.graphics_page.surfaces_edges": "곡면과 모서리",
"prefs.graphics_page.view_bar": "뷰 표시줄에서도 사용",
"prefs.graphics_presets_note": "프리셋은 아래의 선택 항목을 한 번에 설정합니다.",
"prefs.hide_constraint_glyphs_zoomed_out": "축소 시 구속 조건 기호 숨기기",
"prefs.hide_constraint_glyphs_zoomed_out_note": "스케치를 충분히 축소하면 구속 조건 기호가 가려지지 않도록 숨깁니다. 끄기: 확대/축소와 관계없이 고정된 화면 크기를 유지합니다.",
@@ -1668,6 +1788,7 @@
"prefs.history_origin_bodies_note": "왼쪽 또는 브라우저 아래의 히스토리가 활성 구성 요소의 바디 폴더와 원점으로 시작하며, 브라우저처럼 표시 아이콘과 메뉴가 있습니다. 아래쪽 히스토리는 바뀌지 않습니다.",
"prefs.history_play_button": "기록 컨트롤에 기록 재생 버튼 표시",
"prefs.history_play_button_note": "기록 컨트롤의 한 단계 뒤로와 한 단계 앞으로 사이에 기록 재생을 추가합니다. 컨트롤을 마우스 오른쪽 버튼으로 클릭해 표시하거나 숨길 수도 있습니다.",
"prefs.history_sketches": "히스토리의 스케치",
"prefs.hold_stop_snapping": "누르고 있으면 스냅 중지",
"prefs.home_fit_size": "홈·맞춤 크기",
"prefs.home_fit_size_note": "홈, 맞춤, 연 디자인 또는 뷰 탐색기의 뷰 변경으로 모델을 화면에 맞출 때 모델이 뷰에서 차지하는 비율. 아래 설정과 관계없이 적용됩니다. 100 %: 패널, 나침반, 내비게이션 바 사이의 뷰를 가득 채웁니다.",
@@ -1682,6 +1803,22 @@
"prefs.language_system": "시스템",
"prefs.layout": "레이아웃",
"prefs.layout_note": "컴팩트는 패널을 창 가장자리에 고정합니다. 브라우저 반투명은 플로팅에서 사용할 수 있습니다. 어시스턴트 위치는 어시스턴트 페이지에서 바꿀 수 있습니다.",
"prefs.layout_page.active_commands": "활성 명령 강조",
"prefs.layout_page.always": "항상",
"prefs.layout_page.card": "카드",
"prefs.layout_page.column": "열",
"prefs.layout_page.hover": "마우스를 올릴 때",
"prefs.layout_page.icons_labels": "아이콘 + 레이블",
"prefs.layout_page.ok_cancel": "확인과 취소",
"prefs.layout_page.on_hover": "마우스를 올릴 때",
"prefs.layout_page.open": "열기",
"prefs.layout_page.origin_bodies": "원점과 바디를 위에 표시",
"prefs.layout_page.panels": "패널",
"prefs.layout_page.pinned_menus": "고정 명령을 메뉴에서 제외",
"prefs.layout_page.play_history": "히스토리 재생 버튼",
"prefs.layout_page.slot_count": "슬롯 {count} / {max}개",
"prefs.layout_page.view_corner": "뷰 모서리에도 표시",
"prefs.layout_page.window": "창",
"prefs.left": "왼쪽",
"prefs.left_click": "왼쪽 클릭",
"prefs.line_drag_draws_one": "누른 채 드래그하면 선 하나 그리기",
@@ -1703,6 +1840,13 @@
"prefs.mouse_heading": "마우스",
"prefs.mouse_mapping": "마우스 매핑",
"prefs.navigation": "탐색",
"prefs.navigation.axes_signed": "축",
"prefs.navigation.cube_compact": "큐브",
"prefs.navigation.device": "장치",
"prefs.navigation.free": "자유",
"prefs.navigation.orbit_style": "회전 방식",
"prefs.navigation.radial_help": "오른쪽 버튼을 길게 누르거나 튕겨 도구 링을 표시합니다.",
"prefs.navigation.turntable": "턴테이블",
"prefs.navigation_note": "마우스, 트랙패드, 3D 마우스로 뷰를 움직이는 방식입니다.",
"prefs.navigator_fit": "뷰 탐색기로 뷰를 바꾼 뒤 모델 맞춤",
"prefs.navigator_fit_note": "큐브의 면, 모서리, 화살표나 기즈모의 축을 클릭하면 어느 방향에서든 전체가 들어가는 한 거리(경계 구)에서 모델의 중심을 바라봅니다. 켜져 있는 동안 홈 뷰도 같은 확대/축소와 중심을 쓰므로 그 뒤에 고른 뷰는 방향만 바뀝니다. 끄면 뷰가 제자리에서 회전하며 대상과 확대/축소를 유지합니다.",
@@ -1715,9 +1859,14 @@
"prefs.origin_plane_names": "원점 평면 이름",
"prefs.origin_plane_names_note": "브라우저, 대화 상자, 목록에 표시되는 이름입니다. {top}은 XY, {front}는 XZ, {right}는 YZ이며 Z가 위쪽입니다. 명령, 스크립트, 저장된 디자인은 XY, XZ, YZ를 유지합니다.",
"prefs.overall_speed": "전체 속도",
"prefs.parameter_list_rows_tip": "빈 수식 상자에서 아래쪽 화살표로 표시할 매개변수의 최대 개수입니다.",
"prefs.persistent_ribbon_pressed": "활성 리본 명령 강조 표시",
"prefs.persistent_ribbon_pressed_help": "효과가 켜져 있는 동안 리본 명령을 강조 표시합니다. 끄면 이전의 중립적인 모양과 명령 도움말을 유지합니다.",
"prefs.perspective_fov": "시야각",
"prefs.plugins_enabled": "사용",
"prefs.plugins_installed": "설치됨",
"prefs.plugins_intro": "샌드박스 추가 기능.",
"prefs.plugins_show_folder": "플러그인 폴더 표시",
"prefs.preview_choices": "선택 항목 미리보기",
"prefs.preview_tint_new_faces": "미리 보기에서 새 면에 색 입히기",
"prefs.preview_tint_new_faces_help": "바디를 변경하는 피처의 미리 보기에서 추가되는 면을 미리 보기 색으로 그릴 수 있습니다. 끄면 바디가 원래 색으로 그려집니다.",
@@ -1752,8 +1901,43 @@
"prefs.shift_add_ctrl_toggle": "Shift는 추가만, Ctrl / Cmd는 토글",
"prefs.shift_add_note": "켬: Shift는 추가, Ctrl / Cmd는 토글. 끔(기본값): 둘 다 토글.",
"prefs.shortcuts": "단축키",
"prefs.shortcuts_import": "가져오기",
"prefs.shortcuts_intro": "각 명령의 키.",
"prefs.shortcuts_not_assigned": "할당되지 않음",
"prefs.shortcuts_press": "키를 누르세요...",
"prefs.single_surface_analysis": "표면 분석 하나만 사용",
"prefs.single_surface_analysis_help": "끄면 표면 음영 분석과 연속성 분석을 함께 유지할 수 있습니다. 켜면 마지막으로 시작하거나 변경한 분석만 유지됩니다.",
"prefs.sketch.align_points": "다른 스케치 점에 정렬",
"prefs.sketch.align_points_note": "그리는 선을 두 축의 점에 동시에 정렬합니다.",
"prefs.sketch.auto_dimensions": "새 도형에 모든 치수 지정",
"prefs.sketch.auto_dimensions_note": "입력한 값과 도형을 고정하는 모든 치수.",
"prefs.sketch.auto_locate": "스케치 원점에서 배치",
"prefs.sketch.auto_locate_note": "모든 치수 지정 시 적용.",
"prefs.sketch.center_arc": "중심점 호가 커서를 따라감",
"prefs.sketch.click_selects": "클릭으로 선택",
"prefs.sketch.conflict_colour": "충돌 색상",
"prefs.sketch.drawing": "그리기",
"prefs.sketch.exit_keep": "유지",
"prefs.sketch.exit_previous": "이전 뷰로 돌아가기",
"prefs.sketch.exit_unless_rotated": "회전하지 않았다면",
"prefs.sketch.faces_edges": "면과 모서리",
"prefs.sketch.hide_glyphs": "축소 시 기호 숨기기",
"prefs.sketch.marks": "스케치의 표시",
"prefs.sketch.note": "그리기, 스냅 및 선택.",
"prefs.sketch.opening_closing": "열기 및 닫기",
"prefs.sketch.prefer_free_points": "겹치는 곳에서는 이동 가능한 점 우선",
"prefs.sketch.project_options": "투영 옵션을 먼저 묻기",
"prefs.sketch.recent_snap": "두 안내선의 교차점에 스냅",
"prefs.sketch.select_other_hidden": "다른 항목 선택에 숨겨진 형상 포함",
"prefs.sketch.shortcut_cycles_modes": "단축키를 다시 누르면 모드 전환",
"prefs.sketch.shortcut_cycles_modes_tip": "끄면 도구를 다시 시작합니다.",
"prefs.sketch.snapping_guides": "스냅 및 안내선",
"prefs.sketch.state_icons": "상태 아이콘",
"prefs.sketch.tracking_guides": "중심 및 중점 안내선",
"prefs.sketch.tracking_guides_note": "마우스를 올린 중심과 모서리 중점에서 임시 가로 및 세로 안내선을 표시합니다.",
"prefs.sketch.tracking_points": "스케치 점에서도 안내선 표시",
"prefs.sketch.unless_turned_note": "회전하지 않았으면 복귀",
"prefs.sketch.whole_bodies": "전체 바디",
"prefs.sketch_align_points": "그리는 동안 다른 스케치 점에 맞추기",
"prefs.sketch_align_points_note": "그리는 동안 점이 한 스케치 점과 수평으로, 다른 점과 수직으로 동시에 맞춰지고 점선 안내선이 표시됩니다. 클릭하면 두 정렬이 모두 유지됩니다. 끄기: 이전 점에만, 한 번에 한 축씩 맞춥니다.",
"prefs.sketch_conflict_colour": "충돌하는 기하를 충돌 색상으로 표시",
@@ -1779,6 +1963,8 @@
"prefs.source": "소스",
"prefs.speed": "속도",
"prefs.stand_up_note": "STEP 또는 IGES 파일이 Z가 위쪽인 상태로 열린 후 X축 기준 90도 회전을 제안합니다.",
"prefs.stand_up_unknown": "알 수 없는 프로그램의 파일",
"prefs.stand_up_unknown_note": "파일에 알려진 프로그램 이름이 없을 때 제안하는 방법입니다.",
"prefs.status_bar": "상태 표시줄",
"prefs.status_bar_note": "창 아래쪽 가장자리의 얇은 표시줄에 편집 중인 항목, 열려 있는 스케치의 상태(해당 색상으로), 설계 단위가 표시됩니다. 단위를 클릭하면 변경할 수 있습니다. 스케치 상태는 더 이상 뷰 모서리에 표시되지 않습니다.",
"prefs.status_done": "완료",
@@ -1812,6 +1998,10 @@
"prefs.theme_follow_note": "다른 도구가 파일을 다시 쓸 때마다 다시 불러옵니다. 테마를 선택하면 따르기를 중지합니다.",
"prefs.theme_follow_off": "끄기",
"prefs.theme_follow_stop": "따르기 중지",
"prefs.theme_notice_dismiss": "닫기",
"prefs.theme_option_blur": "패널 뒤 흐림",
"prefs.theme_option_blur_help": "반투명 패널 뒤의 흐림을 물리적 픽셀 단위로 설정합니다. 0이면 꺼집니다.",
"prefs.theme_option_invalid": "{option}의 저장된 값이 유효하지 않아 테마 기본값을 사용합니다. 저장된 값은 유지됩니다.",
"prefs.theme_option_opacity": "불투명도",
"prefs.theme_option_opacity_help": "플로팅 패널의 불투명도를 조정합니다. 뷰포트 색상 위에서도 텍스트를 읽을 수 있도록 최솟값이 적용됩니다.",
"prefs.theme_option_tab_colours": "탭 색상",
@@ -1820,6 +2010,7 @@
"prefs.theme_option_tab_fade_ms_help": "탭을 전환할 때 패널 및 뷰포트 색상을 서서히 변경합니다. 0이면 즉시 전환합니다.",
"prefs.theme_options_badge": "옵션",
"prefs.theme_options_more": "더 보기 · {count}",
"prefs.theme_options_newer": "이 테마에는 더 최신 버전의 SolidCraft가 필요합니다. 업그레이드 후 원본 테마를 다시 가져와 옵션 정의를 복구하세요.",
"prefs.theme_options_reset": "초기화",
"prefs.theme_options_unavailable": "이 기기에서는 사용할 수 없음",
"prefs.timeline_half_hour": "−30분",
@@ -1847,8 +2038,8 @@
"prefs.validation.label": "커널 전체 검사",
"prefs.validation.off": "끄기 (실험적)",
"prefs.validation_note": "모든 단계 후: 스윕, 불리언, 블렌드마다 검사합니다.\n피처마다 한 번: 각 피처의 결과를 한 번 검사합니다.\n끄기 (실험적): 검사하지 않습니다.",
"prefs.value_boxes_in_view": "뷰에서 핸들 옆에 값 표시",
"prefs.value_boxes_in_view_tip": "명령의 값(거리, 반경 등)을 모델 위 핸들 옆 상자에 표시합니다. 끄면 값은 명령 창에 입력하며, 선택 후 키보드 입력이 그 창으로 가고 모델을 가리는 것이 없습니다.",
"prefs.value_boxes_in_view": "핸들 옆에 값 표시",
"prefs.value_boxes_in_view_tip": "돌출의 거리, 필렛의 반지름 등을 뷰 안의 상자로 표시합니다.",
"prefs.view": "뷰 동작",
"prefs.view_confirm_corner": "뷰 모서리에 확인 및 취소",
"prefs.view_confirm_corner_note": "대화 상자나 스케치가 열려 있는 동안 뷰 오른쪽 위의 체크와 X 표시가 대화 상자의 확인과 취소와 같은 동작을 하거나 스케치를 마치고 취소합니다. 뷰 탐색기는 그 아래로 내려갑니다.",
@@ -2791,10 +2982,10 @@
"select the sheet edge first": "판금 모서리를 먼저 선택하세요",
"select two components": "구성요소 두 개를 선택하세요",
"settings.display_quality_note": "세밀할수록 곡면과 모서리가 더 매끄럽게 표시되지만 메시 생성 시간이 늘어납니다. 뷰포트만 바뀌며 설계나 내보내기는 바뀌지 않습니다.",
"settings.double_click_components": "구성 요소를 두 번 클릭하여 활성화하거나 연결된 구성 요소를 제자리에서 편집",
"settings.double_click_components": "구성요소를 두 번 클릭하여 활성화",
"settings.frame_rate_note": "뷰의 오른쪽 아래에 초당 프레임 수를 표시합니다.",
"settings.insert_lists_open_designs": "설계 삽입에서 다른 탭에 열린 설계를 먼저 표시",
"settings.measure_axis_distances": "X/Y/Z 거리 표시",
"settings.insert_lists_open_designs": "디자인 삽입에서 열린 디자인을 먼저 표시",
"settings.measure_axis_distances": "측정에서 축 거리 표시",
"settings.measure_axis_distances_help": "측정 대화상자가 열려 있는 동안 전체 거리와 함께 월드 축 방향 치수와 거리를 표시합니다.",
"settings.msaa_applies_on_restart": "안티앨리어싱은 다음에 SolidCraft를 시작할 때 적용됩니다",
"settings.msaa_reset": "{setting}: {requested}×는 이 그래픽 장치에서 지원되지 않아 {reset}×로 변경했습니다(지원: {supported}).",
@@ -2804,6 +2995,7 @@
"settings.reset_intro": "다음 설정은 이 컴퓨터에서 작동하지 않아 SolidCraft를 시작할 수 없게 하므로 변경되었습니다:",
"settings.reset_title": "설정이 변경되었습니다",
"settings.supersampling_note": "뷰를 더 높은 해상도로 그린 뒤 축소합니다. 4K 미만 화면에서 모서리, 선, 그리드가 더 매끄러워집니다. GPU 시간이 듭니다: 1.5×에서 약 2.25배, 2×에서 4배의 픽셀.",
"settings.update_changed_links": "탭으로 돌아올 때 변경된 링크 업데이트",
"sheet.flange_type_base": "베이스",
"sheet.flange_type_contour": "윤곽",
"sheet.flange_type_edge": "모서리",
@@ -2992,6 +3184,7 @@
"ui.3d_mouse_section": "3D 마우스",
"ui.3d_mouse_show_in_view": "3D 뷰에 3D 마우스 표시",
"ui.3d_mouse_show_in_view_tip": "왼쪽 아래에 3D 마우스 그림을 띄워 움직임을 따라 보여 줍니다.",
"ui.3d_mouse_sketch_orbit": "스케치 편집 중 뷰 회전",
"ui.3d_mouse_skip": "내 장치는 이 동작을 못 해요",
"ui.3d_mouse_skip_tip": "이 동작을 양방향 모두 건너뛰고 보정에서 뺍니다.",
"ui.3d_mouse_speed": "3D 마우스 속도",
@@ -3159,7 +3352,7 @@
"ui.agent_chat_send_now_tip": "에이전트의 현재 단계를 중지하고 이것을 다음으로 보냅니다 (Ctrl+Enter)",
"ui.agent_chat_setup_body": "SolidCraft는 이 컴퓨터에 설치된 코딩 에이전트와 통신합니다. 환경 설정 › AI에서 에이전트를 선택하세요.",
"ui.agent_chat_setup_title": "어시스턴트 설정",
"ui.agent_chat_side": "에이전트 채팅 위치",
"ui.agent_chat_side": "채팅 도킹",
"ui.agent_chat_signed_in": "로그인됨",
"ui.agent_chat_state_auth": "로그인 필요",
"ui.agent_chat_state_busy": "작업 중…",
@@ -3176,7 +3369,7 @@
"ui.agent_chat_undone": "(취소됨)",
"ui.agent_chat_view_attached": "뷰 첨부됨",
"ui.agent_chat_waiting": "확인 대기 중",
"ui.agent_cursor": "에이전트 커서",
"ui.agent_cursor": "에이전트 포인터",
"ui.agent_cursor_speed": "에이전트 커서 속도",
"ui.agent_off_thread": "에이전트가 명령을 실행하는 동안 창 반응성 유지",
"ui.agent_off_thread_note": "에이전트의 느린 명령(AI 패널 또는 MCP 클라이언트에서 실행)은 창과 별도로 계산됩니다. 뷰를 계속 회전하며 살펴볼 수 있고, 뷰의 칩에서 하나씩 중지할 수 있습니다. 끄면 각 명령이 창의 스레드에서 실행되며, 창은 명령이 끝날 때까지 기다립니다.",
@@ -3219,14 +3412,14 @@
"ui.ai_key_needs_https": "키는 HTTPS를 통해서만, 또는 이 컴퓨터로만 전송됩니다.",
"ui.ai_key_note": "SolidCraft 설정이 아닌 시스템 키체인에 저장됩니다. 로컬 서버라면 비워 두세요.",
"ui.ai_keychain_error": "시스템 키체인이 거부했습니다: {error}",
"ui.ai_mode_all": "모두 허용",
"ui.ai_mode_all": "모두 실행",
"ui.ai_mode_all_note": "묻지 않고 실행합니다. 충돌 보고서는 계속 제외되고 폴더 제한도 그대로 적용됩니다.",
"ui.ai_mode_all_warning": "에이전트가 묻지 않고 디자인을 저장, 덮어쓰기, 내보내기, 교체할 수 있습니다. 신뢰하는 에이전트와 저장된 디자인에서만 사용하세요.",
"ui.ai_mode_ask": "매번 묻기",
"ui.ai_mode_ask": "먼저 묻기",
"ui.ai_mode_ask_note": "디자인의 모든 변경과 파일 작업이 사용자의 허용을 기다립니다.",
"ui.ai_mode_edits": "모델 편집 자동 허용",
"ui.ai_mode_edits": "편집 허용",
"ui.ai_mode_edits_note": "모델링 명령은 묻지 않고 실행되며, 저장·열기·내보내기 등 파일 작업이나 파괴적인 작업은 계속 묻습니다.",
"ui.ai_mode_global": "위 설정 따름",
"ui.ai_mode_global": "위와 동일",
"ui.ai_model": "모델",
"ui.ai_model_hint": "qwen3:14b",
"ui.ai_modes_scope": "이 설정은 SolidCraft 도구에 적용됩니다. 에이전트 자체 도구(셸, 파일 편집)는 에이전트에 모드가 있을 때 채팅 패널 머리글에서 고르는 에이전트 자체 모드를 따릅니다.",
@@ -3245,7 +3438,7 @@
"ui.ai_save": "저장",
"ui.ai_settings": "AI 설정…",
"ui.ai_show_features": "AI 기능 표시",
"ui.ai_show_features_note": "에이전트 채팅: 이미 사용 중인 코딩 에이전트나 선택한 모델 엔드포인트가 열린 디자인을 다루는 패널입니다. 끄면 AI 버튼, 단축키, 메뉴 항목, 패널이 없고 아무것도 찾거나 시작하지 않습니다.",
"ui.ai_show_features_note": "끄면 모든 곳에서 어시스턴트를 숨기고 아무것도 시작하지 않습니다. 아래 항목이 모두 흐려집니다.\n\n에이전트 채팅: 이미 사용 중인 코딩 에이전트나 선택한 모델 엔드포인트가 열린 디자인을 다루는 패널입니다. 끄면 AI 버튼, 단축키, 메뉴 항목, 패널이 없고 아무것도 찾거나 시작하지 않습니다.",
"ui.ai_switch_tip": "이 대화의 에이전트 변경",
"ui.ai_test": "테스트",
"ui.ai_test_acp_ok": "응답: {agent}",
@@ -3259,7 +3452,7 @@
"ui.ai_tool_running": "실행 중",
"ui.ai_tool_unfold": "클릭하면 결과를 표시",
"ui.ai_tool_waiting": "대기 중",
"ui.ai_toolbar_button": "도구 모음에 버튼 표시",
"ui.ai_toolbar_button": "애플리케이션 바의 어시스턴트 버튼",
"ui.ai_toolbar_button_note": "도구 모음 오른쪽 끝의 AI 버튼. Ctrl+Shift+A와 보기 메뉴로도 패널을 열 수 있습니다.",
"ui.ai_undo_tip": "이 메시지에서 에이전트가 바꾼 모든 것을 한 번에 실행 취소",
"ui.ai_url_hint": "https://example.com/v1",
@@ -3397,6 +3590,7 @@
"ui.default_modelling_orientation": "기본 모델링 방향",
"ui.delete": "삭제",
"ui.delete_select_first": "먼저 삭제할 항목을 선택하세요: 피처, 바디, 컴포넌트 또는 스케치 형상",
"ui.dependents_select_first": "먼저 종속 항목을 표시할 대상을 선택하세요",
"ui.depth": "깊이",
"ui.design": "디자인",
"ui.design_properties.author": "작성자",
@@ -3515,6 +3709,7 @@
"ui.extend": "연장",
"ui.extent_type": "범위 유형",
"ui.extrude": "돌출",
"ui.extrude_auto_to_object_tip": "면, 꼭짓점 또는 평행 평면을 선택하면 범위가 객체까지로 전환됩니다.",
"ui.factor": "배율",
"ui.fade": "페이드",
"ui.fade_length": "페이드 길이",
@@ -3546,6 +3741,10 @@
"ui.fillet_setback_least": "{least}보다 크게",
"ui.fillet_setback_tip": "부드러운 코너 패치가 시작되는 위치: 코너에서 각 모서리를 따른 거리. 기본값은 반지름의 {factor}배. 최소 셋백(셋백 없이 라운드가 만나는 곳, 면이 서로 직각이면 반지름)보다 커야 합니다.",
"ui.fillet_smooth_radius": "반지름 부드럽게 변화",
"ui.fillet_turn": "꺾임",
"ui.fillet_turn_mitre": "마이터",
"ui.fillet_turn_swept": "회전",
"ui.fillet_turn_tip": "두 라운드가 사이의 날카로운 코너 모서리를 돌아가는 곳(보스 밑동의 라운드가 세로 모서리를 도는 곳 등).\n마이터: 라운드가 계속 이어져 마이터로 만납니다.\n회전: 라운드가 코너 모서리에서 멈추고 그 둘레를 돕니다. 코너는 둘 모두에 접하는 토러스입니다. 반지름이 같은 라운드가 곧고 볼록한 코너 모서리를 돌 때만.",
"ui.fillet_type_choices_tip": "일정한 반지름: 모서리 전체에 하나의 반지름.\n현 길이: 면이 어떤 각도로 만나든 라운드의 폭이 같습니다.\n가변 반지름: 양 끝의 반지름과, 추가하면 모서리를 따라 더.\n곡률 연속: 평면과 만나는 곳에서 반사가 꺾이지 않는 단면(G2).\n스플라인: 모서리를 따라 반지름이 설정한 제어 반지름을 따릅니다.",
"ui.fillet_type_curvature": "곡률 연속",
"ui.fillet_type_tip": "모서리를 가로지르는 단면. 곡률 연속: 평면 사이의 곡률 연속(G2) 단면으로, 반사가 면에서 라운드로 꺾임 없이 이어집니다.",
@@ -3555,6 +3754,7 @@
"ui.fine": "세밀",
"ui.finest": "가장 세밀",
"ui.finish_sketch": "스케치 종료",
"ui.first_angle": "제1각법",
"ui.fit": "맞춤",
"ui.fix_unfix": "고정/고정 해제",
"ui.flat": "플랫",
@@ -3655,6 +3855,7 @@
"ui.hideOrigin": "원점 숨기기",
"ui.hidePlanes": "평면 숨기기",
"ui.hideProfile": "윤곽 숨기기",
"ui.hide_origin_by_default": "새 디자인에서 원점 폴더 숨기기",
"ui.history_back": "한 단계 뒤로",
"ui.history_below_browser": "브라우저 아래",
"ui.history_edge_hint": "가장자리를 더블클릭",
@@ -3744,6 +3945,7 @@
"ui.move": "이동",
"ui.multiple": "다중",
"ui.named_views": "명명된 뷰",
"ui.nav_bridge_tip": "장치의 내비게이션 브리지에 연결합니다. 요청하기 전에는 꺼져 있습니다. 3D 입력 장치 자체 프로그램(예: 직접 만든 3D 마우스 드라이버)이 뷰를 움직이며 마우스 포인터는 그대로 사용할 수 있습니다. 연결은 이 컴퓨터의 이 포트만 사용합니다.",
"ui.navigation": "탐색",
"ui.needs_design": "{command}: 먼저 디자인을 열어 주세요",
"ui.newSketchGroup": "새 스케치 그룹",
@@ -3792,6 +3994,7 @@
"ui.pipe": "파이프",
"ui.pitch": "피치",
"ui.placement": "배치",
"ui.placement_handles_tip": "모델 위로 피처를 드래그하는 마커와 회전하는 링입니다.",
"ui.plane": "평면",
"ui.plane_at_angle": "각도 평면",
"ui.plugins": "플러그인",
@@ -3825,6 +4028,7 @@
"ui.profiles": "프로파일",
"ui.project": "투영",
"ui.project_face_edges_sketch": "면 모서리를 새 스케치로 투영",
"ui.projection_of_new_drawings": "새 도면의 투상법",
"ui.properties": "속성",
"ui.properties.copy_matrix": "행렬 복사",
"ui.properties.first_moments": "1차 모멘트",
@@ -3956,6 +4160,8 @@
"ui.start": "시작",
"ui.start_over": "처음부터 다시",
"ui.start_page": "시작 페이지",
"ui.step_values": "화살표와 휠로 값 변경",
"ui.step_values_tip": "값 필드에서 위/아래 화살표와 휠로 숫자를 변경합니다.",
"ui.stitch_tolerance_hint": "엄격",
"ui.stretch": "늘이기",
"ui.supersampling": "슈퍼샘플링 (SSAA)",
@@ -3982,6 +4188,7 @@
"ui.text": "텍스트",
"ui.theme": "테마",
"ui.thickness": "두께",
"ui.third_angle": "제3각법",
"ui.thread": "나사산",
"ui.tie_ends": "연결",
"ui.tie_ends_tip": "연결되지 않은 모든 끝점 구속",
+11
View File
@@ -1368,6 +1368,9 @@
"dialog.custom_axis": "↖ Aangepaste as",
"dialog.dimension_insert_hint": "Klik op een andere aandrijvende maat om de naam ervan in te voegen",
"dialog.dimension_name": "Maat: {name}",
"dialog.dimension_step_down": "{step} omlaag",
"dialog.dimension_step_expression": "Alleen een getal verspringt; een uitdrukking blijft zoals ingevoerd",
"dialog.dimension_step_up": "{step} omhoog",
"dialog.edit_title": "{title} BEWERKEN",
"dialog.no_shown_body": "Geen zichtbaar lichaam om te wijzigen: toon er een of kies Nieuw lichaam",
"dialog.pattern_sketch_not_object": "Kies lichamen of kenmerken: een schets is geen van beide. Herhaal de curven ervan met het eigen Patroon of Spiegelen van de schets, of extrudeer de schets eerst.",
@@ -1392,6 +1395,7 @@
"document.read_only_save": "Dit ontwerp is alleen-lezen geopend: sla het op als nieuw bestand",
"document.save_changes": "Wijzigingen in {name} opslaan?",
"document.save_copy": "Kopie opslaan…",
"document.tab_unsaved_changes": "Niet-opgeslagen wijzigingen",
"document.unsaved_changes": "{name} heeft niet-opgeslagen wijzigingen. Als u niet opslaat, gaan ze verloren.",
"drive.rigid": "{joint} is een starre verbinding: die houdt zijn onderdelen vast en heeft dus niets om aan te sturen. Wijzig het type in Verbinding bewerken om ze te laten bewegen: Draaiend om te draaien, Schuivend om te schuiven, Cilindrisch voor beide.",
"drive.stopped_by_contact": "Gestopt bij {value}: {a} zou door {b} heen gaan",
@@ -2194,6 +2198,13 @@
"prefs.dialogs_edge": "Aan de rand van de weergave",
"prefs.dialogs_left_column": "In de linkerkolom",
"prefs.dialogs_note": "Waar opdrachtdialogen openen: vastgezet aan de rand van de weergave, of in de linkerkolom in plaats van de browser en de geschiedenis, die terugkomen zoals ze waren wanneer de dialoog sluit.",
"prefs.dimension_step_angle": "Wat de pijlen van het venster, Omhoog/Omlaag en het wieltje bij een hoek optellen",
"prefs.dimension_step_length": "Wat de pijlen van het venster, Omhoog/Omlaag en het wieltje bij een lengte optellen",
"prefs.dimension_steps": "Stappen in het venster",
"prefs.dimension_values": "Maatwaarden",
"prefs.dimension_values_note": "Waar de waarde van een maat wordt ingevoerd. Een venster heeft pijlen die de waarde laten verspringen; het maatgereedschap plaatst eerst de tekst.",
"prefs.dimension_values_view": "In de weergave",
"prefs.dimension_values_window": "In een venster",
"prefs.display": "Weergave",
"prefs.edges": "Randen en schaduwing",
"prefs.extend": "Verlengen",
@@ -0,0 +1,34 @@
{
"en": {
"export.paper.blueprint": "Blueprint Blue",
"export.paper.prussian_blue": "Prussian Blue (Historical)"
},
"ar": {
"export.paper.blueprint": "أزرق المخطط",
"export.paper.prussian_blue": "أزرق بروسي (تاريخي)"
},
"de": {
"export.paper.blueprint": "Blaupausenblau",
"export.paper.prussian_blue": "Preußischblau (historisch)"
},
"he": {
"export.paper.blueprint": "כחול שרטוט",
"export.paper.prussian_blue": "כחול פרוסי (היסטורי)"
},
"ja": {
"export.paper.blueprint": "ブループリントブルー",
"export.paper.prussian_blue": "プルシアンブルー(歴史的)"
},
"ko": {
"export.paper.blueprint": "블루프린트 블루",
"export.paper.prussian_blue": "프러시안 블루(역사적)"
},
"vi": {
"export.paper.blueprint": "Xanh bản vẽ",
"export.paper.prussian_blue": "Xanh Phổ (lịch sử)"
},
"zh": {
"export.paper.blueprint": "蓝图蓝",
"export.paper.prussian_blue": "普鲁士蓝(历史)"
}
}
+26
View File
@@ -0,0 +1,26 @@
{
"en": {
"cmd.drawing_image_add": "Add Image"
},
"ar": {
"cmd.drawing_image_add": "إضافة صورة"
},
"de": {
"cmd.drawing_image_add": "Bild hinzufügen"
},
"he": {
"cmd.drawing_image_add": "הוספת תמונה"
},
"ja": {
"cmd.drawing_image_add": "画像を追加"
},
"ko": {
"cmd.drawing_image_add": "이미지 추가"
},
"vi": {
"cmd.drawing_image_add": "Thêm hình ảnh"
},
"zh": {
"cmd.drawing_image_add": "添加图像"
}
}
+42
View File
@@ -0,0 +1,42 @@
{
"en": {
"cmd.drawing_note_add": "Add Note",
"cmd.drawing_note_edit": "Edit Note",
"cmd.drawing_note_inspect": "Inspect Note"
},
"ar": {
"cmd.drawing_note_add": "إضافة ملاحظة",
"cmd.drawing_note_edit": "تحرير الملاحظة",
"cmd.drawing_note_inspect": "فحص الملاحظة"
},
"de": {
"cmd.drawing_note_add": "Hinweis hinzufügen",
"cmd.drawing_note_edit": "Hinweis bearbeiten",
"cmd.drawing_note_inspect": "Hinweis prüfen"
},
"he": {
"cmd.drawing_note_add": "הוספת הערה",
"cmd.drawing_note_edit": "עריכת הערה",
"cmd.drawing_note_inspect": "בדיקת הערה"
},
"ja": {
"cmd.drawing_note_add": "注記を追加",
"cmd.drawing_note_edit": "注記を編集",
"cmd.drawing_note_inspect": "注記を確認"
},
"ko": {
"cmd.drawing_note_add": "주석 추가",
"cmd.drawing_note_edit": "주석 편집",
"cmd.drawing_note_inspect": "주석 확인"
},
"vi": {
"cmd.drawing_note_add": "Thêm ghi chú",
"cmd.drawing_note_edit": "Sửa ghi chú",
"cmd.drawing_note_inspect": "Kiểm tra ghi chú"
},
"zh": {
"cmd.drawing_note_add": "添加注释",
"cmd.drawing_note_edit": "编辑注释",
"cmd.drawing_note_inspect": "检查注释"
}
}
@@ -0,0 +1,34 @@
{
"en": {
"cmd.drawing_balloon_repair": "Repair Balloon",
"cmd.drawing_dimension_repair": "Repair Drawing Dimension"
},
"ar": {
"cmd.drawing_balloon_repair": "إصلاح البالون",
"cmd.drawing_dimension_repair": "إصلاح بُعد اللوحة"
},
"de": {
"cmd.drawing_balloon_repair": "Positionsnummer reparieren",
"cmd.drawing_dimension_repair": "Zeichnungsbemaßung reparieren"
},
"he": {
"cmd.drawing_balloon_repair": "תיקון בלון",
"cmd.drawing_dimension_repair": "תיקון מידת שרטוט"
},
"ja": {
"cmd.drawing_balloon_repair": "バルーンを修復",
"cmd.drawing_dimension_repair": "図面寸法を修復"
},
"ko": {
"cmd.drawing_balloon_repair": "벌룬 복구",
"cmd.drawing_dimension_repair": "도면 치수 복구"
},
"vi": {
"cmd.drawing_balloon_repair": "Sửa chữa bóng chú thích",
"cmd.drawing_dimension_repair": "Sửa chữa kích thước bản vẽ"
},
"zh": {
"cmd.drawing_balloon_repair": "修复零件序号",
"cmd.drawing_dimension_repair": "修复绘图尺寸"
}
}
@@ -0,0 +1,26 @@
{
"en": {
"cmd.drawing_revise": "Issue Revision"
},
"ar": {
"cmd.drawing_revise": "إصدار مراجعة"
},
"de": {
"cmd.drawing_revise": "Revision herausgeben"
},
"he": {
"cmd.drawing_revise": "הוצאת גרסה"
},
"ja": {
"cmd.drawing_revise": "リビジョンを発行"
},
"ko": {
"cmd.drawing_revise": "리비전 발행"
},
"vi": {
"cmd.drawing_revise": "Phát hành bản sửa đổi"
},
"zh": {
"cmd.drawing_revise": "发布版本"
}
}
@@ -0,0 +1,42 @@
{
"en": {
"cmd.drawing_revision_tag_add": "Add Revision Tag",
"cmd.drawing_revision_tag_edit": "Edit Revision Tag",
"cmd.drawing_revision_tag_delete": "Delete Revision Tag"
},
"ar": {
"cmd.drawing_revision_tag_add": "إضافة علامة مراجعة",
"cmd.drawing_revision_tag_edit": "تحرير علامة المراجعة",
"cmd.drawing_revision_tag_delete": "حذف علامة المراجعة"
},
"de": {
"cmd.drawing_revision_tag_add": "Revisionskennzeichen hinzufügen",
"cmd.drawing_revision_tag_edit": "Revisionskennzeichen bearbeiten",
"cmd.drawing_revision_tag_delete": "Revisionskennzeichen löschen"
},
"he": {
"cmd.drawing_revision_tag_add": "הוספת תג גרסה",
"cmd.drawing_revision_tag_edit": "עריכת תג גרסה",
"cmd.drawing_revision_tag_delete": "מחיקת תג גרסה"
},
"ja": {
"cmd.drawing_revision_tag_add": "リビジョンタグを追加",
"cmd.drawing_revision_tag_edit": "リビジョンタグを編集",
"cmd.drawing_revision_tag_delete": "リビジョンタグを削除"
},
"ko": {
"cmd.drawing_revision_tag_add": "리비전 태그 추가",
"cmd.drawing_revision_tag_edit": "리비전 태그 편집",
"cmd.drawing_revision_tag_delete": "리비전 태그 삭제"
},
"vi": {
"cmd.drawing_revision_tag_add": "Thêm thẻ sửa đổi",
"cmd.drawing_revision_tag_edit": "Sửa thẻ sửa đổi",
"cmd.drawing_revision_tag_delete": "Xóa thẻ sửa đổi"
},
"zh": {
"cmd.drawing_revision_tag_add": "添加版本标记",
"cmd.drawing_revision_tag_edit": "编辑版本标记",
"cmd.drawing_revision_tag_delete": "删除版本标记"
}
}
@@ -0,0 +1,26 @@
{
"en": {
"cmd.drawing_scene_hit": "What Is Under a Point"
},
"ar": {
"cmd.drawing_scene_hit": "ما يقع تحت نقطة"
},
"de": {
"cmd.drawing_scene_hit": "Was unter einem Punkt liegt"
},
"he": {
"cmd.drawing_scene_hit": "מה נמצא מתחת לנקודה"
},
"ja": {
"cmd.drawing_scene_hit": "点の下にあるもの"
},
"ko": {
"cmd.drawing_scene_hit": "점 아래에 있는 항목"
},
"vi": {
"cmd.drawing_scene_hit": "Thứ nằm dưới một điểm"
},
"zh": {
"cmd.drawing_scene_hit": "点下方的对象"
}
}
@@ -0,0 +1,26 @@
{
"en": {
"cmd.drawing_scene_line": "Line Under a Point"
},
"ar": {
"cmd.drawing_scene_line": "الخط تحت نقطة"
},
"de": {
"cmd.drawing_scene_line": "Linie unter einem Punkt"
},
"he": {
"cmd.drawing_scene_line": "הקו שמתחת לנקודה"
},
"ja": {
"cmd.drawing_scene_line": "点の下にある線"
},
"ko": {
"cmd.drawing_scene_line": "점 아래의 선"
},
"vi": {
"cmd.drawing_scene_line": "Đường nằm dưới một điểm"
},
"zh": {
"cmd.drawing_scene_line": "点下方的线"
}
}

Some files were not shown because too many files have changed in this diff Show More