Compare commits

..

114 Commits

Author SHA1 Message Date
0xlillith 1b55174743 Render: isolate lens saves and retain bounded previews
Save only lens parameters on existing CPU/GPU jobs while keeping unrelated
draft settings dirty. Retain pinned checkpoints through preview and status,
and release host-only snapshots outside the status lock.

Use the admitted PNG bound for registered-executor checkpoint slots. Preserve
executor work credits, final publication and regrade ownership. Add regression
coverage and align help, translations and capability documentation.
2026-11-05 04:22:03 +01:00
0xlillith 5821ca2b5e Render: register existing live and coat command tests 2026-11-05 02:08:15 +01:00
0xlillith 8829c1c3c5 Render: restore saved shots, progressive images and exports 2026-11-05 02:08:14 +01:00
cronos a137f2d459 Sketch Trim: keep the driving distance when trimming a line offset first (#11442) 2026-11-04 22:23:12 +00:00
thocsmith f87dd73661 Selection filter: from command search it offers Faces, Bodies, Components (#11505) (#11553)
* claim: #11505

* Selection filter: started from command search, it offers Faces, Bodies, Components (#11505)

Command search ran select.filter with no parameters, which could only fail
with "missing field `mode`". Started from the UI (command search, a
shortcut, ui.start), it now opens its three modes as a menu with the
current one marked, as View > Visual style and Navigation tool do (#8868).
Programmatic calls keep their contract and open nothing.

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 22:23:02 +00:00
PaddleStroke af5644aee6 Ribbon: switch panel menus on hover after the first click (#11447) (#11713)
* claim: #11447

* Add an option to switch ribbon panel menus on hover

* Update profile membership count for ribbon hover setting
2026-11-04 22:22:38 +00:00
SixSolid abeb232f17 Sketch Mirror: use an origin axis that is not in the sketch plane (#8201) (#12091)
* claim: #8201

* Sketch Mirror: use an origin axis that is not in the sketch plane (#8201)

A sketch on a part face does not hold the world X, Y or Z axis, so Mirror refused the pick. It now mirrors about the axis's projection into the sketch plane and keeps that line linked as fixed reference geometry. An axis perpendicular to the plane stays refused because it has no line in the plane. The sketch's own X and Y axes keep working as before.
2026-11-04 22:22:10 +00:00
raykholo 1a27c8399d Linux 3D mouse: push/lift and tilt/twist axes are no longer swapped (#3523) (#4833)
* claim: #3523

* Linux 3D mouse: decode spacenavd's axes in its documented frame (#3523)

spacenavd reports x to the right, y up and z away from the user, with the
rotations about those axes. The decoder treated y as away and z as up, so
with a stock spacenavd push and lift traded places, as did sideways tilt
and twist. Map the daemon's axes to the raw HID frame as translation
[x, -z, -y] and rotation [rx, -rz, -ry].

The decode still runs before a device's calibration and the per-axis
motion choice, so both apply unchanged on top of the corrected reading.
The tests now move each daemon axis on its own, in both directions, so a
swap of two axes can no longer pass.
2026-11-04 22:13:57 +00:00
Jackson Oursland 5ab59e7c1b Assistant: DeepSeek and Qwen endpoint presets (#5016) (#5063)
Adds DeepSeek and Qwen presets to both endpoint protocols: OpenAI-compatible (api.deepseek.com, dashscope-intl compatible-mode/v1) and Anthropic-compatible (api.deepseek.com/anthropic, dashscope-intl apps/anthropic).

Co-authored-by: Claude Code <noreply@anthropic.com>
2026-11-04 22:13:47 +00:00
SiroArt 9a274f91df Planned bodies: smaller GPU chunks, and a chunk keeps its colours while they are unchanged (#5490)
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-04 22:13:38 +00:00
PaddleStroke 08fa042039 Dialogs: drag a value's label sideways to change it (#2217) (#9685)
* claim: #2217

* Dialogs: drag a value's label sideways to change it (#2217)

A value row's label (expr_row, hint_row) now scrubs a literal value: about a
hundredth of its size per point of drag, on that step's grid, Shift ten times
finer, Ctrl/Cmd ten times coarser. The unit and decimal mark stay as typed, a
size never turns negative, and expressions and parameters are left alone. The
text changes as if typed, so previews follow and OK still makes one command.

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

* Label drag: measure from the press point, cap values, keep scrub kit-private

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 22:10:04 +00:00
P'tite Tête 09bb8aaaf5 Revolve: an axis origin written as an expression is no longer read as zero (#10252)
* fix: Revolve axis origin takes expressions instead of silently zero

- AxisRef::Line origin is a PointExpr evaluated with parameter values in the design unit (format note in pending)
- An unreadable origin or dir is a BadParams error instead of (0, 0, 0)
- Edit Feature keeps the saved expressions of an untouched origin or dir, for solid and surface revolves, in moved components too
- Face role names stay stable with a parameter-driven axis

* chore: Re-run PR checks after the description fix

* fix: Edit Feature of a revolve maps nothing where nothing is placed

- unplaced designs skip world_state and the pick table, as before the merge
- the untouched-edit test checks that OK applied the edit (CodeRabbit)

---------

Co-authored-by: P'tite Tête <asso@ptitetete.com>
2026-11-04 22:09:27 +00:00
Mateusz Sych 46edd8d749 Agent chat: Custom instructions in Preferences, added to the system note (#8220) (#11105)
* claim: #8220

* Agent chat: Custom instructions in Preferences, added to the system note (#8220)

Preferences > AI gets a multi-line Custom instructions field, saved with
the other AI preferences (agent_instructions, kept by page resets like
other user-written text). The chat adds the text after its own system
note when a session starts, by the routes #9811 set up: ACP session/new
(the Claude adapter's _meta key, the standard systemPrompt field, the
Antigravity key) and the system message or field of every API protocol.
Connect and New chat set it, so an edit applies from the next chat and
never changes the one running. Blank text adds nothing; at most 4000
characters are kept, in the field, over ui.agent_chat and from a hand-
edited settings file. ui.agent_chat takes and reports `instructions`.

* Custom instructions: typing in the field stays in the field

The text area did not report itself to Preferences search as a key sink, so with
the field focused, search took the typed characters and moved focus away. The
field now notes the sink while it has focus, like the other preference fields,
and the #8220 test types into it.

---------

Co-authored-by: MateuszSychXpl <151755914+MateuszSychXpl@users.noreply.github.com>
2026-11-04 22:07:30 +00:00
semmelrinde c5d3dabd1b Split Body: the tool face follows a parameter change (#11674) (#11699)
The tool face of Split Body is now a face pick: it is named when it is
picked and found again by that name, as other face picks are, so a split
by a rail's face follows the rail when a parameter moves it.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 22:06:13 +00:00
Ben Rog-Wilhelm 92171bd79b Hole: a hole on a face that another part touches drills its own part, not the other one (#12066) (#12067)
* claim: #12066

* Hole: a hole on a face that another part touches drills its own part (#12066)

A hole placed by position with no direction drills against the outward
normal of the face at the point. That face was looked for on every body,
so where another part touched the face at the hole, the part made first
won. The hole then drilled away from the part `targets` named and cut only
the tool's run-in pocket into it, without an error.

A hole that names its bodies now goes by their faces wherever one of them
passes through its position, when it is made (hole_direction_at) and when
a design is evaluated (hole_dir_into_face). Saved holes whose direction
leaves their own body's face there are mirrored into it, as format 18 does
for a thin plate's far side; the pending format note records it. A hole
whose bodies have no face at its position (placed on another part's face
to drill the part behind it) goes by every body's faces, as before.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 22:05:30 +00:00
CamWight 2ce55baba1 Dimensions: a radius leader and an aligned dimension's line reach their text (#12093)
The value text is drawn level, in an upright box, and the line runs
through its middle. The layout stopped the line where the box's farthest
corner reaches along it (half.x*|u.x| + half.y*|u.y|), not where the line
leaves the box (min(half.x/|u.x|, half.y/|u.y|)). They agree only when the
line is level or upright: at 45 degrees a radius leader stopped about
12 px short of its text, and an aligned dimension's line broke too wide
around its value.

One helper, box_exit, gives where the line leaves the box, for the radial
leader, the linear break and the leader to a text pulled past the
extension lines. Whether the text fits between the arrows still goes by
the whole box. A text box that isn't finite counts as empty, and one
larger than a sketch can be is held to MAX_COORD.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 22:05:13 +00:00
github-actions[bot] 6870f9539c Regenerate generated files and fold pending files [skip ci] 2026-11-04 23:51:34 +00:00
rugbymauri 2998d412b6 Primitives: clicking a pick field no longer jumps to the Diameter box (#10428) (#10536)
* claim: #10428

Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>

* Primitives: a pick field waits for its pick and says what it is for

Clicking Face, Reference 1 or 2 in the Cylinder window, or Point in the
Box, Sphere and Torus windows, and then clicking something the field
does not take (empty space, the cylinder's own preview) gave the
keyboard to Diameter, so the field no longer looked active and nothing
said why the click picked nothing. A pick field clicked into now keeps
the keyboard until a click picks something, and a click it does not
take leaves a note under the form saying what the field is for.

These fields also get their own tooltips: Face is the face or vertex
the cylinder stands on, Reference 1 and 2 are straight edges of that
face the distances below are measured from, and Point is a vertex,
edge, face, sketch point or the origin whose point fills X, Y and Z.

Fixes #10428

Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>

---------

Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>
2026-11-04 22:04:36 +00:00
Daniel 2396b6be05 Hole: the drag handle looks like the Rest, Boss and Snap Fit handles (#6818) (#6819)
* claim: #6818

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Hole: the handle drawn with the shared marker (#6818)

A Single hole's drag handle (Move holes by clicking or dragging) is drawn with the placement
marker from place_gizmo/look.rs instead of its own ring and cross, so a hole and a Rest on the
same model show one language for the same action. place_gizmo::marker is the one crate-visible
door: it runs the marker's hover and drag motions under the handle's id and draws the crosshair,
never dimmed. Hole keeps its snapping, the click that takes a hole out, its handle names and its
hover text.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Hole: hole_handle removed (#6818)

The shared marker draws the handle now; nothing else used it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Hole: the marker takes no colours it does not use (#6818)

place_gizmo::marker took a &Tokens only to hand it to crosshair, which reads its colours itself,
so the hole's show fetched tokens nothing read. The argument and that fetch go.

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

* Hole: a test sees the handle drawn as the shared marker (#6818)

The hole tests found the handle by its published position, which the look does not change. This
one reads what the frame paints round it: the white dot and four dashed arms at rest, no ring
round the centre, arrowheads when pointed at, solid shafts while it drags. It fails with the old
ring and cross back on the call line.

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

* Place handles: marker hover eases in 120 ms, drag in 220 ms (Refs #6818)

The marker answered the pointer in 220 ms and a drag in 360 ms, which felt slow on review. Both
stay in look.rs as the one place a later preference can read them.

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

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-04 21:55:30 +00:00
austin62jake 3c1074cfc6 Timeline: moving a construction axis now updates the features that use it (#9544) (#9545)
* Evaluation: a construction axis moved past a feature makes it evaluate again (#9544)

The state digest in a feature's cache key left out the construction
geometry before it, and the context digest hashes construction features
without their order. A feature started from the same key with or without
the axis before it, so after a move it kept its old result, and a cached
state restored whole brought back the construction list it was made with.

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

* Evaluation: say plainly what an empty construction list hashes (#9544)

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-11-04 21:55:19 +00:00
PaddleStroke c4a63a1d43 GPU path tracer: don't rerun a setup that crashed in the driver (#9236) (#9666)
* claim: #9236

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

* GPU path tracer: don't rerun a setup that crashed in the driver (#9236)

A driver that takes the process down while it compiles the path tracer
(RADV on an RX 7600) is beyond error scopes and unwind guards. A running
setup now keeps gpu-path-setup.json (its adapter key) in the settings
folder, removed when the setup answers or the app closes normally. A
launch that finds it for the same adapter leaves the setup failed with an
explanation and a Try again button; CPU ray tracing stays available, and
a driver update changes the key and so tries again.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 21:54:45 +00:00
dudedagger42 2fdcc5ae49 Appearance: a picture of your own behind the model (#11353)
* Appearance: a picture of your own behind the model

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

* Background picture: a label that fits its row

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 21:53:10 +00:00
austin62jake 4678a04827 Agents and scripts: the contact commands describe their exact parameters and reply (#11949) (#11950)
* claim: #11949

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

* Contact sets: input and output schemas for the seven contact commands (#11949)

describe_command now gives the exact parameters and reply of
contact.enable_sets, enable_all, disable_all, create, edit, list and
check, and the warn and strict parameter checks use them. The handlers
read their parameters from the raw JSON, so the contracts in
schema/motion.rs read each key as its handler does, with its message:
`occurrences` (OccurrenceKeys) and `set` are required in the schemas
but left to the handlers' messages in the types, through a small
`require` transform. The replies are built from typed values instead of
json! literals, with the same JSON; the listed sets mirror the doc
crate's ContactSet.

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-11-04 21:52:53 +00:00
kaltenbrunnermarkus 63ffadfebf Import: a curve that refers to itself is refused instead of overflowing the stack (#11893)
A 2D TRIMMED_CURVE in a pcurve read its basis straight through read_curve,
past the depth check that only the cached entry points (curve_in, curve_2d,
surface_in) made. A trimmed curve whose basis is itself, or a loop of two,
recursed until the stack overflowed and the app closed.

The depth check now sits in read_curve and read_surface, which every curve
and surface read passes through, so any chain of references, a cycle
included, ends at MAX_GEOM_DEPTH with an error that names the entity:
"curve #9: definitions nested more than 12 deep, or referring to
themselves". The other nested entities (surface, seam, intersection and
bounded surface curves, pcurves, 3D offset curves, offset, trimmed,
revolution and extrusion surfaces) already went through the checked entry
points; they now share the one check.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 21:45:25 +00:00
kaltenbrunnermarkus aee947f0f9 Sketch: hovering over a large sketch no longer slows down (#9614)
* claim: Sketch: keep profile outlines and trim/extend cuts between mouse moves

* Sketch: keep profile outlines and trim/extend cuts between mouse moves

Hovering a large sketch sampled every profile's loops into polylines for
each point asked, and Trim/Extend intersected the hovered curve with every
other curve on every mouse move.

- Loop2::contains rejects points clearly outside a box from the loop's end
  and control points before sampling; Region2::outline samples the loops
  once (RegionOutline). Profile keeps both in OnceLocks and answers
  Profile::contains from them (viewport pick, profile_at_point, profile
  selection by points). Answers are unchanged: the box margin covers
  rounding, and non-finite values or negative conic weights skip the box.
- sketch_modify splits the cuts on a curve (TrimHover) from choosing the
  piece around the cursor. The UI keeps one per sketch state and curve,
  and Extend's preview per sketch state, curve and growing end (afresh
  when the sketch has an infinite line).

Plate with 400 circles (801 profiles), release, per mouse move: UI frame
5.19 -> 1.11 ms, with Trim 9.45 -> 1.50 ms; 30 lines x 30 splines: Trim
piece 6.10 -> 0.0002 ms. Equivalence tests compare with the old answers.

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

* Sketch: keep the profile outline helpers in the sketch crate

The outer loop's box, the clearly-outside test and the outline sampled
once move from geom into crates/sketch/src/profiles.rs as private items
built from geom's public types, so the PR leaves crates/geom untouched.
Profile::contains answers exactly as before; the geom test moves to the
sketch profile tests.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 21:42:06 +00:00
Daniel ad56dd6a93 Box window: a picked point fills Corner and Center in the design's unit (#12051) (#12052)
* Box window: a picked point fills Corner and Center in the design's unit

A vertex, edge or face picked into the Box window's Point input wrote its X, Y and Z as millimetre text whatever the design's length unit was, so an inch design showed "50.8 mm" where the other fields read inches. The fields now take the point through length_input::model, as Move and the Hole references do, so they read "2 in" there and stay "50.8 mm" in a millimetre design. The same applies to the Sphere and Torus centre.

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

* Box window: a picked point reads to a millionth of the design's unit

length_input::model divides without rounding, so a vertex at 38.1 mm read "1.5000000000000002 in" in an inch design. length_input::shown rounds to a millionth of the design's unit, as the millimetre text did before, and the test pins that case.

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

* Box window: a picked point keeps the micrometre in every design unit

length_input::shown rounds to a millionth of the design's unit, and the picked point's text is what OK sends, so in a metre design a vertex could move by up to half a millimetre. Document::design_length keeps the micrometre in every unit and still writes 38.1 mm as "1.5 in". The test now covers a metre design with a point that is not round in mm, and the Sphere and Torus centre.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 21:40:17 +00:00
ABDULLAH cafe015ef9 Arabic: sketch tool hints and errors, view-mode name, face term and Fillet name in the hidden-edge selection preference (#12080)
* Arabic: view-mode name, face term and Fillet command name in the hidden-edge selection preference

* Arabic: translate the sketch tool hints and errors, clearer Fillet phrase in the hidden-edge selection help
2026-11-04 21:40:08 +00:00
Daniel 155387bac1 Sketch Trim: a cut on a shape's centre ends the piece there, with no second point (#12101) (#12103)
* Sketch Trim: a cut on a shape's centre ends the piece there, with no second point

Trimming half of a center rectangle's diagonal left two points at the centre. Trim dropped the diagonal's Midpoint constraint and ended the kept half on a new point held only on the other diagonal, so the old centre was free and the new end slid along the other diagonal. Dragging a corner pulled them apart.

A cut that lands on a point held at the middle of the trimmed line or of the cutter now ends the kept piece on that point. When the trimmed line held it, the Midpoint moves to the cutter if the point is the cutter's middle too (a rectangle's diagonals bisect each other), and otherwise the point is held on the cutter as a new end would be.

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

* Sketch Trim: weld the cut end before a parallel distance moves onto it

A distance between parallel lines that measured from the trimmed line's end moves to the kept piece's start. When that start is a cut welded to a centre point, the move named the new point, which the trim then removed along with the dimension. The weld now happens first.

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

* Sketch Trim: test that a parallel distance moves onto the welded centre

Trimming the start half of a center rectangle's diagonal moves a distance to a parallel line onto the kept half's start, which is the centre. The test fails if the weld runs after that move: the dimension then names the cut point and is removed with it.

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

* Sketch Trim: the centre comment names the case that reaches it

Only a line carries a Midpoint, so the comment's ellipse case could not reach this code.

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

* Sketch Trim: a crossing line whose middle is only near the cut gets no midpoint hold

Trimming a line at its held midpoint moved that hold onto the cutter whenever the cutter's middle sat at the cut, so a crossing line that merely happened to have its middle there was tied to the point. The hold now moves only when the sketch makes the two lines diagonals of a parallelogram (each pair of opposite sides held parallel, horizontal or vertical), as a center rectangle's are; otherwise the point stays on the cutter.

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

* Sketch Trim: ask the solver whether a cutter's middle is held at a cut midpoint

Moving a cut midpoint's hold onto the cutter only for a parallelogram of parallel, horizontal or vertical sides missed rectangles the sketch holds another way: with the bottom side held square to a side instead of horizontal, trimming a diagonal at the centre left the centre held only on the other diagonal, one degree of freedom freer than before. The hold now moves when the sketch already implies it, that is when adding it to the sketch as it was takes away no degree of freedom.

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

* Sketch Trim: solve for the implied midpoint only when a cut lands on one

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 21:39:58 +00:00
Daniel de1db6fb8c Parameters: reach the end of a long expression the same way on every OS in its test (#12104)
a_long_expression_opens_wide_while_edited_and_enter_applies_it moved the cursor to the end of the wrapped field with Ctrl+End. The control channel's cmd flag also sets ctrl, and on macOS egui treats Ctrl with End as an unhandled Emacs-style chord, so the cursor stayed where the click put it. The typed " * 2" did not land at the end, the parameter kept its old expression, and the test failed on macOS while it passed on Linux.

The test now uses Command+Down with ctrl off, which egui maps to the end of the text on every platform. What the test checks is unchanged.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 21:39:49 +00:00
github-actions[bot] c47e848f4a Regenerate generated files and fold pending files [skip ci] 2026-11-04 23:04:10 +00:00
github-actions[bot] 8b4fe10995 Regenerate generated files and fold pending files [skip ci] 2026-11-04 22:53:51 +00:00
Daniel 8fb046aaa8 Sketch: the whole sketch updates while you type a dimension's value (#11584) (#11585)
* claim: #11584

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

* Sketch: the sketch follows a dimension's value while it is typed (#11584)

While a new dimension's value was typed, only the dimension moved: the sketch's curves, points, constraint glyphs and other dimensions stayed at the old size until Enter. The typed value was already solved in a scratch session; it is now solved in the frame it is typed, and the open sketch is drawn from that solve, for every kind of curve, within the display cache's limits per sketch. Esc draws the design again. Picking and snapping of curves and points stay on the design, and no hover highlight of the design is painted while a value is typed. A value that does not solve keeps the last one that did.

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

* Dimension: the sketch scene follows each solve of a typed value, not its address (#11584)

The sketch scene was keyed on the preview's address, which a later solve can have once the drawn one is freed; the scene would then stay at the old value. Each solve now has a number, and the scene is keyed on it.

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

* Dimension: while a value is typed, no design profile is lit over the sketch, and its curves are sampled before its fills (#11584)

The profile under the pointer was lit where the design has it, over the sketch drawn at the typed value; it is no longer, and the highlight is built again when the value is let go. The typed sketch's profiles were sampled before its curves from the one budget, so a sketch whose fills used it up lost its curves; the curves now go first.

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

* Dimension: a selected profile is not lit over the sketch while a value is typed either (#11584)

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

* Dimension: the view goes to the typed preview only once a value is typed (#11584)

A picked dimension's value box opened with a draft that is the design as it is, and the view already treated it as the typed preview: the profile under the pointer, the selection and the hover went dark before anything was typed. The preview now starts with the first key, completion or reference.

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

* Dimension: a reference picked into the value is solved at once (#11584)

The overlays read the preview before the editor solves a changed value, so for the click's frame they drew the solve before the reference.

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

* Dimension: a selected sketch curve is not lit where the design has it while a value is typed (#11584)

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

* Dimension: the pointer's x, y readout is not drawn while a value is typed, as the hover is not (#11584)

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

* Dimension: curvature combs, the design's teeth, are not drawn while a value is typed (#11584)

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

* Dimension: while a value is typed only the open sketch loses its hover and profile highlights; other sketches keep theirs (#11584)

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 21:34:23 +00:00
leValdemar19 7b31c0567b Sketch Transform: turn and scale about a picked point, such as a rectangle's corner (#11718) (#11797)
* claim: #11718

* Sketch Transform: turn and scale about a picked point, such as a rectangle's corner (#11718)

The Transform window turned and scaled the picked entities only about the
middle of their box. It now has a Center field, as Move does. A sketch point
picked there is the point Scale and Angle work about, and Position moves it.
The box, its corner handles and the knob in the view follow the same pivot.
With Center empty, Transform works as before.

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

* Sketch Transform: a click on a corner picks it as Center while Center waits for a pick (#11718)

The rectangle corner the issue wants to turn about sits under the box's
corner handle, so a click there started a scale. While the Center field
waits for a pick, the box's handles leave clicks to the sketch.

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

* Sketch Transform: the handles work again once Center has its point (#11718)

Center is the window's last field, so a pick there left it active, and the
box's handles stayed off until another field was clicked. They are now off
only while Center is empty and waiting for its pick.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 21:33:06 +00:00
Daniel 5efdd52854 Ribbon: a tool pinned into the Finish Sketch group shows pressed (#12049) (#12050)
* Ribbon: a tool pinned into the Finish Sketch group shows pressed

The Finish Sketch and Finish Editing groups never drew the pressed state for any of their buttons, so a drawing tool dragged into them (the group at the ribbon's right edge takes drops) looked like a one-shot action while it ran. Only Finish, Cancel and Finish Edit are now left out of the pressed state.

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

* Ribbon: test that Finish, Cancel and Finish Edit stay plain under a press

None of the three runs as a tool or leaves a dialog behind, so a press is the one way they could light up. The tests now press each one and fail if any of them is dropped from the exclusion. Finish Edit is pressed and released in the edit-in-place test, so the release is also the click that finishes the edit.

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

* Ribbon: Finish with AutoConstrain stays plain under a press like Finish

Finish with AutoConstrain belongs to the Finish Sketch group and ends the sketch like Finish, but it was not in the list of buttons left out of the pressed state, so once pinned as a button it lit while the pointer was held on it.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 21:32:12 +00:00
takuto-NA 61ada52984 Reuse unchanged imported meshes during joint motion (#5476) (#5480)
* perf(mesh): reuse imported contents and display triangles

* fix(viewport): preserve reference mesh detail and glass fallback

* fix(viewport): preserve small faded mesh ordering without OIT
2026-11-04 21:30:58 +00:00
SS-EN 6578d591ac control::execute: check the undo depth before placing the world for the first-body fit (#5940)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 21:30:48 +00:00
JK3D d9421bf2ca Browser: an option to start folders closed and open rows only on request (#8768) (#8771)
* claim: #8768

* feat: Browser folders start closed and find a body only when asked, behind a preference

Preferences › Layout › Browser rows start closed (`browser_rows_start_closed`, off by
default) starts the Browser's folders closed, and placements too whatever "Components start
open" (#9274) says. Rows then open with their arrow, Expand All or Find in Browser, which opens
just the rows down to its body and closes again the ones the last find opened. A body picked in
the 3D view leaves the Browser as it is, and only the body Find in Browser found has the rows
above it marked, so a large assembly's placements are not named again after each edit.

With the preference off, folders start open, placements start as "Components start open"
says, and a pick in the 3D view opens the Browser down to its body, as on main. Find in Browser
looks the body's placement up once and opens its rows before drawing, instead of each row
testing the revealed path while it is drawn.

* Browser tests: read the row order from the Browser's content frame

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 21:29:23 +00:00
PaddleStroke 774c45a512 Dynamics: rigid-body solver core with joints, gravity and springs (#9719) (#9721)
* claim: #9719

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

* Dynamics: rigid-body solver core with joints, gravity and springs (#9719)

A new L1 crate, solidcraft-dynamics (geom only): rigid bodies in maximal coordinates, joint constraint equations with analytic Jacobians for rigid, revolute, slider, cylindrical, planar and ball joints, gravity, linear spring-dampers and constant loads; the index-1 equations of motion solved through a rank-revealing Schur complement (repeated joint equations are solved, not refused); RK4 and Dormand-Prince 5(4) with position and velocity projection; joint values, rates, reactions and energies at every output sample; caps on bodies, joints, rows, steps and samples; Result errors only.

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

* Dynamics: take Vec3 from the record crate after the layer split, refresh the lock file

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 21:27:17 +00:00
P'tite Tête 315d8cfeb7 fix: sketch.intersect keeps a small round between short walls (#9962)
- section() kept the loose fit whenever it had fewer curves, even when it had merged 2 mm walls and an R2 round into one R2.85 arc that misses the walls, leaving the outline open
- The tight fit is now also kept when the loose one strays from the exact crossings (on planes and rulings) and the tight one does not; capped work, other curves count as near
- Test: a box with R2 corner rounds and four tapered grooves stopping 2 mm short, cut at Y=20

Co-authored-by: P'tite Tête <asso@ptitetete.com>
2026-11-04 21:27:05 +00:00
TaeHeon Kim 4ab1640dc8 OCLP detection: run /usr/sbin/nvram by its full path (#10109)
The probe ran `nvram` through PATH, which a Finder launch may not list
and which a look-alike earlier in it could change. The doc comment that
said exec would need unsafe is replaced by a description of what the
code does. Behaviour is otherwise unchanged.

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-04 21:26:51 +00:00
raykholo 3aee35fe01 Web app: keep unsaved work in the browser instead of the leave-page box (#10220) (#10221)
* claim: #10220

* Web app: keep unsaved work in the browser instead of the leave-page box (#10220)

A new preference, off by default, on Preferences > General > Saving (web
app only): "Keep unsaved work in this browser instead of asking when the
tab closes". With it on, each open design with unsaved changes keeps a
copy in the browser's private storage (keep-<page>-<design>.sc/.json),
written once the design has been still for 0.8 s (at most 4 s after the
first change it lacks) and again on pagehide or when the page is hidden.
While every copy is current, beforeunload doesn't ask. A copy goes when
its design is saved, closed, or the preference is turned off.

The next visit offers the copies newer than their design's last save in
SolidCraft's own window, "Restore unsaved design: <name>, changed
<time>?", one window for several, with Restore, Open saved version, and
Discard. A page holds a Web Lock while it runs, so another open tab's
copies are never offered or removed.

With the preference off, the single autosave and the browser's prompt
work exactly as before.

* Web app: the restore window comes from the UI kit's recovery module (#10220)

The kit's text and divider helpers are private to the UI crate, so the
"Restore unsaved design?" window moves to recovery_ui
(browser_copies_window), beside the desktop recovery window, and the web
shell only maps its answer. Also keeps the window handle for the page
listeners instead of using the one moved into the app.

* Web app: restore window buttons sit in one row (#10220)

* Web app: keep unsaved work and still ask, unless told not to (#10220)

Keeping a copy in the browser no longer replaces the browser's leave-page
box by itself. A second option, shown once work is kept and on by default,
leaves the box in place; off, the box appears only while a copy isn't current.

* Web app: kept-copy review fixes (#10220)

A failed write of a kept copy waits 30 s before the next try, a recovery
copy is dropped only after the remembered saved file has opened, the
preference is documented under its current label, and the ui.prefs list
is sorted again.
2026-11-04 21:26:23 +00:00
raykholo 5562d9c6c8 Quick-access bar: pin ribbon commands to the application bar (#10223) (#10224)
* claim: #10223

* Quick-access bar: pin ribbon commands to the application bar (#10223)

A command's menu (a ribbon button, a section drop-down row or a command
search result) has Add to Quick Access: the command's own icon joins the
application bar after Save, Undo and Redo. A click starts it as its
ribbon button does, a drag along the bar reorders the buttons, and the
button's menu has Remove from Quick Access. Empty, and so not drawn, by
default; it holds up to 24 commands.

The list is kept with the toolbar's customisations (`quick_access` in
the toolbar's saved document), so it is saved, exported with the
keyboard shortcuts and carried by settings profiles.

* Quick access: a pinned family variant no longer replaces the ribbon choice

A quick-access click now starts the pinned command with start_pinned, so it
starts exactly that command and leaves the sketch family remembered choice
(the main ribbon button) unchanged.
2026-11-04 21:26:13 +00:00
Colin Faulkingham d16496e057 Navigation: honor reverse zoom for mouse drags (#10354) (#10365)
* claim: #10354

* fix(ui): honor reverse zoom for mouse drags
2026-11-04 21:26:01 +00:00
Arjan Duijs 0690ee5875 Web app: run as a modelling editor inside another page that keeps the design files (#8517) (#11273)
* claim: #8517

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

* web: editing embed for host pages (?embed=editor, #8517)

A host page that keeps the files itself and frames SolidCraft for modelling needs
the modelling UI without the application chrome. `?embed=editor` runs the web app
as such an embed:

- no application bar (logo, File menu, document tabs, update, help, report a bug)
  and no start page; the layout is docked;
- the ribbon, browser, timeline and properties stay; Ctrl+Z and Ctrl+Y still undo
  and redo;
- Ctrl+N, Ctrl+O, Ctrl+S, Ctrl+W and Ctrl+, are left to the host;
- nothing is kept in the browser (no stored designs, autosave, recovery prompt,
  preferences or leave-page prompt), as with the existing viewer embed.

The existing `?embed&open=` viewer embed is unchanged. SolveApp gets one flag,
`embed_editor`, for desktop and test hosts too.

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

* control: document.export / document.import / document.saved for a host (#8517)

An editing embed's host keeps the design itself. Three control methods
(window.solidcraft.request in the browser; the desktop control channel too; not
the agent chat's, whose method list is unchanged):

- `document.export` -> {name, revision, dirty, format: "json", text}: the active
  design as a `.json` design file holds it. `revision` is the one
  `document.revision` answers, so a host that follows the design compares one number.
- `document.import {text, name?}`: replaces the active design with that text,
  timeline and parameters as saved, opened saved with a new `document_id`. It runs
  as a design opened in a tab of its own, with the old tab closed without asking,
  so whatever was open in the old design leaves with it. A text that does not read
  leaves the design as it was. Waits behind a file loading or a design change, as
  commands do, and evaluates on the window thread.
- `document.saved {revision}`: the host saved that revision; refused when the
  design has changed since.

They are control methods, not commands: they act on the app's open design. The
browser API test gets a round trip through all three.

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

---------

Co-authored-by: aduijs <7501203+aduijs@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 21:23:25 +00:00
Colin Faulkingham 1dd921fc52 Components: stop Capture All Positions from doubling moves (#11259) (#11364)
* Model cache: a body made by rounds keeps them, so a later round blends them the same after opening

A design gave another body when opened from its model cache: the rounds a body was made of
lived only in memory, so a later round at their mitre was built sequentially (or refused, as
too large) after opening and blended with them when evaluated anew. The saved model now
records, per kernel body made by rounds, the body they were made on (a place in the bodies,
stored once though no state holds it) and the edges with their radii; reading it attaches
them back down each chain, and a thinned model keeps those bodies with the bodies made from
them. Model cache format 7.

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

* Tests: a corner patch of rounds 2/3/4 takes no more than the strips leave in its block

three_rounds_at_a_corner bounded the patch's extra cut at 3 mm³ beyond the strips, a figure
with no derivation; SMLib's all-at-once body at radii 2, 3, 4 takes 3.099 (V 7878.579574
against strips 7881.678). That body is the strips' outside the block its patch lies in: its
rounds are the strips' cylinders ending at a's section y = rc, b's x = 20 − rc and c's rails
where they meet a's and b's (the patch's four corners, now asserted), so the patch takes at
most what the strips leave in that block (11.86 at 2/2/3, 32.69 at 2/3/4). The lower bound is
that block; the upper one, at most the strips' volume, stays.

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

* Tests: the io crate's hand-built saved models list their rounds, as format 7 records do

Model cache format 7 (f3cf0c4f3) records a `rounds` list in every saved model, and reading one
without it is refused ("saved model: rounds"), as is a model without any of its other lists:
the states, the preview and the thinned model all came back empty. The io tests' saved_model
and timeline_model wrote format-6 records, so six tests (the round trip, the repeated part
stored once, and the four too-big-for-the-file cases) read nothing. Their bodies are none made
by rounds: the list is empty, and every expectation stays as it was.

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

* SMLib: pin eaa998a (tangency-labs/smlib#68, #69, #70)

Batch 4: drafts and loops, mitre corners, rim chamfers, rulings, tight box, stitch, point
classification, eased corners. Batch 5: a face's tight box divides its cells at the surface's
knots (thread boxes exact); a cylinder, cone, sphere or torus is extended along its own circles.
Batch 6: a revolution's extension closes its turn; a dropped point's foot is the converged one.
The C API is ABI 5 (smlib_blend_dropping and smlib_brep_classify_point are new; nothing existing
changed), so the binding expects 5.

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

* Tests: a strip's tangent and curvature run-offs are the distance long across the surface

SMLib measures an extension's distance across the surface (smlib_extend_sheet corrects
CreateExtendedSurface until the parameter line through the side's middle is that long), so each
run-off of the cylinder strip, swept 20 along z, adds 5 x 20: 10 pi/2 20 + 100 = 414.159 for
both. The parabola-length formula (418.18) was truck's.

Ruling d2 (2026-10-30).

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

* kernel: one smooth angle, and a round too small to change the part is refused

The blend's tangent angle is the shaded view's half degree (smooth_edges::ANGLE), one value
for smooth. A constant round lies r (sec(d/2) - 1) inside the crease it replaces (about
r d^2/8); below the edge's tolerance it changes nothing and is refused before SMLib (which
answered SM_ERR_INVALID_INPUT), naming the smallest radius that rounds,
tolerance / (sec(d/2) - 1). d is the least turn along the edge
(tangent_edge::least_angle_along, at tangent_all_along's stations).

Ruling d3 (2026-10-30). The block's tolerance is 2e-5 mm, not 0.1 um: at 1 deg the smallest
radius is 0.526 mm, so R2 and R20 reach SMLib, which still calls an edge within 2 deg smooth
(SmEdge::GetBlendEdgeuse); that test is ignored until SMLib uses 0.5 deg.

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

* Fillet rig: the knife-edge chamfers name SMLib's cause, a cusp's ball radius of 0

smlib_chamfer turns the distance into a ball radius d sqrt((1 - c)/(1 + c)), c the faces'
binormals' dot (capi chamfer_radius); at #851's cusp the binormals agree, the radius is 0 and
the call is SM_ERR_INVALID_INPUT before the fillet executive runs (gdb: chamfer_edges sees
radius 0; the dump's why is empty). SolidCraft hands every chamfer to SMLib, so the
ball-contact chord (the ball of radius d: 3.822 wide at d = 2, 159.2365 removed) is SMLib's to
build.

Ruling d4 (2026-10-30).

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

* kernel tests: a corner round wider than its faces overflows and takes the corner's other edges

R21 on a 20 mm cube's edge builds (6107.529370, closed form, to 2e-12). With R5 on the
corner's other edges the R21 takes the faces they lie on, so the body is R21's alone; SMLib
fails that blend (status 1001, fillet error 4, Unable to Trim Rail Curve) and the test is
ignored until it builds. It replaces the refusal test that expected TooLarge.

Ruling d8 (2026-10-30).

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

* Tests: a 1 degree crease rounds, and the knife edge's chamfers build, on SMLib smlib-batch-4-rulings

With SMLib's smooth limit at half a degree (422e6f1) the 1 degree crease rounds R2 and R20
(the test is no longer ignored), and with a cusp's chamfer the ball of radius d (d231547) the
The 0.5 one stays, for its own cause (the chamfer face left apart from the body). Two corner
cases the same SMLib (replay/1.0.1 and those commits) builds come off too.

Needs SMLIB_REV at that SMLib once it is pushed.

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

* kernel, doc: an overflowing round goes first; an edge it removes is gone, and the fillet says so

SMLib (smlib-batch-4-rulings 0e83cca, SMLIB_ABI_VERSION 5) rounds the edges whose rounds overflow
their faces first and then what is left of the others; smlib_blend_dropping says which picks'
edges the overflow removed whole. The smlib crate binds it (Brep::blend_dropping), the kernel
returns those picks (fillet_removing), and a fillet notes them as its warning: "the edge at
(0, 0, 10) was removed by the larger round, so it is not rounded" (a 50 x 50 x 20 block, R21 on
its bottom front edge with the front left upright: the block is R21's alone, in closed form).

Ruling d8: [21, 5, 5] on a 20 cube builds as R21 alone (6107.529370); the test is no longer
ignored. R25 with R2 at a corner (blend_corner) builds the same way, R25's alone in closed form,
where it was refused. The rig's 1080 notched block outline at R8 builds now and comes off the
known-failing list.

Needs SMLIB_REV at that SMLib once it is pushed.

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

* Fillet rig: the knife edge's 0.5 chamfer builds

SMLib's trim took a fillet meeting its face at more than 78 degrees for one with no side
(smlib-batch-4-rulings 971e567); the chord of the knife edge's 0.5 chamfer meets the flat face at
81 degrees. The case comes off the known-failing list.

Needs SMLIB_REV at that SMLib once it is pushed.

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

* Tests: a 15° tapered text builds, every letter grown, exact where its outline gives it

Owner ruling d6: the letter t of "tres long" was drafted inward by SMLib (its
loop's normal, summed from the cross products of its chords, pointed down;
fixed in SMLib). With the fix every letter grows, and the join builds at 3°
and 15° by the Round rule and by the default rule (tangent for spline
outlines), where before 15° was expected to be refused.

Exact where the outline derives it: a letter's section at height z is its
outline offset by e = z tan a, by the Round rule of area A + P e + C e² while
the offset trims nothing (Steiner; C half each loop's turning less
tan(t/2) - t/2 at each reflex corner), so the letter is
A h + P k h²/2 + C k² h³/3. Each extruded alone, the t at 3° and s, l and o
at 3° and 15° are checked to 1e-6 (SMLib measures them within 1.3e-9; its
mass properties integrate to 1e-8). The concave joins of r, n, g and e are
tighter than e already at 3° (their swallowtails trimmed: r 1.1e-3 under the
formula), the t's by 15° (9.9e-5 over), and on the block the letters grow
into each other, so the whole word is checked for what holds exactly: one
closed body with the block, and half-way up every point of each straight
letter's outline inside the solid (the t drafted inward failed that).

With SMLib's fix (smlib-batch-4-text-loops 82133b2).

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

* Kernel rig: a sloped basin's mitred rim rounds are checked tangent along their contact lines

1080-fillet-sloped-basin-1-mm-rim-r0-5-4-edges asked G1 at every blend seam. Its front wall
leans 19 over 20 and the others 10 over 20, so at each rim corner the two mitred rounds meet the
sloped corner edge at different heights (0.155625 and 0.276393 below the top): the deeper round
runs on past the corner edge and is cut by the plane of the other's wall, a crease by
construction, the gap below the shallower round filled on that plane. The check now measures
the rounds' contact seams, on the top and on each wall, where the rounds are tangent. The
volume is unchanged.

With SMLib smlib-batch-4-mitre (1c15364): the case passes (V 144530.427946 against
144530.427909, 15 faces, sound); before it, only the four corner creases failed the check.

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

* smlib: Brep::classify_point says exactly where a point lies against a body

Binds the fork's smlib_brep_classify_point (SmBrep::Point3DClassify at the body's own zone
tolerance): inside, outside, or on the boundary within that tolerance of a face, an edge or a
vertex. A box and a cylinder just off its curved wall in measure's tests.

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

* kernel: Body::classify_point decides inside, outside or on the boundary exactly

SMLib's point classification at the body's own tolerance, on the exact faces and edges, in
place of a mesh at a chord of 2e-3 of the body's size (about 0.4 mm on a 200 mm part). A point
within the tolerance of a face, edge or vertex is OnBoundary, never decided by a ray. A mesh
body's triangles are its exact shape: on them within the record's tolerance, else by ray
crossings. The kernel's own probes move to it; Body::contains goes once its last callers have.

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

* doc: a hole decides whether it exits at its drill point's shoulder exactly

Material past the depth needs a body with the point inside, air before it needs every body to
have it outside; a point within a body's tolerance of the exit face is neither.

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

* engine tests: probes ask for the exact point class

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

* ui, cli tests: probes ask for the exact point class

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

* kernel: Body::contains, the mesh test, is gone

Every caller asks Body::classify_point now.

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

* kernel tests: the rim chamfers running off their rims build, on SMLib 737c392

chamfer_wider_than_sloped_basin_rim_builds and a_rim_loop_chamfer_running_off_its_rim_builds
waited for SMLib to build the cliff overflow (CloseRemovalRegion refused it,
SM_FILERR_NO_ROOM). The pinned SMLib builds it (eddf0ede4, bba9914b6): both pass on aarch64
Linux and are no longer ignored.

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

* Fillet rig: the cases SMLib 737c392 builds come off the known-failing list

The gate on aarch64 Linux reports 16 fillet cases FIXED with the pinned SMLib: the sloped and
equal-slope basin rim chamfers and rounds past their rims (1080-*, 5835-*), the pocket rim
chamfer past the floor, and the three pocket rim rounds now mitred at the pocket's corners
(every-pocket-*). No other area's gate reports a case fixed.

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

* kernel: the re-blend's removed picks formatted as rustfmt lays them out

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

* kernel: blend_smlib laid out by the 2024 edition's rustfmt

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

* Skills: the tests skill's unit-test example stays under the 3600-character limit

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

* Fillet: the radius-too-large choice in plain nouns

"If too large: Stop and say so / Largest that fits / Narrow where needed"
becomes "Face limits: Exact radius / Largest radius / Narrowed radius", in
every language of the catalog, with the error and the tip that name them.

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

* kernel: a round that would cut through its wall into a hollow is refused

The owner's rule of 2026-10-30: a round running past a rim of its faces
into open air builds; one whose section would remove material through to
a hollow behind its edge (a slit into the hollow, a cut through the part)
is refused as too large, so the fillet names the largest radius that fits.

Decided exactly in the section of a straight convex edge between planar
faces: the round's centre is at depth r below both planes, and it opens
where its arc uncovers a boundary point strictly inside the wedge with no
material beyond it toward the centre (the body's exact point
classification). Looked at: straight edges parallel to the round's (a
hollow's corner), cylinders about parallel axes (a rounded corner), and a
picked concave parallel edge's own round, which fills the corner.

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

* Fillet rig: 1668's R7 past the inside corner is refused at 4 + 2 sqrt2

The round's centre (32 - r, 42 - r) leaves the hollow's corner (30, 40)
outside its circle, opening the wall, where sqrt2 (r - 2) = r: the case
expects the refusal naming 6.828. 2852's R30, refused naming 25.24, now
passes too: both come off the known-failing list.

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

* xtask: known-failing grades a nextest junit report against tests/KNOWN_FAILING_TESTS

`cargo xtask known-failing --junit PATH` reads nextest's junit (a file, or every .xml under a
directory, so all of a run's junit artifacts at once) and fails on a test that failed and the
list does not name, and on a listed test that passed (FIXED: take its line off, as the kernel
rig's gate does). A listed test that did not run is neither, since a pull request tests only the
packages it touches. The list has the rig's KNOWN_FAILING format (`<test> <cause>`, sorted, the
rig's own parser), with `<test>` nextest's `<binary id>::<test name>`; `cargo xtask lists`
checks it is sorted. The list starts empty.

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

* CI: the test jobs grade their junit against tests/KNOWN_FAILING_TESTS

Native Linux tests and the GPU path test shards no longer fail on any failed test: nextest's
"tests failed" exit (100) goes to `cargo xtask known-failing --junit`, which fails on a failure
the list does not name and on a listed test that passes. Any other nextest exit (a build error,
a bad filter) still fails the step at once. The tests skill says to list a failing test with its
cause, in place of ignoring it.

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

* Tests: the known-failing list takes develop's 10 failing tests

Develop 38fd4a4f6 (CI run 41157648345, after the SMLib eaa998a pin) fails 10 cargo tests; each
is listed with the cause its failure message gives. Seven are SMLib refusals or results off
their derived volumes (fillet, chamfer and shell), the cli action matrix and naming corpus
among them; the two corner_g2 tests are the fitted G2 corner patch bulging past the box, which
the explicit G2 corner fixes.

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

* Reference tests: the eight cases that fail on SMLib eaa998a name their cause as known failures

Develop 38fd4a4f6's Reference tests fail 34, 35, 77, 78 and 81 (STEP export finds fillet edges
4.6e-5 to 6.1e-5 off their surfaces against 2e-5), 37 (the apex blend 0.193 off), 80 (the
taper-to-fit area) and 83 (thicken status 1001). Each expected.json names its cause in
known_failure, which the oracle already reads: a known failure that passes fails the job.

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

* Sweep path: a curve of several pieces is turned round whole

The path chain oriented its first piece, not its first curve: a first curve
of several exact pieces (a fit spline, an ellipse, a projected free-form
edge) was never turned round, so a path whose next curve meets it at its
start was refused as "not connected end to end". The chain now orients
curve by curve: the first runs toward the second where they meet at one
end only (else from the end nearest the profile), each next one starts
where the previous ended, with one joint tolerance for both questions.

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

* SMLib: pin 2fc375f (tangency-labs/smlib#71, #72, #73)

A sheet's side is extended exactly the distance asked, or the call says it cannot (#71);
NVIDIA's -Werror build passes again (#72); a body splits by a sheet of several faces as it
is, smlib_split_body_by_sheet (#73). The C API is ABI 6 (the new entry), so the binding
expects 6.

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

* kernel: Extend grows a surface of several faces face by face

A surface body of more than one face (a fit-point spline's spans extruded, #3830) was refused.
Each face with a picked edge is now extended alone by SMLib's extension of its surface, and the
faces are sewn back together, each the face it was. A side that lengthens onto a face meeting it
tangent takes that face along, past its edge continuing the picked one; across a crease the
neighbour grows only when its edge is picked too, otherwise the extension is refused. Faces whose
lengthened sides no longer meet are refused. Up to a target keeps the pieces every grown face
lies in.

3830's extensions now build; its split still fails (the tool point picks one span, and a face
tool cuts with that face's surface alone), so it stays on the list with that reason.

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

* kernel: Split Body by a face of a smooth sheet cuts with the whole sheet

A face tool cut with that face's surface alone, so a face of a surface body made of spans joined
tangent (a fit-point spline extruded, #3830) cut nothing where its span ends inside the body.
Split Body by a face of a surface body now takes the faces joined to it tangentially (the exact
check Extend uses, within the one angle for smooth) and slices with them as they are, through
SMLib's slice by a sheet (smlib_split_body_by_sheet, tangency-labs/smlib 7a01f1b), its sides those
of the picked face. A sheet that only nicks the body cuts nothing, as before; a lone face, or a
face of a solid, still cuts with its surface extended.

3830 comes off the booleans list. Its answer counted the two halves of the cup and not the surface
that cut it, which stays as a body of its own (4879 removes its surface for that reason): three
bodies, the volume unchanged.

Needs SMLib 7a01f1b pinned (SMLIB_REV, prebuilt; its SMLIB_ABI_VERSION 6, which crates/smlib now expects) to link.

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

* Tests: a cylinder strip's run-offs are 100 pi + 100 to 1e-6, SMLib's exact area

The tangent and curvature run-offs of the quarter cylinder r 10 strip, 5 across the surface,
are its section swept 20 along z: exactly 10 pi/2 20 + 5 x 20 = 414.159265. measure is SMLib's
integration on the exact surface (1e-8), not a mesh, so the run-off check is 1e-6 relative and
the 'measured on meshes' notes go. Red until SMLib's extension is solved to its distance
(smlib-extend-converges 590d8a1): eaa998a's curvature run-off is 4.986 long, area 413.882.

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

* kernel: an extension SMLib cannot make exact says so, naming the length asked

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

* Sketch: a control spline can carry its own knots and weights

A sketch spline over control points was always the clamped uniform B-spline
over them, so a free-form edge whose knots are not evenly spaced (or whose
weights are not 1) could only come into a sketch as one curve per Bezier
span. A control spline now may save `nurbs`: its clamped knots and its
weights. Its points, polyline, exact pieces (one a knot span; conics for a
rational quadratic), the solver's distance and curvature, links, parallel
re-projection and inspect.sketch all read them; a loaded design's are
checked. One pending format file (spline-knots).

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

* Sketch projection: a free-form edge comes in as the one exact spline it is

A projected spline edge of several Bezier spans came into the sketch as one
curve per span (#1508), so a G2 round's bottom edge was 16 curves and a path
naming the corner named one span of it (#3604). The kernel now reads such an
edge (a spline, or an elliptical arc of several spans) as the one clamped
B-spline it is over the edge's interval, its knots and weights its own, and
the sketch takes it as one control spline whose ends sit on the edge's
vertices. Planar regions still read edges span by span. The 3604 case builds
with its original path and leaves KNOWN_FAILING.

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

* Fillet rig: blends past an outer rim of their faces build, on SMLib smlib-blends-past-rims

The owner's ruling: a round or chamfer that runs past an outer edge of its face into open air
builds, tangent where it has room and cut by the face beyond where it runs out. SMLib's branch
smlib-blends-past-rims builds them by the rig's analytic answers. Off the known-failing list:
the disc's rim past its 2 mm wall (190), the hole's top rim R12 past the plate's bottom, the
chamfer past the cylinder's bottom (2893), each closing on the wall continued as itself, and
926's chamfer ending on the boss's wall, which now builds to its answer too.

The sloped basins' R4 rim rounds (1080, mitred rounds built together where one overflows) build
to their analytic volumes (to 6e-9), and stay listed with that reason: their continuity check
(g1 at every blend seam) takes in the mitres' creases, G0 by the mitre ruling.
2893-chamfer-two-distances-elliptical-rim is graded as a solid (the owner: it builds) and stays
listed, waiting on the ruling on unequal chamfers along a rim whose angle varies.
5158-fillet-round-just-past-a-10mm-face stays listed with its cause found: the round passes
5e-6 under the box's top back edge, inside the body's tolerance.

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

* Naming: test that a round of a round's end has no seam with that round's faces

The r1 round of a block's top edge, chained onto the end curve of the r2
round of its arc at a cylinder (rig fillets/every-block-cylinder-crease-
top-edge-after-arc-round-r1), ends where it runs into the cylinder. That
edge is no seam of the r1 round: the cylinder is a face of the r2 round,
not of the r1. blend_seams takes it for one today (the r2 round's name
F3:blend:F1:+z|F2:side holds a | of its own), and the rig reads it as a
contact seam at 60 degrees.

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

* Naming: a blend's seams split its edge's name at the | between its two faces

A blend face's name holds the edge it replaced, <face>|<face>, and a side
of that edge can be a blend face whose own name holds a |. Whether a face
is a contact of the blend was read as the edge's name starting or ending
with the face's name next to a |, so the r2 round's own face F2:side read
as a contact of the r1 round F4:blend:F1:+y|F3:blend:F1:+z|F2:side. The
edge's name is now split into its two faces, a blend side read whole as
the two faces of its own edge.

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

* Fillet rig: five refused blends build, off the known-failing list

926 (the chamfer ending on the boss's wall), the oblique cut's r0.1
round, the every-block r1 round (its seam with the cylinder is no
contact seam, after the blend_seams fix), the up-down squares' concave
chamfers and the shelled box's lip chamfer build with SMLib branch
smlib-blend-refusals (f8650b8 and later). 1207 r0.5 passes SMLib's check
on its rails now and stays listed for the next cause: its end rib on the
boss, which starts where the round and the boss are tangent.

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

* Kernel rig: the trough and the pocket whose floors fall to a drain build

SMLib now offsets a vertex where four faces meet (two walls and two facets at the floor's
corners, four facets at the drain) to two vertices joined by a new edge (tangency-labs/smlib
branch smlib-shells). Both volumes match their closed forms. The pocket's lining, hanging from
the top's rim round the pocket, stands apart from the slab's outer walls with the cavity rising
between them to the open top: the case says two shells in the one body.

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

* Kernel rig: the plate with a hole through its arc side shells open at its far end

SMLib's merge now intersects the offsets of an edge's faces where they could not be mated along
it (the hole's offset poking out through the half-round side's; tangency-labs/solidcore#1795):
one closed valid solid with the one face open, as the owner ruled.

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

* Kernel rig: the ring cut frame and the up-down join shell

SMLib's inset now keeps only the regions that are cavity, not the one the offsets close where
they overlap (between the two faces of #545's 0.5 floor, round the up-down join's two empty
octants). The frame's core under the recess is hollowed to a sealed void, so the case says two
shells and its closed form, 21044 mm³.

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

* Kernel rig: #616's lid shells open at a rib's side, with the 0.15 lip

SMLib keeps the skirt top's offset where its loops wind the other way under the opening, the
cavity's floor (the owner's ruling of 2026-10-31: build it; a lip thinner than the wall is no
break-through).

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

* Known failing tests: the action matrix's shells and chamfers as they fail with SMLib batch 7

The ring cut frame's and the up-down join's shells build now, and so do the up-down join's and
modern-plastic's chamfers (SMLib smlib-blend-refusals). #616's lid in the corpus still fails
SMLib's loop check, the shelled box shelled again at an inner wall is unchanged, and the
oblique cut of a cylinder's fillet still fails SmSurfOfRevolution's Test #7: 3 actions of 7.

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

* SMLib: pin 26cc451 (batch 7: rounds past a rim, blend refusals that build, shells)

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

* Known failing tests: the shell after a cut whose end touches an arc side builds on SMLib batch 7

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

* Surface fair test: the energy falls strictly, the face moves, and the edges are read after the fair

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

* Sketch scaling test: the first dimension sets l1 to 40 and scales both joined rectangles about the origin

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

* Frames tests: a pick on a moved component or an instance removes the exact round and bore volumes

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

* Issue 1080 tests: the rib's rounds and the mitred corner chamfers and rounds have their exact volumes

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

* Engine tests: lip_after_boss checks each lip's and groove's exact band, hole_as_deep_as_the_plate each hole's exact bore

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

* Robustness tests: lip_after_boss and hole_as_deep_as_the_plate live once, in the engine's tests.rs with exact volumes

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

* Engine tests: loft_cut goes; loft_spacing's hollow elbow covers its section counts and more

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

* Kernel loft test: the square-to-circle loft has the oracle's exact ruled volume, without printing

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

* Tessellation lens test: the lens's triangles fill exactly the rhombus through its arcs' midpoints

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

* Pattern test: six chamfered holes take exactly five more holes' bore and chamfer rings

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

* Kernel pocket floor fillet test: the corner rounds and the floor loop round add their exact volumes, by Pappus at the corners

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

* Shell regression: the saddle-corner shell opened on the top box's side has its exact volume

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

* SMLib history test: every operand's order is the identity on its record's faces and edges

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

* Boolean history tests: the SMLib module's box-minus-sphere, box-minus-cylinder and box-union-box copies go; the top-level ones check more

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

* Modify history tests: shell, offset and thicken are one test each through the public API, with the volumes, refusals and history the SMLib copies checked

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

* Chamfer history test: the chamfer after each box cut is exactly one face on the painted block too

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

* Grid test: a pole side's face is the five triangles fanned from its one grid node, none degenerate

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

* Kernel tests: the twelve ignored print-only debug tests and surveys go; coplanar_curved_booleans checks the flush-capped cylinder cuts and unions they printed

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

* Kernel: the #883 board fixture generator is an example, not an ignored test

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

* STEP import test: a seam sphere whose meridian stops short of the pole imports as the whole 2 mm ball

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

* Material tests: the nine copies in material_properties go; the persistence file checks the library copy's card and formats 20 and 21 in one loop

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

* Sample test: one loop checks every sample builds cleanly and without a panic; the robustness copy goes

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

* Known failing: the non-antipodal seam sphere import, refused for a loop of no (u, v) area

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

* Tests: rustfmt the board fixture example and the coplanar asserts

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

* Kernel rig: the two 3871 cone cases were byte-for-byte the 258 cases; the 258 cases carry their sources

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

* Kernel rig: a volume or area can be graded as a hand-derived range where analysis only bounds it

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

* Kernel rig: fillets keeps one case per regime; 17 replications of a case in the same regime go

The sloped-basin chamfers and rounds at 0.5 mm rims repeat the 1 mm rim's regimes; two of the
three knife-edge chamfer tips, the r1 and box r3 curvature corners, the r1 tangent corner, the
1 mm pocket-rim and hole-rim chamfers short of the floor and the r1 together-round each repeat a kept case's regime
and grades. #854 moves to 851-chamfer-arc-top-edge-9-5, and the cases that cited a dropped one
cite the kept one or state its formula.

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

* Kernel rig: five cases outside fillets repeat a kept case's regime and go

A square cut is the slanted plane's ordinary crossing; the cam's half turn is the quarter turn's
contact at a symmetric angle; the r15 slab stands inside the same coincidence tolerance as the r10;
the 90 degree unfold and the 40 degree flange repeat the 72 degree and 20 degree cases' formulas.

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

* Kernel rig: 3243 rim and tooth join graded on its volume (rim plus the trimmed tooth less their 0.01 mm overlap)

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

* Kernel rig: 3782 emboss on a cylinder graded on its volume, area, faces and box (the wrapped 4 x 5 badge raised 1 mm)

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

* Kernel rig: 3604 G2 swept-tool cut graded on a volume range (filleted box minus the swept corner section)

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

* Kernel rig: 4505 coil joined through a cylinder wall graded on its volume, area and box (cylinder plus the wire's screw-swept segment outside r 76)

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

* Kernel rig: 4879 window cut through a thickened spline revolve graded on a volume range and its faces

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

* Kernel rig: 4623 join of crossing pleated loft skins graded on its volume and box (both skins less their overlap)

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

* Kernel rig: 831 tapered glyph extrude graded on a volume range and box (the font's S drafted in by 10 deg)

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

* Kernel rig: 2357 ring of 90 cylinder segments graded on its volume and box (the segments less their 90 joint overlaps)

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

* Kernel rig: 4542 fluted wall joined on a revolved base graded on its volume and box (the base by Pappus plus the fit-spline ring)

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

* Kernel rig: 1985 and 2243 STEP-export enclosures graded on their volume and box (the outline less the cavity, plus gusset and standoffs)

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

* Kernel rig: both 4092 loft, ribs and pad unions graded on their volume and box

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

* Kernel rig: 1525 graded on its volume (the convex body's exact 2 mm inward offset), faces and box

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

* Kernel rig: 1494 graded on its volume (box and cylinder less the 1 mm-offset cavity, sharp to rounded concave edges) and box

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

* Kernel rig: 4492 graded on its box and a volume range (the face's 19 mm sweep added, within the block's sides)

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

* Kernel rig: 745 graded on its volume (the end moved 1 mm plus the spline boss's exact area), faces and box

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

* Kernel rig: 4567 graded on its volume (the revolved spline less its revolved 2 mm offset), faces and box

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

* Kernel rig: the up-down squares shell graded on its volume (the columns less the 0.346 mm-offset cavity, sharp to rounded concave edges) and box

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

* Kernel rig: 545 graded on its volume (the frame less its 0.5 mm-offset cavity, sharp to rounded concave edges), its sealed pillar void and box

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

* Kernel rig: the shell-again case graded on its volume (the shelled box less the 0.5 mm-offset cavity in its walls) and box

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

* Kernel rig: the arc-sided plate shell graded on its volume (sharp to rounded at the hole's bottom), faces and box

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

* Kernel rig: 616 graded on its volume (the lid less its 0.5 mm-offset cavity, sharp to rounded concave edges) and box

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

* Kernel rig: 391 graded on its volume (the shelled housing, sharp to rounded at the flange, less the letters' 0.6 mm deboss) and box

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

* Kernel rig: shell/3576 was the ladder's drafted box with rounds shelled, recipe for recipe; the ladder case grades it and carries #3576 and #3573

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

* Kernel rig: the two 851 chamfers of 2 graded on their volume (the cone's sweep, and its union with the front edge's prism)

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

* Kernel rig: the chamfered corner of the lipped shell graded on its volume (the chamfer's triangle through the wall)

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

* Kernel rig: the concave round into the peg's cylinder graded on a volume range (the torus it must add, the band it can't leave)

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

* Kernel rig: the two folded 72 degree trays graded on their volume (flat area x thickness) and the walls' reach

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

* Kernel rig: the flanged rhombus graded on its volume (flat area x thickness, all four reliefs cut) and its walls' ends

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

* Kernel rig: the base with two adjacent flanges and a hole graded on its volume (base, bends and panels folded)

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

* Kernel rig: the flange beside the ledge graded on a volume range (the flange's two readings of its length)

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

* Kernel rig: the two G0 window patches graded on the box the part they close keeps

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

* Kernel rig: the hub's six swept grooves graded on its volume (the revolved hub less each tube's part inside it)

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

* Kernel rig: the cut mirrored loft graded on the box its sections pin

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

* Kernel rig: the three modelled-thread joins and the fine M48 graded on their volume (the basic profile's groove, Pappus)

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

* Kernel rig: the oblique-cut round graded by its seams (tangent, not curvature-continuous); the flanged rhombus says what it tests (its reliefs stay apart at 60 mm)

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

* Kernel rig: 3841 is 168's rim touching its coin along a circle, and 4092's reduced recipe is its report less a flange clear of the failing join; the kept cases carry their sources

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

* Kernel rig: the oblique-cut round keeps its old grade until the new blend algorithm lands

Its seams grade needs the continuity check to skip the body the round is not on. Fillet cases
wait for the new algorithm.

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

* Kernel rig: 1985's enclosure is 78 x 77 (74 and 73 inside, 2 mm walls); its volume 38063.827 by hand

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

* Kernel rig: the three spline walls' face counts wait for the one-wall decision; their volumes grade them

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

* STEP import: a face bounded by seams alone runs its loop the other way, never turns over

A loop that uses each of its edges once each way (a whole torus within its tube and ring seams)
is the same set of uses run backwards, so it says nothing of the face's side. One written
clockwise in (u, v) turned the face over, against its same_sense: the torus came in inside out
with a warning. Such a loop is now reversed and the face keeps its side.

Test: seam_only_torus_imports_as_a_whole_ring, both orders of the two seams, volume 2π²Rr².

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

* STEP import: a face bounded by a slit alone is the whole of its closed surface

A loop that runs out along a chain of edges and straight back (a meridian from a sphere's pole
stopping short of the other, out and back) encloses no (u, v) area, yet on a closed surface it
bounds all of it less a cut of none. The reader only read such a face when the slit already ran
pole to pole (and moved the sphere's frame onto it only then), so the 2 mm ball whose meridian
stops at 2.5 rad was refused for its empty loop.

The slit is now made the face's seams, so its loop bounds the surface's whole domain: on a
sphere, a revolution whose profile ends on its axis, or a spline closed round u that collapses
at both ends, the slit's meridian is run on to both poles, out and back (a sphere's frame put
on the slit first, whichever two points it joins); on a torus, the circle the slit lies along is
closed and the other circle through its start is the second seam. The seams on a sphere are its
own meridians (angle 0 on the equator), so their pcurves are exact lines on its form.

Tests: non_antipodal_seam_sphere_imports_as_a_whole_ball passes (its known-failing line goes);
seam_only_torus_imports_as_a_whole_ring adds a torus bounded by a slit along its tube.

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

* Rig: the test-peg's concave arc and front top edge rounded together stay within the body's box

A new fillet case, report-fillet-concave-arc-and-front-top-edge-together-solver-residues:
the reported part's bar (with the sketch solver's residues) whose sides run tangent into the
cylinder, its concave arc and front top edge rounded r2 together. The plane of the front face
bounds the whole body, so every blend lies within the unblended body's exact box (SMLib's tight
box); graded to 4e-5, the corner patch's own tolerance. On SMLib 2fc375f the corner patch
stands 0.0374 out of the front face (box y min 0.0205): listed in KNOWN_FAILING.

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

* Rig: the test-peg corner at r5 and r7

Two more fillet cases, the reported part's concave arc and front top edge rounded together at
r5 and r7, with the same exact-box containment check (graded to 4e-5, the corner patch's
tolerance). On SMLib 2fc375f the corner patch stands 0.128 (r5) and 0.230 (r7) out of the
bar's front face (box y min -0.0699 and -0.1723): listed in KNOWN_FAILING.

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

* Rig: blends that end where their face runs tangent into another

Fifteen fillet cases on the test-peg's shape with clean numbers: a bar 40 x 16 x 10 whose far
end is a half disc R8, a disc R8 standing 10 on its top, joined, so the front face runs tangent
into the cylinder along x = 40 and the front top edge (convex) meets the concave arc of the top
tangentially there. Each blend ends on a face tangent to the one it rolls on.

- Rounds: both edges together at r2 and r5; one after the other in either order at r2, and at
  r1.5 and r3; the front edge alone as a curvature round, whose end on the cylinder is a
  semicubical cusp. The containment invariant (the unblended body's exact box, to 4e-5) and
  G1 seams, or the analytic volume where the blend has a closed section.
- Chamfers: the front edge alone and the concave arc alone (analytic volumes: the section along
  x up to the wall, the cone's spandrel ring clipped by the sides), both together, and one after
  the other (1 then 2).
- STEP: the peg written to STEP and read back, its front edge rounded (analytic) and both edges
  rounded together.
- A cone: the peg drafted 5 deg, whose front face runs tangent into a cone along a generator,
  its front edge alone and both edges together.

The quadratures are tanh-sinh, converged to 1e-13; SMLib's own volumes of the passing analytic
cases agree to 4e-9. A sphere is left out: a plane meets a sphere tangentially at a point, not
along a seam, so it is not this ending.

On SMLib 2fc375f eight fail, listed in KNOWN_FAILING: every chamfer that ends tangent (status
1001, SmFilletNx1Corner), the two rounds together (the corner patch 0.0374 at r2 and 0.128 at r5
out of the front face, and the rounds meeting with normals 180 degrees apart where their
contact lines on the top cross), the same on the STEP-read peg (status 1001) and on the drafted
peg (its corner refused as unsupported).

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

* Rig: fillet corners graded curvature-continuous, the default to be

Six fillet cases that pick their edges with no type, so they take the fillet's default, which the
owner has ruled is to be curvature-continuous (G2) at every corner, with circular rounds kept as
an option. Each grades every seam of the rounds and of the corner patch G2, on one valid body:

- an oblique convex corner (a block drafted 15 deg);
- a four-edge and a five-edge apex corner (pyramids lofted to a point);
- a pocket's concave corner;
- a mixed corner (a step: one concave edge, two convex);
- a corner beside a cylinder (the top corner of a half cylinder).

All six fail on SMLib 2fc375f, listed in KNOWN_FAILING: today's default is the circular round,
whose seams jump in curvature by 1/r. On the five-edge apex the corner patch also meets two
rounds at 8.45 degrees (G0).

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

* Rig: the test-peg's front chamfer builds

With the SMLib fork's smlib-tangent-ends (a rail's end on its side edge is the rail/edge
crossing), report-peg-front-chamfer-d2 passes. Its line leaves KNOWN_FAILING, for the pin that
carries that fix.

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

* Rig: the curvature-continuous corner cases pick the curvature fillet

The six corner cases added in 3a854104a took the fillet's default type, on the premise that the
default would become curvature-continuous. The default stays the circular round, so each case
now asks for the curvature fillet itself (type curvature: curvature-continuous sections and
corner patches) and keeps its grading: every seam of the rounds and of the corner patch G2, one
valid body. They are renamed from cov-fillet-g2-default-* to cov-fillet-g2-*.

The oblique three-edge corner (a block drafted 15 deg) and the pocket corner pass. Four fail, as
KNOWN_FAILING now says:

- the corner beside a cylinder: the curvature fillet builds its sections only between planar
  faces;
- the four- and five-edge apex corners: refused because the default setback is shorter than
  where the rounds' contact lines meet (3.90 and 3.51 mm from the apex);
- the mixed corner: SMLib sets back a corner only where its edges are all convex or all
  concave.

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

* Rig: the test-peg's concave chamfer, alone and after the front chamfer, builds

With SMLib's tangent end as an event of the fillet's trace (blend-end-event 6d73e4f), the
concave chamfer's rail on the cylinder ends where it crosses the seam along which the front
face runs tangent into the cylinder, continued past the vertex, and its end rib on the front
face is built from there. report-peg-concave-chamfer-d2 and
report-peg-front-chamfer-d1-then-concave-chamfer-d2 pass and leave KNOWN_FAILING. The two
chamfers together still fail, now at the mixed-convexity corner where they meet: its line says
so. Rig against SMLib 0cc48bd: no area regresses (963/993 pass, 961 on a87c502), ladder 168/168,
kernel lib only its listed failures.

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

* Kernel rig: constant rounds of the exact classes, and a tee's round

A plane cutting a cylinder or a cone at an angle, a sphere or a cone coaxial with the other face, a
cone and a cylinder touching one ball, two pipes touching at a point: their rounds' centre lines
are rational, so the rounds are rational surfaces of degrees fixed by the construction ((4, 2),
(2, 2) revolved, (6, 2), (12, 2)). The tee's centre line is not rational; its round is graded on
its volume. Volumes from the divergence theorem over the exact pieces, at rel 1e-6. The six that
SMLib 0cc48bd traces are listed in KNOWN_FAILING with what they come out as.

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

* Kernel rig: the exact-class rounds pass on SMLib's classified exact rounds

The rounds now come as the rational surfaces their classes give, closed ones as two faces split
at their seams, the touching pipes' as four: one splines entry a face, each of the class's degrees.
Their KNOWN_FAILING lines go.

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

* SMLib: pin 7ac037e (the new blend's tangent-end event, zero-speed G2 ends, and its exact-class rounds)

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

* Kernel rig: the fillets known-failing list is the 22 cases that fail on SMLib 7ac037e

Twelve listed cases build on the new blend's end event and exact classes, or were already
built on develop: they come off the list.

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

* Kernel rig: #316's bottom rounds as large as the wall, or larger, shell with a step wall at the opening, not refused

Where the round meets the open bottom the cavity's wall steps down along the bottom's normal, so the
round meets it at 90 degrees and the opening is exactly the picked face. The cases are renamed and
graded on the exact volumes, and come off the shell known-failing list.

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

* Merge fixes: every crate and test target compiles on the merged tree, but for crates/geom's hlr

world_state, view::bounds, home_camera and scene return Result on develop; new
main code takes that with ? or session_world. New fixtures carry the spline's
nurbs. ForeignCopy::of returns Result. occurrence_placement_with walks the path
with its moves, since the placement index holds pending_moves only.

The hlr port is separate and still to come: this commit does not compile as it
stands in crates/geom/src/hlr.

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

* Drawings hidden lines: read develop's B-rep records

Edges come from the record's edge list, cut to their interval (C3::of_edge), and
faces from its face list; a cylinder, cone, sphere or torus chart takes its bounds
from where its NURBS form lies on its label (S3::of_face). A rigidly placed
revolution has main's revolution chart; other placements stay refused. Moving a body
to its centre goes through Brep::moved. Loops are read against the natural normal of
the face's parameters where a chart wraps.

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

* Hidden-line fixtures: build develop's records from the fixtures' data

Fixtures keep their bounded curves and surfaces and their own edge numbers; a private
builder makes the canonical record, and the local prepare puts the edges back in the
fixture's numbering. Assertions are unchanged.

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

* Hidden lines: tests of the bounded edges, face charts and placed revolutions

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

* drawing-hlr doc: what is drawn and refused on develop's records

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

* hlr tests: a placed revolution is refused when its placement is not rigid

The new model has one record for main's Revolution, which was drawn, and its
PlacedRevolution, which was refused. An identity placement is now the former, so
this test's fixture takes a non-rigid one. The refusal it asserts is unchanged.

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

* Hidden lines: a traced intersection is drawn only where its spline lies on both surfaces

A leader off either surface is refused like a curve with no spline to follow. Tests: a traced rim
drawn on the circle and plane within tolerance, the same leader 1 mm off refused, and kernel bodies
with arcs through the production prepare against closed forms.

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

* Hidden lines: kernel bodies through the production prepare against closed forms

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

* Tests from main: the world state is a Result here

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

* A blend's pick measures close edges on their curves, not their polylines

A polyline can be a chord off its edge, more than two edges are apart near a
tangent point. A pick exactly on a ball's rim took the straight edge the rim is
tangent to, and solidcore built a chamfer of that edge with a clean check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MNXLXkZLQ6T7TSi4wswcmk
(cherry picked from commit c786db6bf4)

* Merge fixes from the first SMLib test pass

Model cache key covers the crates develop's kernel adds under doc (profile, smlib, step).
A piece taking a name also takes the feature that made it: SMLib lists a cut's pieces in another
order, and the swap left the name on a piece of the wrong feature.
A replaced analysis keeps its status slot. A command with no world picks does not place the model
first, so assembly.mass_properties answers for a flattening placement. FeatureCommon loses the
touching key the merge added; only the contracts that read it take it.
Two of main's tests listed as failing on SMLib, with why.

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

* Merge fixes from the ui test pass and the gates

Main's thread.direction keys go with develop's thread hand, which replaced them. Vietnamese for the
messages develop added while main made vi a complete catalog. CGA and 1-bit themes set develop's
comb and section hatch colours. A ribbon button with a persistent effect is pressed only while its
tool runs, so persistent_ribbon_pressed alone decides how its effect is shown; other analyses stay
pressed while on. Two of main's ui tests listed as failing on SMLib, with why.
Gates: fixed thread tables become slices, one collapsed if, rustfmt.

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

* A silhouette takes its facing from the surface's normals

A flat triangle beside the outline can tip the other way from the surface it approximates, and its
edges joined the outline. On SMLib's sphere that made 192 segments instead of the equator's 64, 100
curves and 95 profiles from the tight fit of #7989. Each corner's normal is used where it is close
to the triangle's own; a mesh without such normals (none, zero, not numbers, or averaged across
creases) keeps the triangle's normal, as before.

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

* Clippy and rustfmt on the merged tree

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

* Stepwise open checks every feature under the session's mode

OpenSteps::start evaluated under the default check mode, and finish kept those results under the session's mode: an each_step or off session opened in steps recomputed nothing. start now takes the mode (the app passes its session's), and a mode that changed before finish evaluates the design again under the new one.

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

* Clippy without SMLib: the planar-sheet edge reader is built only with it

Its one caller is in the smlib-only sheets module, so without the feature it was dead code and
clippy -D warnings refused the kernel.

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

* SMLib: pin d01d7b0 (the new blend's one Gⁿ section generator: the chord, circle and curvature sections, and the variable rounds as the union of their balls)

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

* Kernel rig: the section orders, chord, circle and curvature section, on planes and against a cylinder

Eight fillet cases for the blend's cross-section by its order of contact with the faces, each
graded on its analytic volume at rel 1e-7 and its exact continuity (the order, and not the next):

- a box's top back edge chamfered 3 (G0), rounded r3 (G1) and curvature-rounded 3 (G2): the
  areas cut off the corner s^2/2, (1 - pi/4) s^2 and 113/756 s^2, the last by the default
  thumbweight's definition;
- a pipe R10 dipping into a plate, its concave edge chamfered 2 (the chord between the contacts of
  the ball SMLib rolls for that setback, radius 1), rounded r2 and curvature-rounded 2, each
  section's added area by Green's theorem along the plate, the section and the pipe's arc; the
  order-2 section is built from the faces' feet of the point at distance d from both, which on
  the pipe are not linear in d;
- a boss root curvature-rounded 2, the square corner's section revolved (Pappus);
- a variable round r1 to r5 along a box edge (rel 1e-5): the balls' envelope is a circular cone,
  and the volume is the cone's sections integrated along the edge.

Three fail today and are listed: the two curvature rounds whose faces are not a square corner of
planes (SMLib's quintic blend curve, not the order-2 section: rel 6.5e-7 and 1.2e-5) and the
variable round (great circles, not the envelope's: rel 4.8e-5). The other five pass on SMLib
7ac037e.

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

* Kernel rig: the curvature fillets' expected bodies from the order-2 section at thumbweight 0

SMLIB_BLEND_CURVATURE's section is now the order-2 G^n section at thumbweight 0. Between two
planes square to each other it cuts A = 0.154192655384275 s^2 off the corner, its first moment
about either face 0.0291753295725360 s^3 (Green's theorem on the section): the curvature cases
built on that corner (862, 2708, every-box-edge, every-hole-top-rim, every-l-bracket,
every-block-cylinder-crease, the three-edges-at-bar-corner reports, 3604's fillet) take their
volumes from it by their own methods, the peg's front blend 23.3237146876 (its section along x
up to the wall). Between planes the section's t^3 term vanishes (their feet are linear in the
distance): the box edge case is exactly G3. The boss root is a certified fit, G3 to the plate and
G2 to the wall at Continuity Analysis's tolerances: at least G2. The boss root and the pipe on
the plate pass on SMLib's section and leave the known-failing list.

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

* Curvature fillets: the section's default thumbweight is equal area, so the round's volumes are the circle's

On the round's rails the order-2 section takes off what the ball's circle does: (1 - pi/4) s^2 at a
square corner, first moment 0.0474559669443 s^3 (w = -0.5252123889). The kernel and engine tests
and the curvature rig cases take their volumes from it by their own methods; the pipe on the plate
from its own wedge (70.52878 degrees, w = -0.8544103493). The G2 corner patch's seam test is listed
until the corner step.

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

* Kernel rig: four G2 corners wait for the corner step: their patch meets the new section's rounds G1 at one face seam

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

* Kernel rig: the turn at a corner of two rounds, Mitre (the default) and Swept, at a pocket's floor and a boss's base, by exact volumes

A round's spandrel A = (1 - pi/4) r^2 and its first moment about its wall M = r^3 (5/6 - pi/4):
Mitre adds L A -+ 2 M per edge (cut at, or run on to, the corners' bisector planes), Swept turns
the section about a convex corner's edge, (pi/2) M a corner, and refuses a concave corner rounded
less than the ball. The pocket's tangent chain round its R3 corners is a torus each. Graded to
1e-7, where Mitre and Swept differ by 6.6e-6. The Swept cases are listed until solid.fillet
takes a turn.

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

* Kernel rig: a junction family: oblique wedges 30 to 150 degrees rounded and chamfered (exact), bosses upright and tilted, pipe tees and a Y junction

Four are listed with their causes: the equal-pipe tee (its exact conic-pair rounds meet at the
ellipses' crossings in a corner the old corner code cannot join), the tilted bosses (1001, before the
blend rework too) and the Y junction (refused for room at its triple points).

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

* Variable rounds: graded by the union of their balls (their envelope), not the balls' great circles

The smlib crate's laws and ladder 005, 006, 007 and 012 take the volumes the balls' envelope
removes; the law with a corner is its own test, listed as failing until the corner step.

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

* Kernel tests: variable rounds graded by the union of their balls, the removed volume to 1e-4 of itself

removed_by_balls integrates, in each plane square to a right-angled edge, the corner the union
of the balls leaves (by rays from the edge, the least entry over the balls), cut to the body's
section. The law with a corner through a middle point is its own test, listed as failing.

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

* Kernel tests: a spline radius whose balls fold over one another at its peak is refused

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

* Kernel rig: the variable round on its cone passes (the round is the balls' envelope)

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

* Tests: three rounds of different radii at a corner listed: the corner's transition kinks with the balls' touch circles

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

* Ladder: the variable rounds graded to 1e-6 by the union of their balls; 012 by its law going on past the edge

At the default 1e-3 the great circles' volumes (4e-6 off) passed too. SMLib builds each within
1.6e-7. The golden takes the new volumes and areas; counts unchanged. The G2 corner test listed
as failing passes on SMLib's step 3 and leaves the list.

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

* Reference tests: the variable rounds 77, 78 and 81 pass on SMLib d01d7b0; their known-failure marks come off

The new section builds their edges on the surfaces, so STEP export no longer finds them 4.6e-5
to 6.1e-5 off. develop's Reference tests (60393a718) report all three as passing while marked.

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

* STEP import: a ball's halves whose seams are off each other's quarter lines each form within one turn

A sphere split into two halves along a circle about its axis, each half covering a pole with no
seam, gets a seam per half from the first vertex of its loop to the pole. The circles between the
halves group their frames so they turn together, and when the two seams are not a whole number of
quarters apart no one turn fits both: the group kept its frames, a half's form ran past one turn,
SMLib's trim (SMLib and solidcore alike) across the form's end crossed the face, and the mesh
folded over the ball (a rotor's ball showed torn; its half had 196 times its area in triangles).

When no one turn fits the group, each spilling face is now turned on its own to start on a quarter
line. On the rotor file this also clears three other faces that were filled leniently and a
0.076 mm edge gap; regression test in step_in/tests.rs with the area and volume of each half.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SdcgEu4Lr3moCCisdZShKz
(cherry picked from commit b3188e005d427043b918267449c2336a1777e54f)

* SMLib: pin 5e8c9c1 (the new blend's corners: Ball and End/Join; exact fold and room checks with fit sizes; overflow in place; the round as its definition)

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

* Kernel tests: a law's dip shows its corner ball's sphere, a peak's two rounds cross (the reasons had them swapped)

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

* Reference tests: the variable rounds 77, 78, 80 and 81 expect the union of their balls

A variable round is the envelope of the balls rolled along the edge, which takes less than a
quarter round in each section. The expectations are now that union, the law continued past the
edge's ends along its end slopes, integrated by Gauss-Legendre panels in x and t; 2, 4 and 8
panels agree to 1e-9 of the body's values, and 77's removed volume matches a brute-force union of
balls to 2.5e-10. The nearest-ball search now scans the balls the ray meets around ball x
(|x - s| < r(s) sqrt(sin 2t)) on their own, which near the faces and where 78's radius closes to 0
are narrower than the global scan's step. Regenerating keeps each case's known_failure. 79, a
constant radius, is unchanged.

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

* Known failing: the law corners' kernel and crate tests and rig 1207 pass on SMLib 48ea616

SMLib builds a variable round's law corners as its balls' union (the corner
ball's zone at a dip, the sides' crossing at a peak): the kernel's corner
through a middle point (39.5410) and the crate's 4 -> 2 -> 4 law (39.5421) pass.
Rig 1207 (the bar's top edge rounded after the boss's arc, r0.5) builds and
grades on the same SMLib; it failed on c820346.

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

* Kernel rig: every fillet and chamfer case rounds its tangent chain

The owner's ruling of 2026-11-01: the rig and the ladder round with tangent_chain true, how a
user's default pick behaves. The 38 fillets-area recipes and the ladder's 07-fillets/016 and 018
that passed false now pass true. The shell recipes keep theirs: a shell's tangent_chain is its
open-face chain, a different feature.

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

* Kernel rig: the partial-selection cases kept as -partial-chain copies, tangent chain off

Where a case's point is one edge of a tangent chain rounded alone, a copy keeps tangent_chain
false as its own case (SMLib rounds exactly the picked edges, ending at the smooth vertex); their
expectations stay as they were, to be re-derived:
- fillets/1207-fillet-box-end-edge-into-unblended-round-partial-chain;
- fillets/every-fillet-after-fillet-curvature-front-then-side-r3-partial-chain;
- fillets/1207-fillet-bar-top-edge-after-boss-arc-round-partial-chain, renamed from
  ...-no-tangent-chain: with the chain on it is exactly 1207-fillet-bar-top-edge-after-boss-arc-round,
  which the rig already has;
- ladder/07-fillets/020 and 021, the r5/r3/r7 and r5/r3/r2 corners (016 and 018) with the r3 on
  its edge alone: with the chain on, the r3 runs on along the r5 round's end arc and down the
  vertical edge the third round was to take.

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

* Kernel rig: the concave arc between the block's tangent sides kept with tangent chain off

fillets/every-block-cylinder-tangent-concave-arc-r2-partial-chain: the pick takes one piece of the
concave half circle, which SMLib splits; with the chain off its other pieces are left out, and the
blend is refused at the piece's end (the base gate's outcome). With the chain on the case rounds
the whole half circle. The copy keeps its expectation, to be re-derived.

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

* Kernel rig: two chains that now build graded by their exact volumes

With the tangent chain on, two cases round more than the edge they pick, and their old
expectations were the single edge's:
- 1207-fillet-box-end-edge-into-unblended-round: the r1 chain is the end face's top edge, the r3
  round's end arc and the back upright; 34 A + (pi/2) A (3 - c) by Pappus on the arc:
  V = 7953.139216 (it built 7953.139216, the old 7957.505 passed only inside 1e-3), 10 faces.
- 851-fillet-top-front-then-front-left-upright: the upright's chain runs up the first round's
  end arc on x = 0 and along the left top edge, so the corner becomes the R2 ball's eighth:
  V0 less the first round's spandrel to the wall (35.344727) less 27 (4 - pi) + 2 pi/3:
  V = 9530.777979 (built 9530.777987), 10 faces.

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

* Kernel rig: a curvature chain round a round's end refused for its size; the concave half circle builds

- every-fillet-after-fillet-curvature-front-then-side-r3: the side edge's chain runs round the
  first curvature round's end curve, whose radius of curvature has a weighted mean of 3 (its
  setback) and is unbounded at the ends, so somewhere below 3: the size-3 blend's contact line
  folds back on itself. Refused, "larger than the faces beside its edges allow", as ladder
  07-fillets/016's r7 on the r5 arc. (It was solid or refused for running on tangentially.)
- every-block-cylinder-tangent-concave-arc-r2-no-tangent-chain: the chain takes every piece of
  the half circle and ends at the convex edges it runs on into; it builds the analytic 19091.9620,
  so the outcome is solid (the refusal alternative was the chain-off one's).

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

* Known failing: two 851 chains whose corner piece is a ball of tube-centre radius 0

851-fillet-arc-then-top-front and 851-fillet-top-front-then-upright: with the tangent chain on,
each R2 chain runs along the end curve of an R2 round on a curved face (the arc round's end on the
front face; the first round's end on the wall), where the rolling ball sits on that round's own
centre line, so the piece is the ball about (39.2538, -19, 8): the three-edge rolling-ball corner.
SMLib fails with status 1001; the same piece on a plane end arc builds (front-left upright).
Expectations unchanged.

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

* Ladder: 016 and 018 refused, their r3 chain takes the vertical edge; 020 and 021 recorded

With the tangent chain on, 016's and 018's r3 runs on along the r5 round's end arc and down the
vertical edge, so the third pick (60, 40, 8) is 3 from both of that round's contact lines and
names no edge: refused, asking for a point along one edge. The corners they were made for are
020 and 021 (the r3 alone), whose golden entries are 016's and 018's (021 measured on f12a101).

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

* Kernel tests: three rounds at a box corner end on one rational patch on the ball's sphere, not an analytic sphere

SMLib's Ball corner is always the rational patch exactly on the sphere (owner ruling, blend
design §23), so the corner counts as an 'other' face beside the three cylinders and six planes.
The removed volume still checks the corner against r³ − πr³/6.

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

* Kernel rig: the curvature round on a pipe on a plate is listed: a seam grades G3 where exactly G2 is wanted

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

* Kernel tests: the box-and-fillet wire fixture records the round's cylinder frame as SMLib now keeps it

The round's cylinder keeps the frame it was made on when it is turned (SMLib's SwapUV no longer
re-derives it): its origin sits at x = −20 on the same axis line, (x, 2, 8) along x, radius 2.
Same cylinder; one number in the record moves.

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

* Known failing: two kernel tests' reasons as they fail on SMLib 5e8c9c1 (a partial selection running into an unblended tangent edge)

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

* Kernel rig: two curvature rounds get 600 s, past the 180 s default, while G2 certification is slow (tangency-labs/smlib#96)

On the CI runners the boss root and the hole rim, both size-2/size-1 curvature rounds, build
correctly but take more than 180 s: the order-2 section's span certificate and the round's
definition are evaluated station by station. The per-case limit is the one the thread cases use.

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

* Lid ring sketch test: find the ring under the pick, not by main's kernel's name

On SMLib the lip join's exact history names the lid underside's ring a piece
of the lid's own start (F14:start#[...]), as the naming rule says for a face
that came from the feature's input; main's kernel named it after the case's
top it rests on (F2:top). The test looked the ring up by that name and so
stopped before the attachment it guards. It now takes the underside piece
under the pick point, which the cap certificate attaches on either kernel.

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

* Viewport culling test: each box's triangles from its mesh, not 12

SMLib meshes a 10 mm box in 108 triangles, so the hard-coded 12 a box stopped
the test before it reached the culling. The expected counts are now each
body's span in the scene.

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

* Timeline chip test: a fillet that really reduces

The R20 fillet with fit reduce on one top edge of a 10 mm box builds on SMLib
without a reduced-radius warning, so the fixture had no warned feature. It
now rounds the four vertical edges at R6, reduced to the full round (5), as
the copy-warning test beside it does, and checks the warning says so.

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

* Oracle: 34-chamfer-then-fillet and 35-fillet-curved-curved pass on SMLib 5e8c9c1 (their STEP edges are on their surfaces)

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

* Known-failing: the shell of a fully rounded box opened at one face fails on SMLib 5e8c9c1 (tangency-labs/smlib#102)

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

* Known-failing: the action matrix's oblique cylinder cut fillet passes on SMLib 5e8c9c1; two shells still fail

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

* Kernel rig: the boss root's curvature round gets the rig's 1800 s cap: on the CI runners, 8 cases at a time, its G2 certification passed 600 s (tangency-labs/smlib#96)

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

* Kernel rig: a case's continuity can ask for an order per seam (seams: [{order, edges, exact}])

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

* Kernel rig: the pipe on a plate's curvature round meets the plate G2 and the pipe G3, 40307.8856312 (option B's order-2 jets, tangency-labs/solidcraft#10845)

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

* Edge names: a seam names its one face once; the curve-id test reads every edge kernel-neutrally

A seam's two sides are the same face, and edge names list each face an edge
bounds once, so SMLib's extruded circle names its side's seam F2:side:c1, with
no separator. The name grammar now says one face also means a seam. The #5858
test no longer counts one '|' an edge: every part of every edge name is a
whole face name of the edge, one per face it bounds, two for the rims and one
only for the side's seam (checked to run bottom to top on the circle).

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

* rustfmt on the merged tree

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

* Known failing on SMLib: two of main's new tests assume truck's box mesh and fillet torus

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

* SMLib: pin f916a8d (a round on a definition face is its definition; the G1 setback fill certified convex, setbacks too short refused naming the least; the G2 certificate halves a span no level can certify at once)

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

* Fillet: a setback too short for its corner is refused naming the least that builds (smlib_blend_fit_size)

SMLib's setback corner takes a G1 fill only once certified convex, and refuses a setback too
short for that as SMLIB_ERR_NO_ROOM with SMLIB_FIT_SETBACK_AT_LEAST, the smallest setback that
builds. The kernel reads it (solidcraft-smlib binds smlib_blend_fit_size) and names it; the
Fillet dialog says it in plain words. Other room refusals keep their words. The r5 box corner
set back 5.5 is refused naming 5.962, which builds.

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

* Kernel tests: three rounds of different radii at a corner meet in SMLib's setback corner

Each round runs whole to its section square to its edge at its setback (the owner's rule, at
least 1.5 r, scaled to the least at which the G1 fill is certified convex), and the hole is the
setback split: one patch per round and the triangle in three quadrilaterals, 15 faces, G1.
Outside the box the sections span the body is the three strips', so its volume is theirs within
that box's. A later r3 round at the mitred corner makes the body all three at once make
(7928.142563938, 15 faces), as the engine and the model-cache tests have it.

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

* Kernel tests: SMLib's own corner between curvature rounds is curvature continuous, as the G2 patch is

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

* Kernel rig: the boss root's and the hole rim's curvature rounds take the default timeout again (37 s and 34 s on the box under the rig's parallel load, tangency-labs/smlib#96)

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

* Agents: model decisions use exact geometry; a mesh is for display, export, previews and mesh bodies

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

* xtask: mesh-decisions, model decisions use exact geometry; today's 176 mesh decision sites are listed and the list only shrinks

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

* Sheet metal: cuts and holes pass over sheets and bends by their exact boxes, never a mesh's

A primitive cut skips the sheets kernel::apart (exact bounds grown by the record tolerance)
finds clear of its tool; the bend check leaves the clear case to kernel::boolean, which answers
it from the same exact boxes with no intersection work. The mesh-box helpers are gone, and their
four lines leave xtask/mesh-decisions.txt (172 sites left).

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

* Agents: AGENTS.md loses a stray merge-conflict marker line at its end

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

* Locales: Vietnamese for the setback-too-short refusal, now that vi is a complete catalog

Main made vi a complete catalog; develop's new message (ui.fillet_error_setback_short) had no vi
entry, so every_complete_catalog_carries_every_english_message failed on the merged tree.

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

* Kernel rig: exact volumes for 1213's bent-tube round and 4782's bar round, rel 1e-6

1213: the loft is a cubic skin through the five sections (ellipses between
them), not a torus; its volume 131601.597487935 by quadrature less the exact
rolling-ball removal 233.662461919 gives 131367.935026 (was a Pappus estimate
on a torus, 131360.148, at rel 5e-4).

4782: the section's area in closed form, 25 pi/3 - 5 sqrt(75) + 25, so
V = 630.2934873 (was a grid estimate, 630.293125).

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

* Kernel rig: the boss root's and the hole rim's curvature rounds take 600 s again: under CI's parallel load they pass the 180 s default (develop 820b7b6)

They take 37 s and 34 s alone on the box, but CI runs 8 cases at once on slower runners. Their
G2 certificate cost is the open item (tangency-labs/smlib#106, moved to solidcore#2344).

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

* After the merge: clippy's auto-deref, and the mesh-decisions list for the merged tree

Develop's list was taken on develop alone. Main's code (tap-marking reach, cut and hole box
checks, join pieces, print faces, placed measuring, selection items, imported mesh payloads) and
the branch's own exact_edges and named_vertex use meshes too; three listed functions were renamed
by main (face_outline and plate_recesses into FaceSlice, paste into build).

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

* After the merge: main's new tests on the SMLib kernel

- Cut To the far side of a curved wall (#8634): the tool stops past the first crossing of the
  face ahead of the profile; SMLib's whole bore is one face that a line crosses twice, so the
  single-crossing rule found none and the cut took nothing.
- Clearing view analyses goes through refresh_display, as main's #10687 made the others, so an
  open feature editor keeps OK and Cancel after Zebra.
- vi, a complete catalog on main, gets the setback-too-short message.
- fillets/KNOWN_FAILING: 4782's cause is now its continuity check (SMLib f916a8d builds the
  expected volume).
- Known failing, as main's tests assume truck: the spline wall's spans as one mesh patch, a
  hole touching an edge at a point leaving one body, and a copy of a copy's triangles matching
  a fresh mesh.

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

* Twisted sweep: circles about a straight path are exact cylinders (solidcore#2184)

A twist about a straight path maps a profile circle centred on the path's
line onto itself, so its swept wall is exactly the cylinder. The skin
through the stations stayed within 3.9e-9 of it (6.6e-9 on the outer wall),
and a boolean with another body on that cylinder failed with status 1001
(solidcore#2184; the kernel's handling of a B-spline face lying on a
cylinder stays open there).

sweep_controlled now builds those walls exactly when the path is lines
along the profile's normal and there is no end scale or rails: a profile
whose every loop is such a circle is swept untwisted, and such holes in
another outer loop are cut as exact cylinders from the skinned sweep of
the rest. Other loops, and an axis-centred arc inside a loop of other
curves, are skinned as before; a failed cut falls back to skinning all.

Regression test in smlib_sweeps_tests.rs: #2184's tubes joined at 10, 16,
18, 20 and 30 degrees give one closed body of 11.5625 pi with cylinder
walls; a twisted hexagon with the bore keeps a cylinder bore.

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

* After the merge: a hole that misses is found by exact boxes; a face is a one-face patch; main's mesh-box helpers gone

- `hole_misses` (main's #10701, through brett's branch) decided from the targets' mesh boxes. It now uses their exact boxes (`Body::exact_bounds`). An unknown box still leaves the hole not known to miss.
- With #10303's two shortcuts and `hole_misses` off the mesh, `body_box`, `mesh_box` and `padded_box` have no caller left, and are deleted.
- `project`'s intersection link passes its one face to `meeting_edges` as a one-element patch. This branch picks one face, not #9009's mesh patch.
- xtask/mesh-decisions.txt drops the six sites that no longer use a mesh here: `apply_traced_with`, `cut_end_past_face`, `beyond_cylinder_reach`, `hole_misses`, `body_box`/`mesh_box`/`padded_box`.

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

* After the merge: split_move's Result reaches its callers; to_local's duplicated move check goes

`frames::split_move` returns a Result on this branch: an unplaceable model is an error, not a silent no-split.
- Move/Copy of a component (main's #9914) propagates it, as its other calls do.
- `to_local` had the solid.move check twice after the merge (brett's Option one and ours). Ours stays, after the no-picks/not-placed return.

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

* After the merge: main's two new tests take world_state's Result, as the rest of their files do

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

* Native B-rep: a ball patch is its own surface kind, read from and written to SMLib's flat kind 6

SMLib's flat arrays now carry the corner of three equal rounds as `SURFACE_BALL_PATCH` (kind 6): its net as a NURBS face's block, with its sphere's centre and radius as its definition (ids 36 and 166). The record reads it as `ball_patch {center, radius}`, a label like the analytic kinds, with the net as its `nurbs`. A plain sphere with a form won't do: the net is not a piece of SMLib's sphere NURBS, so SMLib's `IsNurbSurfaceSphere` would refuse it.

Written back, a ball patch is kind 6 with the same definition, which SMLib takes once its net is certified on that sphere. Elsewhere:
- Offsets classify it as its sphere, so its offsets stay exact and collapse at the centre.
- Face kinds count it as a sphere: fillet_three_edges_at_a_corner's corner is now `sphere`, no longer `other`.
- STEP, which has no ball-patch entity, gets the net as the face's B-spline surface, pcurves included.
- Iso curves and SSI of the label are refused; the face's form is what evaluates.

Test: a_rounded_cubes_ball_patches_go_round_with_their_spheres. The cube's eight corners are ball patches about the corners pulled in by r. Points of each patch lie on its sphere to 1e-12. The patches are kind 6 again after import, with the same definitions bit for bit, and the closed-form volume holds.

Format: pending/ball-patch-faces.rs. Docs: docs/native-brep.md and the rig README's `surface_types` (`ball_patch`).

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

* SMLib: pin v1.1.0-alpha (a18bbe5): the generic blend's step-4 corners, the Ball's sphere patch through the flat arrays, all-target CI

Since f916a8d:
- #120: every target again, plus the warnings fix;
- #121: the Ball's patch as flat kind 6 with its sphere, read by the previous commit;
- #122: tests gated on counts;
- #123: the Mixed corner;
- #124: the End at a smooth vertex;
- #125: four or more rounds, and open setbacks;
- #126: the equal-area thumbweight guard;
- #127: the trunk merge.

The SMLIB_PREBUILT checksums are from the release prebuilt-a18bbe5…, written by `scripts/fetch-smlib.sh --pin`.

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

* smlib export tests: rustfmt's line for the rounded cube's edge midpoints

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

* Kernel rig: the fillet and ladder results on SMLib v1.1.0-alpha, the new failures recorded with their reasons

Fixed (off the known-failing list): the Mixed corner's peg cases and the concave arc with the front top edge (smlib #123), and the 40° boss junction (smlib #124). Eight cases in all.

New failures, recorded as they are, each with its reason. They passed on f916a8d.
- **Six cases grade a blend seam G0 where G1 (or G2) is wanted,** at the end of the round's contact seam, after smlib #124's End at a smooth vertex. Two of them also shift in volume.
- **Four cases come back unsupported** after smlib #125, which leaves setbacks to the Setback's corners only.
- **Ladder 07/012** makes 9 faces, 20 edges and 13 vertices, where its golden had 8, 18 and 12. Its golden entry is the new result, with the reason added.

Whether each change is by design or a defect is being established. A defect is fixed in solidcore, where the End and the Setback are being brought over (#2364, #2365), not in the frozen SMLib.

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

* Kernel ladder: 07/021 and 06/018 recorded as they are on SMLib v1.1.0-alpha, each with its reason

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

* Kernel ladder: a golden case marked unstable is gated on its status, and 06/018 is marked

On SMLib v1.1.0-alpha, 06/018 makes 597 or 594 edges from the same input:
the union's post-process keeps pointers to edges it has deleted. A golden
entry's `unstable` reason makes the gate compare that case's status only
and name it in its output; recording keeps the reason, and only an edit of
the golden removes it. v1.1.0-alpha.1 fixes the cause and removes the mark.

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

* After the merge with main: its new code on develop's exact bounds, types and thread fields

- The sheet cut's cull (#10301) and the tapped wall's (#10227) use each
  body's exact box (`exact_box`), not its display mesh.
- model.threads replies with develop's thread fields: form, hand (right,
  left or both) and starts.
- surface.boundary's schema checks a network curve's continuity as the
  handler does, and words a list entry's error as it does.
- Main's new tests take develop's Hole fields and world_state's Result.
- mesh-decisions: contact_sweep's triangle check is now `depth` (main
  split `hits`).

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

* Known failing: the 14 cargo tests SMLib v1.1.0-alpha fails, with what each sees; two cases that pass now

Develop's CI after the a18bbe5 pin (run 41522001173) fails 14 tests that
passed on f916a8d, and passes two listed ones. Each new line says what the
test sees on v1.1.0-alpha; v1.1.0-alpha.1 fixes the setback hole's cause.
shell_tangent_chain_that_opens_every_face_explains_how_to_keep_walls and
oracle 37-pyramid-apex-blend pass and leave their lists.

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

* Cargo.lock: develop's profile, smlib and step crates at main's 0.30.1

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

* Kernel rig: main's six new sketch-only commands are exempt from a kernel case

sketch.construction_line (and its horizontal and vertical forms) and
sketch.spline.insert_point, remove_point and reverse change only the 2D
sketch.

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

* Cuts leave the bodies their tools can't reach, by exact boxes (#10225 on exact geometry)

- The per-target cut loop skips a body whose exact box lies clear of
  every tool's, as main's version did with mesh boxes. The sheet culls
  (#10226, #10301) and this one now leave the far sheets and bodies alone:
  a_cut_leaves_bodies_it_cannot_reach_as_they_were passes and leaves the
  known-failing list.
- a_cut_still_runs_where_its_tool_touches_grazes_or_meets_a_curved_face:
  a tool 0.5 µm clear of the block is now left out (exact boxes; 0.5 µm is
  far past the bodies' tolerances); touching and 0.1 mm in still cut.
- Known failing, with reasons: main's edge lookup by a stored point
  (#11286, on the display mesh) is not taken; SMLib refuses the
  wrong-way chamfer for its tangent chain first.
- EXEMPT: main's eight new sketch-only commands.

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

* Kernel rig: main's new view, rendering, appearance and settings commands are exempt from a case

62 of the commands main added change no geometry: the view and render
panes, rendering lights and results, appearance tools, settings profiles,
point clouds (drawn as points, never a body), the selection, two sketch
edits, component links and History bar visibility, edit.repeat,
timeline.play, inspect.compare_picture (a picture against the outline),
and component.paste_from_design, which pastes the app's cross-window
clipboard that no command fills. Each joins EXEMPT's first section under
its family's reason.

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

* Kernel rig: construct.plane.axis_point gets construction cases

The plane through a box edge and its far corner, graded against its
closed-form normal (0,-2,3)/sqrt(13) through the origin, and the refusal
of a point on the axis.

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

* Kernel rig: assembly cases for mass properties, align, find limits and moving items between parts

- assembly.mass_properties: two boxes (2 g/cm3) in a rigid group: mass
  0.006 kg, centre (18.333,5,5), inertia diag(0.1, 1.0333, 1.0333) kg mm2
  by the parallel-axis theorem.
- occurrence.align: a block's end face onto a base's top face: a quarter
  turn about y, centre (70,20,20), box (65,15,10)-(75,25,30).
- joint.find_limits: a slider between two stops: min -20, max 10 mm.
- component.move_features, browser.reparent and
  component.move_sketches.to_parts: items moved into a turned and moved
  part keep their world place (volumes, centres and boxes by hand).

The mass case assigns a material it creates, so material.create and
material.assign leave EXEMPT. PATHS names the new command modules.

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

* Kernel rig: export and print face cases in the import area

- file.export_step, export_stl, export_3mf, export_obj: a 30x20x10 box
  written and read back (the meshes converted to a solid): V 6000,
  A 2200, 6 faces, box (5,-10,0)-(35,10,10).
- file.export_urdf: a hinged arm on a grounded base: 3 links, 2 joints,
  and the base's mesh, written in metres, reads back as the
  1 x 0.5 x 0.2 box (V 0.1, A 1.6).
- body.print_face: a cylinder's flat end is accepted, its side refused.

PATHS adds the URDF writer and the print face modules.

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

* claim: #11259

* fix: preserve transferred profile frames when capturing positions

---------

Co-authored-by: jyviko <2501648+jyviko@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: brett19 <brett19@gmail.com>
Co-authored-by: jyviko <jyviko@gmail.com>
2026-11-04 21:22:49 +00:00
Homero Thompson 1d387d3f21 Measure: the area of a curved face on the exact surface, not the display mesh (#9086) (#11486)
* Measure: the area of a curved face on the exact surface, not the display mesh (#9086)

* Measure: a non-straight border edge leaves the band to the mesh; fix the Vec3 imports (#9086)

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 21:22:24 +00:00
github-actions[bot] 51d237829a Regenerate generated files and fold pending files [skip ci] 2026-11-04 21:50:55 +00:00
bherbruck df4f9d3529 Inspect menu: Component Colors is a normal on/off item with a check mark (#11854)
* Inspect menu: Component Colors is a normal on/off item

The Component Colors entry in Inspect > Check was labelled "Enable Component Colors" and
swapped to "Disable Component Colors" with a different icon while on. It is now labelled
"Component Colors" in both states, keeps one icon, and shows a check mark when on, like Draft
Analysis and the other toggles beside it. The command id and parameters are unchanged.

The now-unused "disable" strings and the colored_components_off icon are removed. Other
languages' old "Enable ..." wording is dropped so they fall back to English until retranslated.

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

* Keep the Component Colors label in the fully translated catalogs

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

* Checkable menu rows tell screen readers whether they are on

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

* Georgian: Component Colors loses its Enable/Disable wording

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 20:12:36 +00:00
takuto-NA ef2f485e15 Extend viewport AO to the existing virtual ground (#3413) (#11979)
* claim: #3413

* feat(gpu): extend viewport AO to the virtual ground

* test(gpu): retain measured AO render attachments

* perf: initialize ground AO pipeline with concurrent jobs

* fix(gpu): size ground AO receiver for the current frame radius

* fix(viewport): retain orbit caches when ground AO visibility changes
2026-11-04 19:42:01 +00:00
github-actions[bot] a43cd99e55 Regenerate generated files and fold pending files [skip ci] 2026-11-04 21:11:47 +00:00
github-actions[bot] 8f26457487 Regenerate generated files and fold pending files [skip ci] 2026-11-04 21:01:46 +00:00
sascha2101 07ef94e102 Hole: drill only the shown bodies when no target is picked (#7520) (#7562)
* claim: #7520

* Hole: drill only the shown bodies when no target is picked (#7520)

A Hole that names no bodies drilled every body in its path, hidden ones
too, and its dialog preview replaced the hidden bodies with drilled ones.
Like a cut (#3961), it now takes the shown bodies of the active component
as its participants while some are hidden, stored so showing the others
later changes nothing. With nothing hidden, or everything, it still drills
every body.

Its face points are named on the bodies it drills first, so a hidden
body's face in the same plane no longer takes the picked face's name.

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

* Hole: drill the picked body though it is hidden (#7520)

Review on #7562: with the picked face's body hidden (picked through Select
Other, or hidden while the dialog is open), the shown-bodies default left it
out, so the hole drilled only shown bodies further along, or nothing, without
a message. The body whose face holds the hole's position now joins the shown
ones, unless a shown body has a face there too (a face shared by a shown and
a hidden body is the shown one's, as a pick in the view takes it).

The test also covers editing a hole made with nothing hidden while a body it
drills is hidden: timeline.redefine stores participants only when the edit
passes them, so the hole keeps its empty scope and the hidden body its bore.

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

* Hole: test that a hole made with a body hidden replays the same (#7520)

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

* Hole: follow the face-name lookup now returning a Result (#7520)

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

* Hole: tests follow main's Vec3 and world_state (#7520)

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

* Hole: format (#7520)

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

* Hole: with every body hidden, drill them all as before (#7520)

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

---------

Co-authored-by: sascha2101 <221609842+sascha2101@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
2026-11-04 19:19:18 +00:00
semmelrinde 02f276e296 Report a bug: limit answers by characters and show the room left (#7191) (#7196)
* claim: #7191

* Report a bug: limit answers by characters and show the room left (#7191)

The answers were cut to 2,000 bytes, silently: 666 characters of
Chinese or Japanese. They now keep 2,000 characters, as the text field's
own limit counts them, in the form, in the report and in a crash draft.
Under 200 characters left, an answer says how many there are. The
report's budget grows so three full answers in any script still fit
beside the technical details.

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

* Report a bug: leave the crash draft's description as it was

It is the app's own English text, well under the limit in bytes as in
characters; leaving the line alone keeps it apart from #7189's change.

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

* Locales: Arabic and Hebrew for the new strings

ar and he are complete catalogs on main now (#3458): every new message
needs them. The pending-messages test compares right-to-left messages in
display order once #7532 lands (#7527).

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

* Locales: Vietnamese for the new strings

Vietnamese became a complete catalog on main (#5823), so every new English
message needs it too.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 19:15:23 +00:00
austin62jake 9602587b0d Navigation: a middle or right drag keeps the mode it started with (#9557) (#9592)
* Claim #9557

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

* Navigation: a middle or right drag keeps the mode it started with (#9557)

The mode of a middle or right drag was worked out again from the keys
held on every frame, so letting go of Shift partway through a Fusion
Shift+middle orbit turned the rest of the drag into a pan. It is now taken
when the drag starts and kept until its button is released, as a left
drag with the mapping's keys already was. Preferences > Navigation > "A
drag keeps the navigation it started with" (on) turns it off.

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-11-04 19:14:40 +00:00
github-actions[bot] f8a98eed33 Regenerate generated files and fold pending files [skip ci] 2026-11-04 19:59:52 +00:00
github-actions[bot] 7629421375 Regenerate generated files and fold pending files [skip ci] 2026-11-04 19:48:22 +00:00
Homero Thompson d034633265 Desktop: the file chooser says when it is not the desktop's own (#10618)
* test: the file chooser says when it is not the desktop's own (#10159)

rfd falls back to zenity in silence when the XDG portal is not there, and the
user gets a foreign window with no explanation. The host must be able to say so.

RED: the test fails with 'without a D-Bus session bus the file chooser is not the
desktop's own: the user has to be told'.

* fix: say when the file chooser is not the desktop's own (#10159)

rfd opens the file chooser through the XDG desktop portal; without the portal it
falls back to zenity, a window that does not belong to the app, and the app never
says so. The user gets a foreign window and no explanation.

The host now asks the session bus, on a thread of its own, whether the portal is
there, and keeps the answer. When a file is opened and the chooser is not the
desktop's own, the reason goes to the session log (Help > Messages), which is
where a user looks for it.

* fix: the file chooser messages read as notices, not assertions (#10159)

The maintainer asked for two things on the pull request: the strings of the
pending catalogue read like a test assertion ("the user has to be told...")
rather than a notice a person would see, and the doc comment above
desktop_chooser_fallback was there twice.

The three reasons are now plain messages, in every locale of the file: they
say what the chooser is and why, and stop there. The doc comment of the
function is now the only one, and it says what it did.
2026-11-04 19:12:58 +00:00
Yishin Li fc51df3b01 cadmpeg 0.6.7: FreeCAD placements, sphere faces and closed shells come in right (#10382)
Only the version moves: SolidCraft builds against 0.6.7 unchanged. 0.6.7 applies App::Part
and link placements once, writes a sphere's seam ring as a valid loop, and indexes the
lookups that made large assemblies slow to decode. Two real FreeCAD documents that 0.6.3
refused as invalid open with it.


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

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 19:01:59 +00:00
austin62jake 8f4d7959df Agents and scripts: Parameter Sweep describes its exact parameters and reply (#10977) (#10978)
* claim: #10977

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

* Parameter Sweep: input and output schemas (#10977)

inspect.parameter_sweep gets a typed contract and reply types in
schema/parameter_sweep.rs and leaves unconverted.txt. Each key reads as
the handler reads it, with its message; the reply is built from
ParameterSweep and its item types, with the same JSON as before.

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

* Parameter Sweep schema: a missing parameter reaches the handler's message

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

* parameter_sweep schema: do not publish a null default on the required parameter

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 19:00:58 +00:00
Tury991 338fa197d3 Dimension text: round values to a step (#4896) (#11380)
* claim: #4896

* feat(doc): support dimension text rounding steps (#4896)
2026-11-04 19:00:07 +00:00
SS-EN 727bb23c7e Section Analysis: a body standing on another keeps its own cap, also in CPU pictures (#4622)
Where one body stands on another (a post on a plate), the post's bottom,
seen through the cut, and the plate's top are the same plane. In the CPU
rasterizer (headless pictures without a GPU) the plate's top won that
depth tie and showed through the post's cut. The cut now wins a tie by a
few f32 steps (DEPTH_EPS).

The viewport already draws this right since #6363 and #6728; a GPU
regression test now covers it, both creation orders, orthographic and
perspective, with both depth formats.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 18:52:15 +00:00
CarelT 8d6c45f640 UI kit: one shared needs-attention mark, used by the agent chat's badge (no behaviour change) (#5424)
* UI kit: one shared needs-attention mark, used by the agent chat's badge

The agent chat's "waiting" badge becomes `ui_kit::attention_mark` (a count, or
a plain dot) in `ui_kit/attention.rs`, so other things that wait for the user
(linked designs that changed, #5354) share one mark. The count is drawn
exactly as before. No change in behaviour.

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

* UI kit: sort the attention re-export

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

* ui_kit attention mark: take the corner inset from Tokens::space_3

The inset came from the agent chat panel as plain numbers, which font_sizes_and_dense_form_metrics_come_from_tokens refuses outside theme::Tokens (review on #5424). space_3 is 3.0, so nothing moves.

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

---------

Co-authored-by: Carel Theron <careltheron@Carels-MacBook-Pro.local>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
2026-11-04 18:52:05 +00:00
Jackson Oursland 6d16fae4ed Render preview: extract the product scene only when what it shows changes (#7465)
The live product preview's signature included the session revision, which
every command moves: a selection click extracted every body and built every
product vertex again (118 ms on a 200-body assembly) before the key comparison
skipped the upload. The signature is now the document and world state (held,
so their addresses are not reused) and every input of body visibility,
including hidden folders and the active sketch.

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-11-04 18:51:42 +00:00
semmelrinde ef92f35b16 Sketch drag: a step solved ahead that solves becomes the sketch, one step per core (#7648) (#7649)
Past a failed step a scripted drag solves the next steps at once and takes
those that fail in a row as tried; the first that solved was then solved
again by the walk. It now becomes the sketch from its solved copy, so no
step is solved twice. A batch holds one step per core (at most 8), and on
one core or in the browser build, where a batch would run its steps one
after another, the walk solves the steps itself. The walk takes the same
steps with the same outcomes, so results and counted solves don't change.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 18:51:32 +00:00
kaltenbrunnermarkus 232a902680 Circular pattern: cutting many copies of a hole is faster (#9007) (#9038)
* claim: #9007

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

* Circular pattern: cut copies of a hole that lie apart in one go, and check missed copies cheaply (#9007)

The check for copies that remove no material intersected every copy with
the whole body; a point test along the hole's axis (and through the middle
of the copy's overlap with the body) now proves most copies take material
and only the rest are intersected. Copies the kernel's coarse box test
takes for meeting are measured on finer meshes and, when clearly apart,
joined with join_apart and cut once.

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

* Format (#9007)

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

* Look for a copy's material through the middle of each of its faces (#9007)

A tool of two pieces (two circles cut at once) turned by 45 degrees has the
middle of its box between the pieces; the lines along the axes through it
miss both, and the copy was intersected. The lines through the middle of
each face's box run along each cylinder's axis.

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

* Circular pattern: cut copies that notch the same round face one by one (#9007)

Copies whose boxes only finer meshes show apart were cut in one go. When
several of them notch the same curved face of the target (grooves along a
bore), the result kept extra lines on that face: a revolved ring with 37
grooves came out in 184 faces instead of the 151 a one-by-one cut leaves,
at the same volume. Such copies are now cut in turn; copies that only
share flat faces (through holes in a tube wall) are still cut at once.

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

* Circular pattern: tell copies apart and find copies that cut nothing on exact shapes (#9007)

The checks this pull request added decided on meshes, which `cargo xtask
mesh-decisions` now refuses. They decide on the exact shapes instead:

- Two tools whose boxes meet are apart when their union comes out in two
  pieces that share no face, edge or vertex (crossing, nested and touching
  tools do not).
- A copy takes material when a point along its axis, the axes of its round
  faces or the middle of the box it shares with a target is inside both, by
  exact point classification; only copies that test cannot place are
  intersected.
- Copies that notch the same round face are no longer cut one by one: cut
  in one go they now leave the same faces as one by one (a revolved bore
  with 37 grooves: 80 faces either way, as on main).

The test of copies that come close now uses 8.5 mm, where they overlap; at
9 mm they are 0.012 mm apart and are cut in one go.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 18:42:02 +00:00
PaddleStroke cfc4908291 Sheet metal: returns on a notched tray now close at the convex corner (#8823) (#9714)
* claim: #8823

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

* Sheet metal: returns on a notched tray close at the corner away from the notch (#8823)

Closing two returns at a corner was refused when either return already lay
over another flange in the flat pattern, even far from that corner. On a tray
with a notch, the walls on either side of the notch's inner corner both unfold
into the notch, so the return on one of them lies over the other there; the
return's opposite convex corner was then left open ("closed, the flanges would
overlap in the flat pattern") and the two returns ran through each other.

A closure is now refused only when it makes the two returns overlap or adds to
what either already shares with another flange in the flat.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 18:41:07 +00:00
froglemonade2 1432131e95 English: command labels in title case, other labels in sentence case (#9085) (#10734)
* English: command labels in title case, other labels in sentence case (#9085)

The rest of #9085 after #9194 (the plural and the spelling) and #9907
(the Preferences page names). Command labels are in title case (422 of
430 were already); the seven in sentence case follow, and Fix/UnFix
reads Fix/Unfix. The UI text skill states the rule. The user docs name
the Preferences footer's button by its current label, Restore page
defaults (#10295).

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

* Navigation bar menu test: the echo names the command by its own label

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

* UI text skill: state the case rule in one short sentence, under the 3600 limit

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

* Localization doc: the case rule in full, with examples

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

* Localization doc: the example button is Restore page defaults now

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 18:39:52 +00:00
CaRLymx 35bc04847a Spanish (es-ES): catch up with en.json (2026-11-04) (#10895)
* i18n: make engine errors translatable

Engine errors (`EngineError::BadParams` and the offset/path errors the kernel
returns) were shown in English regardless of the active locale.

- Add `tr_engine_error`, which maps the known messages to `sketch.error.*`
  keys and returns anything unknown unchanged.
- Normalise the `invalid parameters for `<command>`: ` prefix before matching,
  and interpolate the offset id into the translated message.
- Route `friendly_error` through `tr_engine_error`.
- Avoid redundant status writes in `run_points`/`run_entities`.
- Ship the es-ES pending strings for the sketch tool hints.

* Spanish (es-ES): catch up with en.json

Translate the 734 messages `es.json` was missing and fix `ui.project`
("Proyecto" → "Proyectar", the action behind the sketch project tool).

The catalogue now matches `en.json` key for key (5,591 entries), keeps every
placeholder intact and stays alphabetically sorted. That includes the 61
messages added upstream since the last catch-up (92a9a5e0).

Terminology: "Turntable" → «Plato giratorio», "hover" → «pasar el puntero»,
"card" → «tarjeta».

* i18n: attribute the sketch hints and keep the offset error neutral

The pending sketch hints file adds an asset, so it gets its own row under
attribution/pending rather than editing ATTRIBUTION.md.

The engine reuses the offset-ends error for trim, extend and break, so the
message no longer tells the user to trim: it asks them to edit the source
curves instead.

* i18n: translate shortcut-started tool hints and fix the break message

Shortcuts are handled in logic(), before ui() installs the language scope, so a
tool started by its key stored the English hint even with another language
selected. Install the scope for the whole frame.

The engine has nothing to break, so say so: "nothing to break here".

* i18n: scope a tool start so its hint uses the app language

A direct start can run outside the frame scope (and a shortcut runs in logic),
so the hint was built with the thread default. Open the scope where the tool
starts.

* Spanish (es-ES): translate the 30 new messages

The blur preferences, the dimension step settings and the fillet turn modes
arrived upstream after the last catch-up.

* i18n: scope command errors and use US spelling in the hints

The native menu runs Undo and Redo before the frame scope exists, so an error
from them was translated in the thread default language.

The pending hints mixed British spellings into the English source; the English
catalog is US-spelled, so the fallbacks say center and en-GB keeps centre.
2026-11-04 18:39:43 +00:00
Mateusz Sych 06def2231d Agent chat: tell the agent the design changed by hand since its last reply (#10854) (#11144)
* claim: #10854

* Agent chat: tell the agent the design changed by hand since its last reply (#10854)

The chat remembers the design as the agent last saw it, at the end of a
turn it answered or worked in. When the next message goes and the design
is no longer that one, a note goes before the message for the agent (not
into the conversation, like the note after an interruption): the user
changed the design by hand, which parameters changed from what to what,
which features are new, edited or removed, where the History marker went,
and to read the design again before reusing positions, picks or sizes.

A new chat and a stopped backend forget it, so a session that has seen
nothing gets no note. Each list names at most 12 items and counts the
rest; names and expressions are cut at 60 characters.

* Agent chat docs: the design-changed note asks the agent to read the design again (#10854)

---------

Co-authored-by: MateuszSychXpl <151755914+MateuszSychXpl@users.noreply.github.com>
2026-11-04 18:39:09 +00:00
PaddleStroke d39fd6c397 Assistant: discover Mistral Vibe and offer sign-in (#11692) (#11704)
* claim: #11692

* Assistant: discover Mistral Vibe and support its sign-in (#11692)
2026-11-04 18:38:05 +00:00
Brett Lawson 4faf63e48a Loft: profiles picked one after another in one sketch are one section (#11720) (#11906)
* Loft: profiles picked one after another in one sketch are one section (#11720)

The Loft dialog made every picked profile its own section, so two picks in
one sketch (both halves of a split outline, say) became two sections in one
plane and the loft was refused. Consecutive picks in one sketch now form one
section {sketch, profiles: [...]}; a later return to that sketch is still a
section of its own, so the there-and-back loft of #8221 is unchanged.

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

* Loft: an unchanged edit keeps a section of all profiles "all" (#11720)

An edit loads a section of "all" as each profile of its sketch; now that
those picks make one section, OK saved them as indices. Indices fill a
letter's counters that "all" keeps as holes, so a later rebuild could
change the loft. Like Extrude, Revolve and Emboss, a section that was
"all" stays "all" while the picks are unchanged and cover every profile.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 18:37:41 +00:00
takuto-NA 91ecea8cfd Properties: account for sheet perforations (#6025) (#6028)
* claim: #6025

* fix(engine): account for sheet perforations in physical properties

* fix(inspect): retain kernel topology counts for sheet bodies
2026-11-04 18:30:31 +00:00
PaddleStroke c8c0c28fc8 Point clouds: keep every PTS point within the import limit (#11093) (#11717)
* claim: #11093

* Point clouds: count point rows before thinning PTS imports

* Point clouds: exclude invalid coordinates from text sampling
2026-11-04 18:27:21 +00:00
PaddleStroke f91db06904 Sketch Project: links to old numbered faces can be recovered (#11666) (#11707)
* claim: #11666

* Sketch: recover legacy references on original whole faces
2026-11-04 18:24:02 +00:00
Andrzej Błasik 9df9d1fbdc fix(smlib): NURBS weights compare within rounding of the coordinates' size (#11974)
same_nurbs compared the weights of a face's NURBS form and the rebuilt
analytic surface's NURBS within an absolute 1e-12. A weight is found from
the control points, so its rounding grows with the coordinates: 3.8e-12 at
coordinates of about 320 on a cone. The face was refused with "a NURBS form
that is not its analytic surface". Weights now compare within
1e-12 * max(1, |coordinates|), as same_nurbs_curve already does for points.
2026-11-04 18:23:53 +00:00
rugbymauri 8068e8f4b8 Preferences: the theme strip's arrows page by a screenful of themes (#11434) (#11600)
* claim: #11434

Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>

* Preferences: the theme arrows move by the themes that fit in view

The previous and next arrows beside the theme cards in Preferences >
Appearance moved the row by one card per click, although their hover
text says "Previous themes" and "Next themes". With 26 themes and about
six cards in view, reaching the last theme took some twenty clicks.

The shared carousel now moves by the number of cards that fit at the
current width, so one click shows the next screenful. The last page
ends on the last card, the first page starts on the first, and the
arrow at an end stays dimmed and inert. The step follows the window
width, and the row still opens on the chosen theme.

Fixes #11434

Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>

---------

Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>
2026-11-04 18:21:39 +00:00
PaddleStroke 863b0777da Sketch: dragging a constrained line by its middle no longer stalls (#11696) (#11714)
* claim: #11696

* Sketch: move constrained lines that stall during dragging
2026-11-04 18:19:46 +00:00
austin62jake 80889762ce Agents and scripts: Configure, Configuration Column, Row and Rule describe their exact parameters and reply (#11895) (#11896)
* claim: #11895

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

* Configuration table: input and output schemas for Configure, Configuration Column, Row and Rule (#11895)

describe_command now gives the exact parameters and reply of the four
commands that change a design's configuration table, and the warn and
strict parameter checks use them. The handlers read their parameters
from the raw JSON, so the new contracts in schema/config.rs read each
key as its handler does, with its message: lists of names as
params::string_list (whose json_brief and MAX_STRING_LIST become
pub(crate)), exactly one column target, faces picked or a face column's
key, a row's cells, and rules (whose schema describes them; the handler
keeps its own check). What only the design can answer stays the
handler's. All four reply with ConfigTableReply, as config.table does.

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-11-04 18:19:35 +00:00
rugbymauri a3f293c987 Error messages: an out-of-range value names the field that is wrong (#9625) (#9684)
* claim: #9625

Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>

* Name the value at fault when a hole, sheet rule, joint or extrude is refused (#9625)

Several refusals were right but named a check the input had passed, so
the user changed the wrong field:

- solid.hole: an unknown `fit` was reported as an unknown bolt size; it
  now says the fit must be close, normal or loose. A countersink angle
  out of range was reported as a countersink too narrow; the angle and
  the width are now checked apart. A `diameter` number out of range was
  reported as missing; it now gives the range, as other parameters do.
- Sheet rules: a thickness of 10000 mm or more said "must be positive";
  it now gives both limits. The K-factor and the bend radius are named
  on their own, and the message no longer implies a radius of 0 is
  refused.
- joint.drive: a number out of range said "numbers or expressions"; it
  now gives the range.
- Extrude: a distance past the kernel's limit said `end = 1000000000`;
  it now says the extrusion is too long and gives the limit.

Fixes #9625

Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>

* Refuse a too-long extrusion before the kernel and match the countersink floor (#9625)

The kernel is paused, and its `finite()` check also guards a revolve's
angle and a cylinder's and a torus's sizes, so its message must not say
"extrusion". crates/kernel is back as it was; the doc evaluator now
refuses an extrude (plain, tapered, or redefined from the Edit Extrude
dialog) whose end lies 10000000 mm or more from the sketch plane,
mirroring the kernel's limit, and says so. A cylinder of radius 2e7
keeps the kernel's own message.

The countersink check refused 0.1° while the message said "more than
0°". The floor stays (the cone grows as 1 / tan of the half angle) and
the message now names it: more than 0.115°.

Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>

---------

Signed-off-by: Maurizio Monticelli <maurizio@whatwedo.ch>
2026-11-04 18:18:14 +00:00
PaddleStroke e27d17c421 Assistant: offer the Mistral AI endpoint in Add Agent (#11246) (#11715)
* claim: #11246

* Assistant: offer the Mistral AI API endpoint (#11246)
2026-11-04 18:16:34 +00:00
github-actions[bot] 52bc27aaf6 Regenerate generated files and fold pending files [skip ci] 2026-11-04 18:48:25 +00:00
github-actions[bot] c357b1565e Regenerate generated files and fold pending files [skip ci] 2026-11-04 18:40:09 +00:00
cronos de9a2bc846 Shaded with hidden edges: optionally select edges through faces (#11841)
* Allow optional hidden-edge picking in Shaded with hidden edges

Keep visible-only picking as the default. Cover the preference, hover cache,
section and body visibility, and Fillet inputs in the existing UI tests.
Use platform-absolute fixture paths in the Windows CI argument-parser test.

* Respect section caps and visible-edge ties in hidden-edge picking

* Keep section caps as occluders behind nearer kept faces
2026-11-04 18:13:40 +00:00
Mateusz Sych 6b6bd26648 Agent chat: a prompt's bubble is as tall as its message (#11882) (#11883)
* claim: #11882

* Agent chat: a prompt's bubble is as tall as its message (#11882)

The prompt's Copy message row (#10741) was laid out right to left and centred
across its height. egui gives such a row all the height left in the
conversation's view, so a prompt near the top of a conversation stretched
down over the empty view, with Copy message floating in the middle. The row
is top-aligned now.

* Agent chat: keep the test's harness bound (#11882)

---------

Co-authored-by: MateuszSychXpl <151755914+MateuszSychXpl@users.noreply.github.com>
2026-11-04 18:13:19 +00:00
ABDULLAH 8239b914ba Arabic: rim term in the Lip help, Up/Down keys in the dimension step preferences, frame-rate note (#11870)
* Arabic: rim term and the shell-first step in the Lip help

* Arabic: Up/Down keys in the dimension step preferences, frame-rate note matches the English
2026-11-04 18:12:49 +00:00
Janiz-zzz e95946ac82 i18n(fi): translate the 822 messages Finnish lacks (#12036)
Added as assets/locales/pending/finnish-catch-up-2026-11.json so it
folds after merge: help texts, the redesigned Preferences (sketch,
navigation, dimension values, panel blur), fillet corner, setback and
turn options, threads, import report, browser and viewport messages.
cargo xtask i18n lists nothing missing for fi.

Co-authored-by: Janiz-zzz <267742026+Janiz-zzz@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 18:11:29 +00:00
cronos 46ce12b7f0 perf(ui): reuse assembly rendering caches during orbit (#2889) 2026-11-04 18:06:34 +00:00
austin62jake aba00427fa Render shots: input and output schemas for Duplicate, Rename and Remove (#11450) (#11451)
The handler's strict parameter type moves to schema/render_shots.rs as
ShotParams, with descriptions and JsonSchema, and becomes the contract of all
three commands; the handler reads the same type. It builds ShotChange instead
of a json! literal, and takes its operation as a ShotOperation enum.

Co-authored-by: Claude <noreply@anthropic.com>
2026-11-04 18:02:53 +00:00
alfonsotames cccf8c1ece Snap points: a flat face offers its corners, edge middles and every hole's centre (#11476) (#11477)
* Snap points: a flat face offers its corners, edge middles and every hole's centre (#11476)

A flat face now offers its whole outline as snap points, read from the
exact faces and edges: every corner, the middle of every straight edge
and the centre of every round edge, holes included, as well as the centre
of its area. Corners on a closed circle's seam, or where the kernel split
one circle into pieces, are left out.

snap_points_from gives each point with where it came from (SnapSource):
the face it was picked on, the face's outward normal and a point inside
its edge, so a tool can keep the point by name (joint.create's edge,
corner and on) and turn a frame to the face. SnapPoint itself does not
change, so code that builds one keeps compiling.

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

* Snap points: a round face's end centres say which way their end faces (#11476)

The centres at a cylinder's or cone's two ends now carry a normal in their
SnapSource: the axis pointing out of that end, which is the normal of the
face a hole opens onto or of a pin's end. A joint origin taken from one can
then lie on that face, as the centres a flat face offers already do.

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

* Snap points: a hole centred on its face keeps its own point and rim (#11476)

Snap points were one point when they lay at one place, whatever their kind,
so a hole centred on its face was lost behind the face's centre, and with it
the rim its point keeps. Now only points of one kind are merged: the face's
centre and the hole's centre both stay, and only the hole's has its rim.

SnapSource::normal's doc now says it is also the axis out of a cylinder's or
cone's end.

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

* Snap points: a washer's two rims keep a centre each (#11476)

Centres at one place are one point only when they are on one circle: the
inner and outer rims of a washer face now keep a centre each, with its own
rim, while the pieces the kernel split one circle into are still one point.

SnapSource::normal's doc now says that at a round face's end it is the
axis out of that end of the face, which at a blind hole's bottom points
into the material below, not out of it.

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

* Snap points: rims are told apart by their radii's size, not their place (#11476)

Two centres at one place were one circle when their rims' radii differed by
less than the place's tolerance, which grows with the distance from the
origin: 60 m out, rims of 4 and 4.05 counted as one and one lost its point.
The radii are now compared with a tolerance from the radii themselves.

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

* Snap points: nearby points stay apart far from the origin (#11476)

Points of one kind at one place were merged within a tolerance of 1e-6 of
their distance from the origin, 0.06 mm at 60 m, so two small holes there
whose centres are 0.05 apart lost one centre. Places now use 1e-9 of that
distance (0.06 µm at 60 m): well above the rounding of coordinates and of a
circle fitted to an imported edge, so a point found twice is still one, and
far below any feature. The radii keep their own tolerance.

The merge is its own function, so its test can give it points directly.

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

---------

Co-authored-by: Alfonso Tamés <mufufu@Mufufu-MacBook-Pro.local>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 18:02:44 +00:00
Jackson Oursland fb80454368 User guide: window tour gets a numbered screenshot and a navigation clip (#10323)
* Book: a numbered window screenshot and a navigation clip, from a capture script

The window tour now opens with a screenshot of the real window, its parts numbered in the
order the page describes them, and a short looping clip of orbit, pan, zoom and Home.

examples/demo/doc_shots.py makes both, and any later figure: each shot is a function that
starts a fresh app (its own settings) under Xvfb, drives it over the control channel with
the demo's Demo driver, and writes a still (numbered callouts, palette-optimised PNG) or an
H.264 clip (the drawn pointer of overlay.py, short button labels, idle cut by trim_idle.py)
into docs/book/src/media/. CONTRIBUTING.md says how to redo a figure when the UI changes.

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

* doc_shots: clips keep a moment on each result; the window tour again on 0.29.0

trim_idle.py takes --hold S, the seconds of each idle stretch to keep (default 0.1, as before,
so the README demo is unchanged). The guide's clips keep 0.8 s, so a reader can see what each
step did before the next begins. The window screenshot and the navigation clip are made again
with it, on SolidCraft 0.29.0.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 17:33:39 +00:00
Dahak Builds f92e16fa25 Keep orbit gizmo drag tracking past the window edge (#9943) (#9978)
* Claim #9943: keep orbit gizmo drag tracking past the window edge

* Warp the orbit gizmo drag back to the window centre at the edge (#9943)

---------

Co-authored-by: Dahak Builds <dahakbuilds@users.noreply.github.com>
2026-11-04 17:16:33 +00:00
Dahak Builds dcbedbd805 Don't blindly reopen a design that stopped the last session (#5064) (#10443)
A design that crashes or hangs the app while it is being reopened was
reopened blindly every launch, bricking the app each time. A launch that
reopens designs now marks them in the recovery folder; the marker is
cleared as each design finishes opening. A launch that finds the marker
left behind skips those designs once and says which one the last session
stopped on, so the app starts usable and the loop breaks.

Co-authored-by: Dahak Builds <dahakbuilds@users.noreply.github.com>
2026-11-04 17:16:15 +00:00
Dahak Builds 9bb216a357 Ribbon: only switch the tab when a command actually starts (#10540) (#10549)
* claim: #10540

* Ribbon: only switch the tab when a command actually starts (#10540)

select_command_tab ran before the command's own checks, so a command that
only reports a missing parameter or nothing to work on still moved the
ribbon. The ribbon now moves when the command starts and a refused command
puts the tab back, as a disabled command already never leaves it. View
display toggles and choices menus keep the ribbon's tab however they are
started, matching F6, the navigation bar and the view cube.

Written by Qwen.

* rustfmt (#10540)

Written by Qwen.

---------

Co-authored-by: Dahak Builds <dahakbuilds@users.noreply.github.com>
2026-11-04 17:16:09 +00:00
github-actions[bot] fd9b3fd9f2 Regenerate generated files and fold pending files [skip ci] 2026-11-04 18:00:54 +00:00
Mateusz Sych 6488a540c9 Windows: a lost graphics device restarts the app instead of crashing (#10240) (#11109)
* claim: #10240

* Lost GPU: a loss met by a queue write restarts the app instead of crashing (#10240)

The per-frame probe (#6528) finds a device the driver reset before the
frame's uploads, but the driver resets a device at any moment: a reset
that lands after the probe is still met first by a queue write or the
present, and wgpu-core unwraps the driver's Lost there
(PendingWrites::activate) instead of losing the device. That panic
ended the app and wrote a crash report.

- egui-wgpu: the painter runs each frame under DeviceLost::survive. A
  panic the frame meets is taken for the loss once the probe then finds
  the device lost (the probe's path loses the device and runs its lost
  callback); the painter draws nothing more and the app restarts as for
  any lost device, keeping unsaved changes for recovery. Any other panic
  carries on unwinding.
- Crash reports: a panic inside wgpu-core that unwraps Lost is the
  device loss, not a bug, and is not reported. A command that meets it
  fails; the next frame's probe restarts the app.

---------

Co-authored-by: MateuszSychXpl <151755914+MateuszSychXpl@users.noreply.github.com>
2026-11-04 17:15:58 +00:00
froglemonade2 3e70ad9876 View navigator: right-click to choose Axes, Cube or a Custom body (#10266)
* View navigator: a custom body as the navigator, and a right-click menu to switch types

A right-click anywhere on the view navigator opens a menu with its three types: Axes, Cube and
Custom. Custom opens the Custom View Navigator panel (a feature panel with OK and Cancel): pick a
body in the view, its texture (Themed: the cube's colours; Modeled: the body's own appearance and
the view's edge colour), and whether the cube's rotation arrows and Home button go around it. The
navigator previews the panel's choices while it is open; Cancel leaves it as it was.

The custom navigator draws the body at the cube's size through a small depth-buffered raster, so
hidden faces stay hidden; hovering a face highlights it, a click looks straight at it (and frames
the model with navigator_fit on), a drag orbits. Until a body is chosen it draws the cube.

The body is kept as a packed snapshot (16-bit, base64) with the preferences and in settings
profiles, so it stays across runs and designs and travels with an exported profile. Bodies over
10,000 triangles, and long or flat ones (thinnest width under a fifth of the widest), are refused.

New command view.navigator (style, body, texture, arrows, home) does the same without opening
anything; new preferences navigator_texture, navigator_arrows and navigator_home, and a Custom
choice for navigator_style. Defaults are unchanged.

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

* Custom view navigator: a hovered face in Modeled takes the view's selection colours

In the Modeled texture the face under the pointer is filled and outlined the way the view selects
a face of the model (sel_face, sel_edge), not in the cube's hover blue and accent, which Themed
keeps.

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

* Custom view navigator: a hovered face in Modeled lights up as the view's mouse-over does

The face under the pointer keeps its own colour, a little lighter (hover_face_lift) and still
shaded, with its edges in the view's mouse-over edge colour (hover_edge), as the 3D view shows a
face under the pointer; not the selection colours. Themed keeps the cube's hover blue and accent.

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

* Custom view navigator: rotation arrows and Home on by default

navigator_arrows and navigator_home start on, so a new custom navigator has the cube's arrows and
Home button around it; the Custom View Navigator panel turns either off.

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

* Custom view navigator: the strings in every complete language, and test fixes

- The new messages in Arabic, Hebrew and Vietnamese (complete catalogs, like Japanese and Chinese).
- The navigator's raster goes through one colour helper, listed with its reason among the files
  allowed colour literals (the design's appearance shaded into pixels).
- nav_settings joins the requested-icon manifest (the view.navigator command's icon).
- A visible edge piece is kept only when the stretch seen with it, across the edge's segments, is
  at least two samples long: a hidden edge no longer shows a sliver at a silhouette corner.
- The navigator layout test treats Custom without a body as the cube it draws; the panel and
  size tests no longer assume an empty undo list, the style before opening, or a coarse sphere.

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

* Custom view navigator: profiles carry its texture, arrows and Home

Settings profiles keep a fixed set of preference keys (#9907), which the custom navigator's newer
settings are not part of. They travel in the profile's own `view_navigator` entry, next to its
body (`view_gizmo`): captured, checked (a value this version can't read is left out) and applied,
leaving the fixed key set unchanged. The cube's arrow inversion (#10286) applies to the custom
navigator's arrows too, through the shared arrows().

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

* Custom view navigator: follow main's Vec3 move and fallible world_state

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

* Custom view navigator: list view.navigator in the rig's EXEMPT, refuse a stored body that can't save again, right-click without a widget over the navigator

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

* Custom view navigator: keep the body unless picked again, save only a profile that reads back, no navigator menu through a popup

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

* Custom view navigator: refuse a stored body with a triangle past its points

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

* Custom view navigator: a stored body meets the triangle and aspect limits a picked one does

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

* Custom view navigator: a body at the 5:1 limit stays inside it once packed

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 17:12:27 +00:00
Igor Bronovskyi 30842efb22 Ukrainian: translate the 174 messages main gained after the 3 November sync, panel layout and the redesigned preferences included (#11064)
* i18n(uk): translate the 80 messages main gained after batch 11

* i18n(uk): translate the messages main gained after 2 November

Add Ukrainian for the 158 English messages upstream added since the last
sync, and update 10 existing translations whose English text changed.

The new messages go in assets/locales/pending/uk-sync-2-nov.json, as the
locale files are not edited for new messages. The changed English text is
updated in assets/locales/uk.json, because a pending file cannot change an
existing message.

* i18n(uk): translate the messages main gained after the 3 November sync

Add Ukrainian for the 174 English messages upstream added since the last
sync, and update 56 existing translations whose English text changed.

The new messages go in assets/locales/pending/uk-sync-2-nov.json, as the
locale files are not edited for new messages. The changed English text is
updated in assets/locales/uk.json, because a pending file cannot change an
existing message.

* i18n(uk): apply CodeRabbit review fixes

Use the catalog's established wording: 'вибрати' in the keyboard hint,
name the linked component in the double-click help, and use the command
labels 'Упор', 'Бобишка' and 'Клацкове з'єднання' in the placement group.

* i18n(uk): use "Защіпкове з'єднання" for Snap Fit

The catalog called the plastic snap-fit joint "Клацкове з'єднання", a
calque of "snap". Use the standard "Защіпкове з'єднання" and
"защіпковий гачок" in the command label, help, sample description and
the design tools placement group. The drag hint already said "защіпку".

* i18n(uk): replace calques and outdated terms

Use "збіг" for coincident, "виступ" for lip, "ступиця" for hub, "шаблон" for preset, and "наддискретизація" for supersampling. Render and panel host texts now say "рендерер" or "середовище"/"вікно" instead of "хост".
2026-11-04 17:05:44 +00:00
Colin Faulkingham 549aef17a5 Changing anti-aliasing no longer resets the saved window system (#10116) (#10147)
* claim: #10116

* fix: preserve the saved window-system preference at startup

* fix: preserve graphics preferences in safe mode
2026-11-04 16:57:30 +00:00
Daniel 4d738cd210 Sketch: an ellipse is drawn with its axes, and D measures its half-axes (#9291) (#9292)
* Sketch: an ellipse comes with its axes, and D measures its half-axes

An ellipse could only be sized by its minor radius, drawn like a circle's
radius, and there was no way to give its full axes. sketch.ellipse now also
adds the major and minor axes as construction lines through the centre.
The major axis runs from the major end to the point opposite it; the minor
one is perpendicular to it, ends on the ellipse, and both are bisected by
the centre. That adds six coordinates and six equations, so the ellipse
keeps its five degrees of freedom, and it needs no new constraint kind and
no format change.

With the axes in place every ellipse size is an ordinary dimension. D on an
axis line gives its length, the whole axis. D on the outline gives a half-
axis, the distance from the centre to an axis end, chosen the way a line
chooses horizontal or vertical but in the ellipse's own frame: text pulled
across the major axis measures along it (the major half-axis), text pulled
out past an end of it measures across it (the minor one). The end on the
text's side is used. Near the boundary between the two, a dimension being
placed keeps the half-axis it had (sketch.dimension's previous_axis, which
the Dimension tool sends), so the preview does not flip. A half-axis and
its whole axis determine each other, so the second becomes a driven
dimension through the existing redundancy path.

Deleting the ellipse leaves its axes, as deleting a Center Rectangle's
edges leaves its construction diagonals. Ellipses without axes, from
existing designs, keep the minor radius as before, and type radius still
gives it.

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

* Guide: an ellipse's axes and half-axis dimensions

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

* Sketch: test that an ellipse's half-axis preview keeps its axis near the boundary

The Dimension tool sends the half-axis its last preview chose, so near the
boundary between the major and the minor half-axis the preview keeps it.
This covers that path from the tool, from both sides of the boundary.

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

* Sketch: an ellipse's axes select and drag with it, as a rectangle's diagonals do

A double-click on a Center Rectangle's edge or diagonal selects its edges
and both diagonals, and dragging a diagonal moves the rectangle without
changing it, because its lines share corner points. An ellipse's axes are
held by constraints instead, so a double-click selected only the ellipse
(and Delete then left the axes behind), and dragging the major axis moved
the ellipse but stretched its minor half-axis.

Sketch::ellipse_axes now finds an ellipse's construction axes (the
detection sketch.dimension already used, moved into the sketch crate).
Sketch::chain treats an ellipse and its two axes as joined, so a
double-click on any of them selects all three. Dragging either axis moves
the ellipse and both axes as one, keeping both half-axes. Dragging the
outline still sets the minor half-axis.

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

* Sketch: a corner test reads its line's id instead of assuming l1

sketch.ellipse now adds two axis lines, which take the first line ids, so
the extend test reads the id of the line it draws from the reply.

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

* Sketch: an ellipse's axes cut a trim, as a Center Rectangle's diagonals do

Trim, extend and break take every other curve as a cutter, construction
lines included, so an ellipse's construction axes stop them as a Center
Rectangle's diagonals do. A test now states it for both shapes, and the
guide says it. The freeform cutter test keeps the axes it used to delete
and asserts the piece a trim removes: from where the major axis crosses
the circle to where the ellipse does.

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

* Sketch: test dragging an ellipse's outline with its axes present

Dragging the outline sets the minor half-axis, and the minor axis line,
held on the ellipse at one end, follows it; the centre and the major
half-axis stay.

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

* Sketch: other dimension types on an ellipse with axes keep the minor radius

Only auto, major and minor give a half-axis on an ellipse drawn with its
axes. Any other type (radius, diameter, horizontal and the rest) gives
what it gives on an ellipse without axes, as the parameter doc says,
instead of quietly becoming a half-axis chosen by the text. The arm for
a missing text_at is dropped: with no text the rule already picks the
minor half-axis.

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

* Sketch: test an ellipse's half-axis dead zone from the major side too

The tests only showed a previous minor half-axis being kept near the
boundary; the point they used on the major side is major by geometry
alone. A point inside the band where geometry says minor now shows a
previous major half-axis kept, in the engine and in the Dimension
tool's preview.

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

* Sketch: a placed ellipse half-axis is the one its preview showed

On an ellipse already sized, a half-axis is added as a driven dimension
on the click, and that command left out the half-axis the preview kept
near the boundary, so the click could place the other one. It now
passes it as the preview does.

The remembered half-axis also comes only from a draft whose text was
placed (one without text is the minor half-axis by default and biased
the first placement), is kept per sketch as well as per picks, and is
forgotten once a dimension is placed or the picks are cleared.

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

* Sketch: say how an ellipse's axes are recognised and what that costs

The axes are read from the constraints, with no mark in the design, so
the doc comment says which lines count, that deleting the midpoint or
point-on-curve constraint leaves an ellipse without axes, and that each
lookup scans curves times constraints.

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

* Sketch: an ellipse with one axis deleted still picks its half-axis by the text

With one construction axis deleted, D on the ellipse fell back to the minor radius wherever the
text was pulled, because the half-axis choice needed both axes. It now reads what is left of
them and chooses as before: the major half-axis goes to the major end (also from the far side,
when the point opposite is gone with its line), the minor one to its axis end, or as the minor
radius when the minor axis is the one deleted. An ellipse with neither axis keeps the minor
radius.

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

* Sketch: the one-axis half-axis test checks what is measured to

The test for an ellipse with one axis deleted checked only the reply's axis and value, which
both ends of an axis give alike. It now checks the dimension's kind and end: the axis end on
the text's side, the major end from either side once the point opposite is gone, a radius once
the minor axis is. With the major axis deleted and no text the reply names the minor axis and
measures to its end, where it used to be a radius with no axis named.

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

* Sketch: an ellipse's minor radius is drawn as its half-axis, without R

A radius on an ellipse is the minor half-axis, and it was drawn as a circle's radius: one
leader from the centre towards the text, marked R, which measures nothing a reader can see.
It is now drawn as the distance from the centre to the minor axis end on the text's side,
the way the half-axis is drawn while the ellipse has its axes, and labelled with the length
alone. This is what D gives once the minor axis line is deleted, and on an ellipse with no
axes.

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

* Sketch: an ellipse's half-axis keeps a radius's stored text

The half-axis frame read the stored text from the middle of the half-axis, so a radius placed
on an ellipse in an existing design would show its text somewhere else. The text is stored from
the centre again, as a radius's always was, and the side drawn to follows it. An unplaced one
sits where a linear dimension to the minor axis end puts its text, and stacked texts step out
from the centre as a radius's do. A view test reads the painted label, so the drawing cannot
go back to R unnoticed.

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

* Sketch: an ellipse moved, turned or scaled takes its axes along

Move, Sketch Transform and Scale moved only the curves picked, so an ellipse moved without its
axes left them where they were: its centre no longer halved them, the minor end left the
ellipse, and the sketch stayed unsolved. A Center Rectangle's diagonals share its corners and
always go along; an ellipse's axes share only the major end. The axes now go with the ellipse,
as they do when it is dragged. Copy is unchanged: it copies no constraints, for a rectangle
either.

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

* Sketch: a preference draws ellipses without their axes, as before

Drawing an ellipse with its construction axes changes what the Ellipse tool makes out of the
box, so the old ellipse stays one setting away: Preferences › Sketch › Draw ellipses with their
axes, on by default. Off, the tool draws the ellipse alone. sketch.ellipse takes axes: false
for the same, and keeps drawing the axes when it is not given.

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

* Sketch: sketch.ellipse refuses an axes value that is not true or false

A string such as "false" was read as not given, so the axes were drawn; it is now refused as
the text commands refuse a non-boolean bold or italic. The move test also covers an ellipse
whose minor axis was deleted: the major axis left goes along.

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

* Sketch: D picks a half-axis on an ellipse without axes too

An ellipse with no axis lines (both deleted, a mirrored copy, one from an older design) gave the
minor radius wherever the text was pulled, so its major half-axis could not be dimensioned on
it. The half-axis is now chosen by where the text is on every ellipse: the major one to the
ellipse's own major end, the minor one as its minor radius. With no text it is the minor
radius, as before.

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

* Sketch: with one half-axis dimensioned, D on the ellipse gives the other

D on an ellipse chose the half-axis by where the text was pulled even when that half-axis was
already held by a dimension, so the second D usually guessed the one already there and was
refused as over-constrained. With a driving dimension already on one half-axis (to an axis
end, the minor radius, or the whole axis along its line or between its ends), D with no type
now gives the other, in either order and with or without the axis lines. With both held it
chooses as before; an explicit major or minor is kept.

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

* Sketch: the held half-axis rule is documented and its cases tested

The Dimension command's parameters and the guide now say that with one half-axis held, D on
the ellipse gives the other. Tests cover the whole axis held as a distance between its ends
and as its line's length with the other axis deleted, and check that a mirrored ellipse has
no axes. The placement test undoes its first dimension, so its second half still tests where
the text is rather than the held half-axis.

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

* Sketch: Transform previews an ellipse with its axes

OK in Sketch Transform moves, turns and scales an ellipse's axes with it, but the window drew
where OK would leave only the picked curves, so the axes stayed put in the preview until OK. An
ellipse picked alone is now drawn with the axis lines it has.

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

* Sketch: the typed-radius ellipse test counts the ellipse's axes

Main's #10282 test drew an ellipse with typed radii and expected one curve; with the
ellipse_axes preference on by default the tool also draws the two axes.

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

* Sketch: an ellipse's remaining axis still drags and selects it

With one axis deleted, dragging the other axis did not hold the ellipse (with the minor axis
left, the drag squashed it), and a double-click on the ellipse or that axis selected only the
one curve. The drag and Sketch::chain now read whichever axes are left through
ellipse_axis_lines, as Transform and Move already do.

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

* Sketch: drop the unreachable third arm of the held half-axis check

A distance from the centre to the major end or the point opposite it is already matched by the first arm, so the third arm and its major_end closure never ran.

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

* Sketch: D on an ellipse's axis line is the whole axis wherever the value is pulled

An axis line took the free line's rule, so on a turned ellipse a value pulled beside or above the box the axis spans gave its horizontal or vertical distance. An ellipse's axis is a construction axis, so auto now always gives its length, which the held half-axis check already counts.

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

* Sketch: an ellipse's axis is never a side of a Fillet or Chamfer corner

A line drawn from an ellipse's major end shares that point with the major axis, so Fillet or Chamfer there took the two as a corner (two lines skip the construction filter) and rounded the line into the axis. The axes are left out of a corner, picked by its point or as a and b.

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

* Sketch: test the move reply, extend and the one-axis Transform preview for ellipse axes

The reply of sketch.move, sketch.transform and sketch.scale lists the axis lines, Extend stops at an axis, and Sketch Transform previews an ellipse with the one axis left after the other was deleted.

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

* Sketch: with Draw ellipses with their axes off, D on an ellipse is its minor radius as before

The preference brought back the old drawing only; D on an ellipse had changed with no way back. sketch.dimension takes half_axes (default true, refused when not true or false); false makes auto on an ellipse the minor radius wherever the text is. The Dimension tool and the context menu's Sketch Dimension pass it from the preference, and with it off a radius on an ellipse is drawn and labelled R as before.

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

* Sketch: read an ellipse's axes in one pass over the constraints

ellipse_axis_lines scanned every constraint for every curve, so 500 ellipses with their axes (1500 curves, 2000 constraints) took 1.6 s to read and Move on all of them 2.0 s. It now collects the lines the centre bisects first: 3 ms and 0.36 s.

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

* Sketch: the dimension and corner parameters say how an ellipse's axis lines take part

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

* Preferences: Draw ellipses with their axes says D gives the minor radius when off

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

* Sketch: ask only the ellipses centred on a line's midpoint whether it is their axis

is_ellipse_axis read every ellipse's axes to answer for one line. An axis is bisected by its ellipse's centre, so it now asks only the ellipses centred on a point that bisects the line. The answer is the same.

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

* Sketch: a horizontal or vertical dimension across a level major axis holds it for D on the ellipse

half_axes_held counted a distance, a length and a radius, but not a horizontal or vertical dimension across the major axis, which is its length when the axis lies that way. D on the outline could then take the held major half-axis and be refused as determined; it now gives the minor one. Across a slanted axis such a dimension is only a projection and does not count.

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

* Dimension: a draft on an ellipse is drawn with the axes preference it was made with

The frame read the preference as it is now, while the dimension added is the draft's.

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

* Sketch: a horizontal or vertical dimension across an upright minor axis holds it too

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

* Sketch: drop the unused Sketch::ellipse_axes; ellipse_axis_lines says how the axes are read

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

* Sketch: an ellipse's own axes do not cut it, so Trim takes a lone ellipse whole and stops at real crossings

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

* Sketch: Fillet and Chamfer offer no corner at an ellipse's axis end, as the engine takes none

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

* Sketch: a deleted ellipse leaves its centre on its axes, as a Center Rectangle's centre stays

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

* Sketch: ellipse axis docs say what they do, and the corner picker tests a curve's ends before its axis role

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

* Sketch: the trim-at-a-crossing test checks that part of the ellipse is left

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

* Sketch: an ellipse axis moved, turned or scaled on its own takes its ellipse along, as a drag does

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

* Sketch: a selection dragged by an ellipse axis takes the ellipse along, as the axis alone does

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

* Docs: the Sketch preferences page lists Draw ellipses with their axes

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

* Sketch: the ellipse axes help text says D gives the minor radius, drawn and labelled R as before, wherever the value is pulled

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 16:54:46 +00:00
github-actions[bot] 1cfe77bb85 Regenerate generated files and fold pending files [skip ci] 2026-11-04 17:13:13 +00:00
Nathan Dane e3f99de8af Onyx: a tactile stone-grey theme with broad subtle blur (#10338)
* Onyx: add the finished stone-grey theme and document its blur range

* Onyx: distinguish grey construction lines from gold dimensions
2026-11-04 16:35:28 +00:00
ReDead 40b1b70dec Shell: pick a body from the Browser for a closed hollow (#794) (#11441)
* claim: #794

Co-authored-by: ReDead <ReDead@users.noreply.github.com>

* Shell: pick a body from the Browser for a closed hollow (#794)

Faces/Body accepted only faces, so a Browser body click never filled the
Shell dialog. Accept bodies, build faces:[] with that body, restore empty-face
shells as a body pick, and keep the chip/highlights on the body itself.

Co-authored-by: ReDead <ReDead@users.noreply.github.com>

* Shell: refuse faces that sit on a different body than the Browser-picked one

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: ReDead <ReDead@users.noreply.github.com>
Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 16:01:35 +00:00
github-actions[bot] 0862f1679a Regenerate generated files and fold pending files [skip ci] 2026-11-04 16:25:53 +00:00
austin62jake 2cd54b6af9 Agents and scripts: the standard part commands describe their exact parameters and reply (#11003) (#11004)
* claim: #11003

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

* Standard parts: input and output schemas (#11003)

parts.library, parts.insert, parts.fastener and parts.resize get typed
contracts and reply types in schema/parts.rs and leave unconverted.txt.
Each key reads as the handler reads it, with its message; the replies
are built from PartsLibrary, InsertedPart and ResizedPart, with the same
JSON as before.

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

* Standard parts schema: Vec3 comes from solidcore_record

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

* Standard parts schema: hole picks must start with a list

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

* parts.insert schema: the first hole pick must be a point, not any list

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 15:30:52 +00:00
N3MI-DG c9c063620a Hole: Bridged option for counterbores (#11103) (#11104)
* claim: #11103

* Hole: Bridge option for counterbores (#11103)

A counterbore hole takes an optional `bridge` length: two 3D-printing
layers that thick below the counterbore's floor, a slot along X across
the counterbore and then a square, both 0.02 mm wider than the hole, so
a printer bridges the step instead of sagging into it. Empty (the
default) keeps a plain counterbore.

The clearance keeps the bore, cut straight through both layers as for a
plain counterbore, clear of their sides: at exactly the hole's width it
ran tangent along them and broke through. The command and the geometry
refuse a layer that is not a positive length, layers that don't fit in
the hole below the counterbore, and a bridge on any other hole type.

The Hole dialog shows a Bridge field below Drill Point for counterbores.

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

* Hole: translate the Bridge label for the complete catalogs (#11103)

Arabic, Hebrew, Japanese, Vietnamese and Chinese carry every English
message, so `cargo xtask i18n --check` needs the new label in each.

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

* Hole: bridged counterbores refuse layers past a through hole's exit, too narrow a counterbore and a malformed bridge (#11103)

- Through all, the second bridge layer must end inside a body (exact point classification),
  short of the exit face; the tool's reach is no measure of the material.
- A bridged counterbore must be at least 0.04 mm wider than the hole, so the slot's ends have
  room on its wall (no square root of a negative number reaches the extrude).
- `solid.hole` refuses a `bridge` that is given but not a length expression (`true`, `[]`, `""`)
  instead of making a plain counterbore; `null` still leaves it out.
- Loop2 and Seg2 now come from solidcore_record (main moved them).
- The "nothing saved" check looks for the `"bridge"` key: `format_pending` names
  `bridged-counterbore` in every design.

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

* Unit defaults test: solid.hole has the bridge field too

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 15:24:15 +00:00
Nathan Dane 9060abc83e Sliders: keep the parent material clear beneath the track (#10375) 2026-11-04 15:17:54 +00:00
austin62jake bec762fb2b Agents and scripts: the render library browsing commands describe their exact parameters and reply (#11443) (#11444)
* claim: #11443

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

* Render library: input and output schemas for List, Describe, Targets and Preview (#11443)

Describe, Targets and Preview read their strict parameter types from
schema/render_library.rs, which become their contracts. List keeps its two
readers (the physical profiles' takes availability: null, the other
catalogs' doesn't); its contract takes what either takes. The handlers build
LibraryPage, LibraryResource, LibraryTargets and LibraryPreview instead of
json! literals; a catalog resource is read into ResourceSummary through a
small Listed reader. The physical profiles' replies keep their JSON, which the
schemas describe.

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

* Render library schema: the list says which availability each catalog takes

render.library.list has two readers: the physical profiles' takes
availability all, usable or null; the other catalogs' (and a call
without kind, which lists surfaces) take all, usable, specification or
blocked, but not null. The schema now says so with an anyOf on kind.
The type still takes what either reader takes: the readers check the
query and the page before availability, so refusing it in the type
would change which message a call with two mistakes gets in strict.
A new test checks that the schema takes ten calls exactly when the
handler does.

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-11-04 14:50:44 +00:00
leValdemar19 826fce68eb Assembly: Move Occurrence says when the part's joints put it back (#11723) (#11791)
* claim: #11723

* Assembly: Move Occurrence says when the part's joints put it back (#11723)

A part that rigid joints hold to something grounded was moved, then put
back by the joint solve after the command, while occurrence.move still
replied with the moved transform. occurrence.move now solves the joints
before it replies. When they put the part back, the reply gives where the
part is, adds reverted: true and a warning that names the joint holding it
and what to change instead. A free chain of parts follows the move as before.

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

* Move Occurrence: the warning names the joint for a part in a repeated assembly too (#11723)

A part inside an assembly placed more than once has no path of its own, so
the reverted-move warning asked for one instead of naming the joint. It now
looks at the placement the caller named, or else the first one.

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

* Move Occurrence: list the part's joints when no single joint holds it back (#11723)

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 14:39:17 +00:00
SS-EN 73f50d7bb4 Viewport: hidden parts no longer draw their joint-origin triads (#11294)
* Viewport: hidden occurrences draw no joint-origin triads

A named joint origin's triad (red/green/blue axes, white dot) was drawn for every
origin, so hidden inserted hardware still showed one at its placement. It now
follows the occurrence's visibility (and its parents') and the Joints folder of
the design and of the placement.

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

* Viewport: a copy's joint-origin triad follows that copy's visibility

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 14:24:55 +00:00
github-actions[bot] d1927aac25 Regenerate generated files and fold pending files [skip ci] 2026-11-04 13:33:45 +00:00
Brett Lawson b224ad6dae Kernel rig: the Lip refusal case matches the reworded message (#10871) (#11937)
#10871 reworded Lip's refusal on a face that is not a shelled rim to
"Lip needs the rim of a shelled body: ...", but the plastic rig case
cov-lip-on-a-face-without-an-opening-refused still looked for "pick the
rim face of a shelled body". The case now matches the new message, so the
kernel rig's plastic shard passes again on main.


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

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 12:49:40 +00:00
bherbruck ca10e02573 Russian: correct the plural of degrees of freedom and make two notes match English (#11856)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 12:42:56 +00:00
bherbruck 5500681d6a Brand kit: logo files and a short brand guide in assets/branding (#9117)
Adds assets/branding/logo with the static mark (colour, one-colour
black and white), transparent 512/1024/2048 px PNGs, an outlined
wordmark and a horizontal lockup, all drawn from the app icon's
blue cutout layer. assets/branding/README.md covers use, colours,
clear space, minimum size and the name.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 12:36:16 +00:00
bherbruck fabeaa329a Desktop app: closing a design stops its kernel work, and designs build the same on any kernel (#11771)
* Desktop: closing a design stops its kernel work; same result on any kernel

A design's jobs now run under a stop token that closing or replacing the
design fires: an operation under it refuses to start, and one running ends
at solidcore's next check. The kernel it ran on is not used again. Calls
that answer from a body itself always finish, since a stopped call can leave
what it borrows half-changed, and what fills a body's shared cells is not
stopped.

Every kernel call starts clean (the same random rays, no stations from
earlier calls), so a design builds the same on any kernel of the pool, down
to its B-rep records. The pool is bounded: at most 8 kernels wait between
calls, and at 256 running calls a new call fails rather than grow or wait.

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

* Stop tokens: a copy never stops its design; a stopped job can't decide a refusal

A copy replacing its own design stopped the token it shares with the design
it came from, so that design's later work would be refused. Only the design
itself stops it now. Which face a refusal names no longer depends on whether
the job that found it had been stopped: the refusal is kept with the body,
and a stopped job's probes were all refused, which named the wrong face.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 12:35:39 +00:00
803 changed files with 78962 additions and 53906 deletions
+14 -2
View File
@@ -317,7 +317,6 @@ Editable drawing code, native projects and shipped exports are collected under `
| assets/icons/ui/design/svg/model/circle_ttt.svg | SolidCraft contributors | two-tone modelling icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/coil.svg | SolidCraft contributors | two-tone modelling icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/colored_components.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/colored_components_off.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/combine.svg | SolidCraft contributors | two-tone modelling icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/component.svg | SolidCraft contributors | two-tone modelling icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
@@ -787,6 +786,19 @@ Original SolidCraft drawings, Open for owner review; no runtime assignments.
| 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 |
| assets/branding/logo/solidcraft-mark.svg | SolidCraft contributors | static SolidCraft mark: the five faces and gradients of assets/icons/app/macos/source/SolidCraft.icon/Assets/03-solid-blue-cutout.svg (the final frame of assets/styles/web/loading-logo.svg), cropped to the mark's bounds; original artwork | MIT OR Apache-2.0 |
| assets/branding/logo/solidcraft-mark-mono-black.svg | SolidCraft contributors | one-colour (black) version of assets/branding/logo/solidcraft-mark.svg, same paths; original artwork | MIT OR Apache-2.0 |
| assets/branding/logo/solidcraft-mark-mono-white.svg | SolidCraft contributors | one-colour (white) version of assets/branding/logo/solidcraft-mark.svg, same paths; original artwork | MIT OR Apache-2.0 |
| assets/branding/logo/solidcraft-mark-512.png | SolidCraft contributors | transparent 512 px render of assets/branding/logo/solidcraft-mark.svg (cairosvg); original artwork | MIT OR Apache-2.0 |
| assets/branding/logo/solidcraft-mark-1024.png | SolidCraft contributors | transparent 1024 px render of assets/branding/logo/solidcraft-mark.svg (cairosvg); original artwork | MIT OR Apache-2.0 |
| assets/branding/logo/solidcraft-mark-2048.png | SolidCraft contributors | transparent 2048 px render of assets/branding/logo/solidcraft-mark.svg (cairosvg); original artwork | MIT OR Apache-2.0 |
| assets/branding/logo/solidcraft-wordmark.svg | SolidCraft contributors | the name SolidCraft set like the loading logo's wordmark (bold, +0.02 em tracking, #15499e) in IBM Plex Sans Bold from assets/fonts/ibm-plex-sans (SIL OFL 1.1, IBM Corp.), outlined to paths; no font file is embedded | MIT OR Apache-2.0 |
| assets/branding/logo/solidcraft-lockup-horizontal.svg | SolidCraft contributors | assets/branding/logo/solidcraft-mark.svg beside assets/branding/logo/solidcraft-wordmark.svg, in the proportions of assets/branding/web/source/og.png; original artwork | MIT OR Apache-2.0 |
| assets/styles/ui/themes/onyx.json | SolidCraft contributors | original Onyx stone-grey palette, option overlays and semantic icon-role mapping; existing SolidCraft artwork is unchanged | MIT OR Apache-2.0 |
| docs/book/src/media/ui-tour/window.png | SolidCraft contributors | screenshot of SolidCraft 0.29.0 itself (`solidcraft --sample`, default dark theme, software renderer, 1280x800) with original numbered callouts (digits set in DejaVu Sans Bold), captured with examples/demo/doc_shots.py | MIT OR Apache-2.0 |
| docs/book/src/media/ui-tour/navigate.mp4 | SolidCraft contributors | original screen recording of the actual SolidCraft 0.29.0 English desktop UI (`--sample`, default dark theme, software renderer) orbiting, panning and zooming, with the original drawn pointer of examples/demo/overlay.py and labels set in DejaVu Sans Bold, captured with examples/demo/doc_shots.py | MIT OR Apache-2.0 |
| assets/locales/pending/sketch-tool-hints.json | carlymx | Original sketch tool hints and engine error translations | MIT OR Apache-2.0 |
| assets/locales/pending/primitive-pick-field-tips.json | SolidCraft contributors | Original primitive pick-field tooltip translations | MIT OR Apache-2.0 |
## Code adapted from sibling projects
@@ -838,7 +850,7 @@ into THIRD-PARTY-LICENSES.txt when `main` regenerates it.
## FreeCAD and Rhino import library
FreeCAD `.FCStd` and Rhino `.3dm` files are read with [cadmpeg](https://github.com/cadmpeg/cadmpeg)
0.6.3 (Apache-2.0), linked as the crates `cadmpeg-core`, `cadmpeg-ir`, `cadmpeg-registry`,
0.6.7 (Apache-2.0), linked as the crates `cadmpeg-core`, `cadmpeg-ir`, `cadmpeg-registry`,
`cadmpeg-container`, `cadmpeg-codec-freecad`, `cadmpeg-codec-rhino` and `cadmpeg-codec-step`
in native and browser builds (`crates/io/src/native_cad.rs`). Only those open-format codecs are built: no
default features, so cadmpeg's codecs for commercial CAD formats and its ShapeManager/ACIS
+12
View File
@@ -88,6 +88,8 @@ The [architecture page](docs/architecture.md) has the layer table. The short ver
- `crates/geom`: vectors, planes, meshes, measures.
- `crates/sketch`: our own constraint solver, plus profile finding.
- `crates/dynamics`: multibody dynamics: rigid bodies, joint constraints, gravity and springs,
integrated in time (RFC #9719). It depends only on `geom`.
- `crates/kernel`: the B-rep kernel boundary. It wraps [truck](https://github.com/ricosjp/truck),
and it's the only crate allowed to touch truck. Fillets, chamfers, shell/offset, Delete Face,
Split Face, STEP/IGES read and write, and face provenance are our own code here. Patched
@@ -472,6 +474,16 @@ the same area), first resolve the command registry and source conflicts, then ru
The generator tests cover stable ordering and additions that leave existing lines
unchanged. The initial switch to sorted rows is a one-time larger diff.
## Pictures in the user guide
The user guide's screenshots and short clips (`docs/book/src/media/`) are captured from the real
app by `examples/demo/doc_shots.py`, one function per figure. Each run starts a fresh app with its
own settings, so your preferences never show. When a change makes a figure out of date, run
`python3 examples/demo/doc_shots.py <page or shot>` (it needs Xvfb, ffmpeg, Pillow and a release
build) and commit the new file. A new figure is a new function there, an ATTRIBUTION row in
`attribution/pending/`, and a `<video>` (clips) or image link on the page. Keep a still under
about 250 KB and a clip under about 1 MB.
## Licence
By contributing you agree that your contribution is licensed as MIT OR Apache-2.0, like the rest
Generated
+27 -18
View File
@@ -564,9 +564,9 @@ checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
[[package]]
name = "cadmpeg-codec-freecad"
version = "0.6.3"
version = "0.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c4a8fa61748cbb3ce80838b00a8cebfe553460583941f5aba6e2b97c16b4d37"
checksum = "6fe0ed1935ace2c203b87107a064818dec2bb2ec2f250c4ad121767f3da75d1b"
dependencies = [
"cadmpeg-container",
"cadmpeg-core",
@@ -580,9 +580,9 @@ dependencies = [
[[package]]
name = "cadmpeg-codec-rhino"
version = "0.6.3"
version = "0.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7af41440d4c1d69e88e612de02cc3c9bad79d03429531e640ac924fef53c6bf"
checksum = "7dd2b76bd1d9301c949fbd731fd22cff9d37d10d1f481c754b557c2187bd0171"
dependencies = [
"cadmpeg-container",
"cadmpeg-core",
@@ -599,9 +599,9 @@ dependencies = [
[[package]]
name = "cadmpeg-codec-step"
version = "0.6.3"
version = "0.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35e1b6d17cf26b31f59f2edab8ef5c29d2a33216c581fd913f012ad4869bd559"
checksum = "f3cbce33ed681a28b62d8b01ae64cca2b01f11f31b9a3e5e2455b9ffde915712"
dependencies = [
"base64",
"cadmpeg-container",
@@ -616,9 +616,9 @@ dependencies = [
[[package]]
name = "cadmpeg-container"
version = "0.6.3"
version = "0.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ab7bcef48ac7480b4917820272d85cd2c9533e17904e15b0e4d22f19a6045bd"
checksum = "03f0d1c22b875294c5fbf4a4738aec8f8c0a4ad035eb28ece404f1dec21f371a"
dependencies = [
"cadmpeg-core",
"crc32fast",
@@ -628,9 +628,9 @@ dependencies = [
[[package]]
name = "cadmpeg-core"
version = "0.6.3"
version = "0.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e86bb7a40521832504bb1f5953a1c706b2c52aad73bf6d84e1b35e5cb0beae3f"
checksum = "795bdc6e2a10ecc0627138874c17e2948a3925cd22c6c6b0c2e4f6621d92f89c"
dependencies = [
"flate2",
"memchr",
@@ -643,9 +643,9 @@ dependencies = [
[[package]]
name = "cadmpeg-ir"
version = "0.6.3"
version = "0.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b570a1a8c579e407a9fd7bb10fa1900f8827f1d578b8fcb4ea2a056b5f323c72"
checksum = "5592036516aaf2574b159a86a3d1ae872dc245261de86e4c0ce6773fed00a249"
dependencies = [
"base64",
"cadmpeg-core",
@@ -658,9 +658,9 @@ dependencies = [
[[package]]
name = "cadmpeg-registry"
version = "0.6.3"
version = "0.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2be23469a9bca0b684f1811118fffb581ebd32ded08e6cc8b0716a41f8c1377"
checksum = "fc163233d5f664337267192848505854de6aeed4843181adf87f58baddcee9a0"
dependencies = [
"cadmpeg-codec-freecad",
"cadmpeg-codec-rhino",
@@ -4153,6 +4153,17 @@ dependencies = [
"web-time",
]
[[package]]
name = "solidcraft-dynamics"
version = "0.40.0-rc.2"
dependencies = [
"serde",
"serde_json",
"solidcore-record",
"solidcraft-geom",
"thiserror 2.0.21",
]
[[package]]
name = "solidcraft-engine"
version = "0.40.0-rc.2"
@@ -4219,7 +4230,6 @@ dependencies = [
"gethostname",
"gltf",
"log",
"png",
"quick-xml",
"rustix 1.1.5",
"serde",
@@ -4230,10 +4240,8 @@ dependencies = [
"solidcraft-geom",
"solidcraft-kernel",
"solidcraft-profile",
"solidcraft-sketch",
"solidcraft-testkit",
"thiserror 2.0.21",
"tiny-skia",
"zip",
]
@@ -4278,9 +4286,11 @@ dependencies = [
name = "solidcraft-render"
version = "0.40.0-rc.2"
dependencies = [
"flate2",
"image",
"moxcms",
"png",
"robust",
"serde",
"serde_json",
"sha2 0.10.9",
@@ -4297,7 +4307,6 @@ dependencies = [
"harfrust",
"serde",
"serde_json",
"sha2 0.10.9",
"solidcore-record",
"solidcraft-geom",
"thiserror 2.0.21",
+4 -3
View File
@@ -22,6 +22,7 @@ solidcraft-kernel = { path = "crates/kernel" }
solidcraft-smlib = { path = "crates/smlib" }
solidcraft-step = { path = "crates/step" }
solidcraft-render = { path = "crates/render" }
solidcraft-dynamics = { path = "crates/dynamics" }
solidcraft-gpu = { path = "crates/gpu" }
solidcraft-doc = { path = "crates/doc" }
solidcraft-io = { path = "crates/io" }
@@ -81,9 +82,9 @@ gethostname = "1"
# Pinned: its IR and codec APIs change between minor versions. Open formats only: no default
# features, so cadmpeg's codecs for commercial formats (f3d, sldprt, inventor, catia, creo, nx)
# and its ShapeManager/ACIS `sat` codec are not compiled in. `step` is the writer we restate through.
cadmpeg-core = "=0.6.3"
cadmpeg-ir = "=0.6.3"
cadmpeg-registry = { version = "=0.6.3", default-features = false, features = ["fcstd", "rhino", "step"] }
cadmpeg-core = "=0.6.7"
cadmpeg-ir = "=0.6.7"
cadmpeg-registry = { version = "=0.6.7", default-features = false, features = ["fcstd", "rhino", "step"] }
# fonts (sketch text outlines; MIT OR Apache-2.0, pure Rust)
ttf-parser = { version = "0.25", default-features = false, features = ["std", "variable-fonts"] }
-6
View File
@@ -173,12 +173,6 @@ 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;
+14 -14
View File
@@ -57,13 +57,13 @@ bytemuck_derive 1.12.1 (Zlib OR Apache-2.0 OR MIT)
byteorder 1.5.0 (Unlicense OR MIT)
byteorder-lite 0.1.0 (Unlicense OR MIT)
bytes 1.12.1 (MIT)
cadmpeg-codec-freecad 0.6.3 (Apache-2.0)
cadmpeg-codec-rhino 0.6.3 (Apache-2.0)
cadmpeg-codec-step 0.6.3 (Apache-2.0)
cadmpeg-container 0.6.3 (Apache-2.0)
cadmpeg-core 0.6.3 (Apache-2.0)
cadmpeg-ir 0.6.3 (Apache-2.0)
cadmpeg-registry 0.6.3 (Apache-2.0)
cadmpeg-codec-freecad 0.6.7 (Apache-2.0)
cadmpeg-codec-rhino 0.6.7 (Apache-2.0)
cadmpeg-codec-step 0.6.7 (Apache-2.0)
cadmpeg-container 0.6.7 (Apache-2.0)
cadmpeg-core 0.6.7 (Apache-2.0)
cadmpeg-ir 0.6.7 (Apache-2.0)
cadmpeg-registry 0.6.7 (Apache-2.0)
calloop 0.13.0 (MIT)
calloop 0.14.5 (MIT)
calloop-wayland-source 0.3.0 (MIT)
@@ -15978,13 +15978,13 @@ accesskit_windows 0.32.1: MIT OR Apache-2.0 (https://github.com/AccessKit/access
accesskit_winit 0.32.2: Apache-2.0 (https://github.com/AccessKit/accesskit)
block2 0.5.1: MIT (https://github.com/madsmtm/objc2)
block2 0.6.2: MIT (https://github.com/madsmtm/objc2)
cadmpeg-codec-freecad 0.6.3: Apache-2.0 (https://github.com/cadmpeg/cadmpeg)
cadmpeg-codec-rhino 0.6.3: Apache-2.0 (https://github.com/cadmpeg/cadmpeg)
cadmpeg-codec-step 0.6.3: Apache-2.0 (https://github.com/cadmpeg/cadmpeg)
cadmpeg-container 0.6.3: Apache-2.0 (https://github.com/cadmpeg/cadmpeg)
cadmpeg-core 0.6.3: Apache-2.0 (https://github.com/cadmpeg/cadmpeg)
cadmpeg-ir 0.6.3: Apache-2.0 (https://github.com/cadmpeg/cadmpeg)
cadmpeg-registry 0.6.3: Apache-2.0 (https://github.com/cadmpeg/cadmpeg)
cadmpeg-codec-freecad 0.6.7: Apache-2.0 (https://github.com/cadmpeg/cadmpeg)
cadmpeg-codec-rhino 0.6.7: Apache-2.0 (https://github.com/cadmpeg/cadmpeg)
cadmpeg-codec-step 0.6.7: Apache-2.0 (https://github.com/cadmpeg/cadmpeg)
cadmpeg-container 0.6.7: Apache-2.0 (https://github.com/cadmpeg/cadmpeg)
cadmpeg-core 0.6.7: Apache-2.0 (https://github.com/cadmpeg/cadmpeg)
cadmpeg-ir 0.6.7: Apache-2.0 (https://github.com/cadmpeg/cadmpeg)
cadmpeg-registry 0.6.7: Apache-2.0 (https://github.com/cadmpeg/cadmpeg)
clipboard-win 5.4.1: BSL-1.0 (https://github.com/DoumanAsh/clipboard-win)
dispatch 0.2.0: MIT (http://github.com/SSheldon/rust-dispatch)
dispatch2 0.3.1: Zlib OR Apache-2.0 OR MIT (https://github.com/madsmtm/objc2)
-4
View File
@@ -63,10 +63,6 @@ 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();
+6 -3
View File
@@ -460,13 +460,16 @@ fn snapshot_runs_its_script_on_the_in_design() {
}
}
/// An unknown command's error suggests the id that was meant first; other close ids (such as
/// `drawing.open`) may follow it.
/// An unknown command's error suggests the id that was meant.
#[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` or `panel.open`?"),
"{}",
String::from_utf8_lossy(&output.stderr)
);
}
/// #1833: `--timings` reports each command's time instead of the measurement, `--profile` adds
+1 -1
View File
@@ -25,6 +25,7 @@ solidcraft-engine = { workspace = true }
solidcraft-ui-egui = { workspace = true }
egui = { workspace = true }
log = { workspace = true }
serde_json = { workspace = true }
url = "2"
percent-encoding = "2"
@@ -33,7 +34,6 @@ eframe = { workspace = true, features = ["default_fonts", "wgpu", "accesskit"] }
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
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", "FileSystemGetDirectoryOptions", "FileSystemWritableFileStream",
-9
View File
@@ -26,15 +26,6 @@
<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. -->
+4 -1
View File
@@ -553,7 +553,7 @@ mod web {
/// Open a design remembered from an earlier session (a recent one, after a reload): ask once
/// for access to the file (call in the click), read it into the engine's files and queue it
/// to open. A file that is gone is forgotten and dropped from the recent designs.
pub fn restore(files: &Rc<Files>, path: &str) {
pub fn restore(files: &Rc<Files>, path: &str, on_opened: Option<Box<dyn FnOnce()>>) {
let Some((id, name)) = parse(path) else { return };
let (files, path, id, name) = (files.clone(), path.to_string(), id.to_string(), name.to_string());
spawn_local(async move {
@@ -568,6 +568,9 @@ mod web {
files.opened.borrow_mut().push(path);
files.wake();
touch(&id).await;
if let Some(done) = on_opened {
done();
}
}
Err(e) => {
let gone = e.contains("NotFoundError") || e.contains("could not be found") || e.contains("no longer remembers");
+175 -5
View File
@@ -5,7 +5,8 @@
//! and go out as downloads (exports, and "Download design"). Where the File System Access API
//! exists a design also lives on the computer's disk, saved and opened through the browser's file
//! dialogs (`disk.rs`). An autosave of unsaved work is kept in OPFS and offered again after a
//! reload or a crash.
//! reload or a crash. With "keep unsaved work in this browser" on, each open design's unsaved
//! changes are kept there instead, one copy per design (`keep.rs`, #10220).
use std::cell::{Cell, RefCell};
use std::rc::Rc;
@@ -13,7 +14,7 @@ use std::rc::Rc;
use js_sys::{Array, Function, Object, Reflect, Uint8Array};
use solidcraft_engine::io::vfs;
use crate::disk;
use crate::{disk, keep};
use wasm_bindgen::{JsCast as _, JsValue, closure::Closure};
use wasm_bindgen_futures::{JsFuture, spawn_local};
use web_sys::{
@@ -79,6 +80,16 @@ pub struct Files {
pub recovery: RefCell<Option<(String, Option<String>)>>,
/// The app's context, to wake it when a browser call finishes.
pub ctx: RefCell<Option<egui::Context>>,
/// This page's id: the copies it keeps are named after it (`keep::entry_id`).
pub keep_page: RefCell<String>,
/// Kept copies stored by this page, and the design revision each holds once its write ended.
pub kept: RefCell<std::collections::HashMap<String, Option<u64>>>,
/// Kept copies being written or removed now (one browser call at a time per copy).
pub keeping: RefCell<std::collections::HashSet<String>>,
/// When (ms) the last write of a copy failed, to wait before trying again (`keep::may_write`).
pub kept_failed: RefCell<std::collections::HashMap<String, f64>>,
/// Copies earlier pages left, newer than their design's last save: offered at start.
pub keep_offered: RefCell<Vec<keep::Entry>>,
}
impl Files {
@@ -288,6 +299,9 @@ pub fn start(files: &Rc<Files>) {
let _ = JsFuture::from(p).await;
});
}
let page = keep::page_id(js_sys::Date::now(), js_sys::Math::random());
hold_page_lock(&page);
*files.keep_page.borrow_mut() = page;
let files = files.clone();
spawn_local(async move {
match disk::known_ids().await {
@@ -300,6 +314,7 @@ pub fn start(files: &Rc<Files>) {
files.say("This page can't use the browser's file storage (it needs https or localhost): designs are kept in local storage, a few MB at most.", false);
}
let mut entries = Vec::new();
let kept = kept_copies(&all);
let has_recovery = all.iter().any(|(n, _, _)| n == RECOVERY);
let legacy = (!has_recovery).then(|| LEGACY_RECOVERY.into_iter().find(|l| all.iter().any(|(n, _, _)| n == l))).flatten();
for (name, bytes, modified) in all {
@@ -315,7 +330,7 @@ pub fn start(files: &Rc<Files>) {
*files.recovery.borrow_mut() = Some((n, meta["path"].as_str().map(str::to_string)));
continue;
}
if name != RECOVERY {
if name != RECOVERY && keep::parse_name(&name).is_none() {
entries.push(Entry { name: name.clone(), size: bytes.len() as u64, modified });
}
vfs::insert(&format!("{OPFS}{name}"), bytes);
@@ -326,6 +341,7 @@ pub fn start(files: &Rc<Files>) {
if !vfs::exists(&format!("{OPFS}{RECOVERY}")) {
*files.recovery.borrow_mut() = None;
}
sort_kept(&files, kept, &live_pages().await);
}
Err(e) => files.say(format!("browser storage: {e}"), true),
}
@@ -343,7 +359,7 @@ pub fn flush(files: &Rc<Files>) {
{
let mut e = files.entries.borrow_mut();
e.retain(|x| x.name != name);
if name != RECOVERY && name != RECOVERY_META {
if name != RECOVERY && name != RECOVERY_META && keep::parse_name(&name).is_none() {
e.push(Entry { name: name.clone(), size: bytes.len() as u64, modified: js_sys::Date::now() });
e.sort_by_key(|a| a.name.to_lowercase());
}
@@ -473,7 +489,15 @@ pub fn recovery_path() -> String {
/// A recovery file lives in OPFS, but its relative links belong to the original design's
/// folder (which can be on disk). Resolve them before Open sees the temporary file location.
pub fn prepare_recovery(original_path: Option<&str>) -> Result<String, String> {
let path = recovery_path();
prepare_copy(recovery_path(), original_path)
}
/// The same for a kept copy (#10220).
pub fn prepare_kept(id: &str, original_path: Option<&str>) -> Result<String, String> {
prepare_copy(format!("{OPFS}{}", keep::design_file(id)), original_path)
}
fn prepare_copy(path: String, original_path: Option<&str>) -> Result<String, String> {
let bytes = vfs::read(&path, solidcraft_engine::io::MAX_DESIGN_BYTES).map_err(|e| e.to_string())?;
let mut doc = solidcraft_engine::io::read_design(&bytes).map_err(|e| e.to_string())?;
solidcraft_engine::resolve_design_links(&mut doc, original_path);
@@ -646,3 +670,149 @@ pub fn take_drops(files: &Rc<Files>, raw: &mut egui::RawInput, theme: bool) {
});
}
}
/// Hold this page's browser lock while it runs, so other tabs know its kept copies are in use
/// (`keep.rs`). Browsers without the Web Locks API hold none; their copies are then offered by
/// any later page.
fn hold_page_lock(page: &str) {
let Some(locks) = web_locks() else { return };
let Ok(request) = Reflect::get(&locks, &"request".into()).and_then(|f| f.dyn_into::<Function>().map_err(JsValue::from)) else { return };
// A promise that never settles: the lock is let go only when the page goes.
let hold = Closure::<dyn FnMut(JsValue) -> js_sys::Promise>::new(|_lock: JsValue| js_sys::Promise::new(&mut |_, _| {}));
let name = format!("{}{page}", keep::LOCK_PREFIX);
if let Err(e) = request.call2(&locks, &name.into(), hold.as_ref().unchecked_ref()) {
log::info!("keep: no page lock: {}", err(e));
}
// The callback lives as long as the page.
hold.forget();
}
fn web_locks() -> Option<JsValue> {
let nav = web_sys::window()?.navigator();
Reflect::get(&nav, &"locks".into()).ok().filter(|l| l.is_object())
}
/// The pages still open (in this browser profile) that hold their lock.
async fn live_pages() -> Vec<String> {
let query = async {
let locks = web_locks().ok_or("no Web Locks")?;
let f: Function = Reflect::get(&locks, &"query".into()).map_err(err)?.dyn_into().map_err(|_| "no query".to_string())?;
let p: js_sys::Promise = f.call0(&locks).map_err(err)?.dyn_into().map_err(|_| "no promise".to_string())?;
let state = JsFuture::from(p).await.map_err(err)?;
let held: Array = Reflect::get(&state, &"held".into()).map_err(err)?.dyn_into().map_err(|_| "no list".to_string())?;
Ok::<_, String>(
held.iter()
.take(10_000)
.filter_map(|l| Reflect::get(&l, &"name".into()).ok()?.as_string())
.filter_map(|n| n.strip_prefix(keep::LOCK_PREFIX).map(str::to_string))
.collect(),
)
};
query.await.unwrap_or_else(|e| {
log::info!("keep: open pages unknown ({e})");
Vec::new()
})
}
/// The kept copies among the stored files: (copy, when its design's file was last written), and
/// the ids of designs stored without their metadata.
struct Found {
copies: Vec<(keep::Entry, Option<f64>)>,
orphans: Vec<String>,
}
fn kept_copies(all: &[(String, Vec<u8>, f64)]) -> Found {
let modified = |path: &str| path.strip_prefix(OPFS).and_then(|n| all.iter().find(|(name, _, _)| name == n)).map(|(_, _, m)| *m);
let mut copies = Vec::new();
let mut designs = Vec::new();
for (name, bytes, _) in all {
match keep::parse_name(name) {
Some((id, keep::Part::Meta)) => {
if let Some(e) = keep::Entry::parse(id, bytes) {
let saved = e.path.as_deref().and_then(modified);
copies.push((e, saved));
}
}
Some((id, keep::Part::Design)) => designs.push(id.to_string()),
None => {}
}
}
// A copy is whole only with its design.
copies.retain(|(e, _)| designs.contains(&e.id));
let orphans = designs.into_iter().filter(|id| !copies.iter().any(|(e, _)| &e.id == id)).collect();
Found { copies, orphans }
}
/// Offer the copies earlier pages left that hold unsaved work, and remove those their design's
/// later save made stale (and parts without the rest), leaving open pages' copies alone.
fn sort_kept(files: &Rc<Files>, found: Found, live: &[String]) {
let mut offered = Vec::new();
let mut gone = Vec::new();
for (e, saved) in found.copies {
match keep::fate(&e, live, saved) {
keep::Fate::Offer => offered.push(e),
keep::Fate::Drop => gone.push(e.id),
keep::Fate::Leave => {}
}
}
gone.extend(found.orphans.into_iter().filter(|id| !keep::page_of(id).is_some_and(|p| live.iter().any(|l| l == p))));
keep::order_offered(&mut offered);
*files.keep_offered.borrow_mut() = offered;
for id in gone {
remove_kept(files, &id);
}
}
/// The path (in the engine's files) a kept copy opens from.
pub fn kept_path(id: &str) -> String {
format!("{OPFS}{}", keep::design_file(id))
}
/// Write the copy of a design with unsaved changes (`bytes`, described by `entry`) that holds
/// its revision `revision`. Its metadata goes last, so a copy is never offered half written.
pub fn write_kept(files: &Rc<Files>, entry: &keep::Entry, bytes: Vec<u8>, revision: u64) {
let id = entry.id.clone();
if !files.keeping.borrow_mut().insert(id.clone()) {
return;
}
files.kept.borrow_mut().entry(id.clone()).or_insert(None);
let meta = entry.to_json();
let files = files.clone();
spawn_local(async move {
let r = async {
write_file(&keep::design_file(&id), &bytes).await?;
write_file(&keep::meta_file(&id), meta.as_bytes()).await
};
match r.await {
Ok(()) => {
files.kept.borrow_mut().insert(id.clone(), Some(revision));
files.kept_failed.borrow_mut().remove(&id);
}
Err(e) => {
files.kept_failed.borrow_mut().insert(id.clone(), js_sys::Date::now());
files.say(format!("keeping unsaved work in this browser: {e}"), true)
}
}
files.keeping.borrow_mut().remove(&id);
files.wake();
});
}
/// Remove kept copy `id` (its design was saved, closed, discarded or restored).
pub fn remove_kept(files: &Rc<Files>, id: &str) {
let id = id.to_string();
if !files.keeping.borrow_mut().insert(id.clone()) {
return;
}
let _ = vfs::remove(&kept_path(&id));
files.kept_failed.borrow_mut().remove(&id);
let files = files.clone();
spawn_local(async move {
// The metadata first: what's left without it is never offered.
let _ = remove_file(&keep::meta_file(&id)).await;
let _ = remove_file(&keep::design_file(&id)).await;
files.kept.borrow_mut().remove(&id);
files.keeping.borrow_mut().remove(&id);
files.wake();
});
}
+360
View File
@@ -0,0 +1,360 @@
//! Keep unsaved work in this browser (#10220): the decisions, in plain Rust (tested natively).
//!
//! With Preferences › General › "Keep unsaved work in this browser instead of asking when the tab
//! closes" on, each open design with unsaved changes has a copy in the browser's private storage:
//! `keep-<id>.sc` (the design) and `keep-<id>.json` (its name, its file and when it last
//! changed). The copy is written shortly after each change ([`due`]) and again when the page is
//! hidden, so it is already current when the tab closes; the page then doesn't ask through the
//! browser's "leave site?" box ([`asks_before_unload`]). A design that is saved, closed or
//! discarded has its copy removed. The next visit offers the copies newer than their design's
//! last save ([`fate`]) in SolidCraft's own window, answered with [`plan`].
//!
//! An id is `<page>-<n>`: the page that wrote it (each page load has its own) and the design's
//! number in that page. A page holds a browser lock named after it while it runs, so another tab
//! never offers, or removes, the copies of a page that is still open.
#![cfg_attr(not(target_arch = "wasm32"), allow(dead_code))]
use serde_json::{Value, json};
/// The start of every kept file's name.
pub const PREFIX: &str = "keep-";
/// The browser lock a page holds while it runs: this, then its page id.
pub const LOCK_PREFIX: &str = "solidcraft.keep.";
/// A copy is written once the design has been still this long (ms)…
pub const QUIET_MS: f64 = 800.0;
/// …or at the latest this long after the first change it doesn't hold yet (ms).
pub const MAX_WAIT_MS: f64 = 4000.0;
/// The most copies offered at once (the rest wait for the next visit).
pub const MAX_OFFERED: usize = 32;
const MAX_ID: usize = 64;
const MAX_NAME: usize = 256;
const MAX_PATH: usize = 4096;
/// Copies' metadata is small: a damaged or hostile one is not read.
pub const MAX_META_BYTES: usize = 64 * 1024;
/// Ids are short and safe in a file name: ASCII letters, digits and dashes, with a page part.
pub fn valid_id(id: &str) -> bool {
!id.is_empty() && id.len() <= MAX_ID && id.chars().all(|c| c.is_ascii_alphanumeric() || c == '-') && page_of(id).is_some()
}
/// The page that wrote copy `id`.
pub fn page_of(id: &str) -> Option<&str> {
id.rsplit_once('-').map(|(page, _)| page).filter(|p| !p.is_empty())
}
/// This page's id, from the time it started (ms since 1970) and a random number in 0..1:
/// letters and digits only.
pub fn page_id(now_ms: f64, random: f64) -> String {
let now = if now_ms.is_finite() && now_ms > 0.0 { now_ms.min(1e15) as u64 } else { 0 };
let random = if random.is_finite() { (random.clamp(0.0, 1.0) * f64::from(0xff_ffff)) as u64 } else { 0 };
format!("{now:x}{random:06x}")
}
/// The copy of design `uid` (`Session::uid`) open in page `page`.
pub fn entry_id(page: &str, uid: u64) -> String {
format!("{page}-{uid}")
}
/// The stored file names of copy `id`: its design and its metadata.
pub fn design_file(id: &str) -> String {
format!("{PREFIX}{id}.{}", solidcraft_engine::io::DESIGN_EXT)
}
pub fn meta_file(id: &str) -> String {
format!("{PREFIX}{id}.json")
}
/// Which part of a copy a stored file is.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Part {
Design,
Meta,
}
/// A stored file's copy id and part; `None` for every other file.
pub fn parse_name(name: &str) -> Option<(&str, Part)> {
let rest = name.strip_prefix(PREFIX)?;
let (id, part) = if let Some(id) = rest.strip_suffix(".json") {
(id, Part::Meta)
} else {
(rest.strip_suffix(solidcraft_engine::io::DESIGN_EXT)?.strip_suffix('.')?, Part::Design)
};
valid_id(id).then_some((id, part))
}
/// A kept copy, as its metadata describes it.
#[derive(Clone, Debug, PartialEq)]
pub struct Entry {
pub id: String,
/// The design's name.
pub name: String,
/// Its file (`None`: never saved).
pub path: Option<String>,
/// When the design last changed (ms since 1970).
pub changed_at: f64,
}
impl Entry {
pub fn to_json(&self) -> String {
json!({"name": self.name, "path": self.path, "changed_at": self.changed_at}).to_string()
}
/// Read copy `id`'s metadata. Anything malformed, oversized or not finite is no copy.
pub fn parse(id: &str, bytes: &[u8]) -> Option<Entry> {
if !valid_id(id) || bytes.len() > MAX_META_BYTES {
return None;
}
let v: Value = serde_json::from_slice(bytes).ok()?;
let name = v.get("name")?.as_str()?.chars().take(MAX_NAME).collect::<String>();
let path = match v.get("path") {
None | Some(Value::Null) => None,
Some(p) => Some(p.as_str().filter(|p| !p.is_empty() && p.len() <= MAX_PATH)?.to_string()),
};
let changed_at = v.get("changed_at")?.as_f64().filter(|t| t.is_finite() && *t >= 0.0)?;
Some(Entry { id: id.to_string(), name, path, changed_at })
}
}
/// Whether a copy that last changed at `changed_at` holds work its design's file doesn't:
/// the file was last written at `saved_at` (ms since 1970; `None` or unknown: never saved, or
/// the time can't be read, so the copy is kept rather than lost).
pub fn newer_than_save(changed_at: f64, saved_at: Option<f64>) -> bool {
match saved_at {
Some(saved) if saved.is_finite() && saved > 0.0 => changed_at > saved,
_ => true,
}
}
/// What the next visit does with a copy it finds.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Fate {
/// Offer it: "Restore unsaved design…?".
Offer,
/// Its design was saved after it: remove it without asking.
Drop,
/// Its page is still open (another tab): leave it alone.
Leave,
}
/// The copy `entry`, whose design's file was last written at `saved_at`, while the pages
/// `live` are open.
pub fn fate(entry: &Entry, live: &[String], saved_at: Option<f64>) -> Fate {
if page_of(&entry.id).is_some_and(|p| live.iter().any(|l| l == p)) {
Fate::Leave
} else if newer_than_save(entry.changed_at, saved_at) {
Fate::Offer
} else {
Fate::Drop
}
}
/// Copies to offer first: the most recently changed, at most [`MAX_OFFERED`].
pub fn order_offered(entries: &mut Vec<Entry>) {
entries.sort_by(|a, b| b.changed_at.total_cmp(&a.changed_at).then_with(|| a.id.cmp(&b.id)));
entries.truncate(MAX_OFFERED);
}
/// Whether a design's copy is due to be written at `now`: the design last changed at
/// `last_change`, and the copy has lacked a change since `first_unwritten` (all ms).
pub fn due(now: f64, first_unwritten: f64, last_change: f64) -> bool {
now - last_change >= QUIET_MS || now - first_unwritten >= MAX_WAIT_MS
}
/// How long after a failed write of a design's copy it isn't tried again (ms).
pub const RETRY_MS: f64 = 30_000.0;
/// Whether a copy whose write last failed at `failed_at` (ms; `None`: it hasn't) may be written
/// at `now`: not before [`RETRY_MS`] has passed, so a full or denied store isn't hit every tick.
pub fn may_write(now: f64, failed_at: Option<f64>) -> bool {
failed_at.is_none_or(|t| !(now >= t) || now - t >= RETRY_MS)
}
/// Whether `wanted` is the design now open, given the open design's path.
pub fn is_open(open: Option<&str>, wanted: &str) -> bool {
open == Some(wanted)
}
/// Whether leaving the page asks through the browser's "leave site?" box: there is unsaved work,
/// and either the user still wants to be asked or it isn't all kept in the browser yet (with the
/// keep preference off it never is, so there the box always stays).
pub fn asks_before_unload(persists: bool, keep: bool, also_ask: bool, any_unsaved: bool, all_kept: bool) -> bool {
persists && any_unsaved && (also_ask || !(keep && all_kept))
}
/// An answer in the "Restore unsaved design?" window.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Answer {
/// Open the copy, with its unsaved changes, as the design it was (saving goes to its file).
Restore,
/// Open the design's file as last saved; the copy goes.
OpenSaved,
/// The copy goes.
Discard,
}
/// What an answer does.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Plan {
/// Open the copy (and, once it is open, give it back its name and file).
pub open_copy: bool,
/// Open this saved file.
pub open_saved: Option<String>,
/// Remove the copy.
pub drop_copy: bool,
}
/// What `answer` does to copy `entry`; `saved_exists`: its file can still be opened. `None`: the
/// answer isn't offered for it (Open saved version of a design never saved, or gone).
pub fn plan(answer: Answer, entry: &Entry, saved_exists: bool) -> Option<Plan> {
match answer {
Answer::Restore => Some(Plan { open_copy: true, open_saved: None, drop_copy: true }),
Answer::OpenSaved => {
let path = entry.path.clone().filter(|_| saved_exists)?;
Some(Plan { open_copy: false, open_saved: Some(path), drop_copy: true })
}
Answer::Discard => Some(Plan { open_copy: false, open_saved: None, drop_copy: true }),
}
}
/// The answers the window offers for `entry`, in order.
pub fn answers(entry: &Entry, saved_exists: bool) -> Vec<Answer> {
[Answer::Restore, Answer::OpenSaved, Answer::Discard].into_iter().filter(|a| plan(*a, entry, saved_exists).is_some()).collect()
}
#[cfg(test)]
mod tests {
use super::*;
fn entry(id: &str, changed_at: f64, path: Option<&str>) -> Entry {
Entry { id: id.into(), name: "Bracket".into(), path: path.map(str::to_string), changed_at }
}
#[test]
fn a_failed_write_waits_before_the_next_try() {
assert!(may_write(1000.0, None));
assert!(!may_write(1000.0 + RETRY_MS - 1.0, Some(1000.0)));
assert!(may_write(1000.0 + RETRY_MS, Some(1000.0)));
// A clock that went back doesn't block for good.
assert!(may_write(10.0, Some(1000.0)));
}
#[test]
fn a_design_counts_as_opened_only_at_its_own_path() {
assert!(is_open(Some("a.sc"), "a.sc"));
assert!(!is_open(Some("b.sc"), "a.sc"));
assert!(!is_open(None, "a.sc"));
}
#[test]
fn copies_are_named_by_page_and_design_and_nothing_else_reads_as_one() {
let page = page_id(1_700_000_000_000.0, 0.5);
assert!(page.chars().all(|c| c.is_ascii_hexdigit()));
let id = entry_id(&page, 7);
assert!(valid_id(&id));
assert_eq!(page_of(&id), Some(page.as_str()));
assert_eq!(parse_name(&design_file(&id)), Some((id.as_str(), Part::Design)));
assert_eq!(parse_name(&meta_file(&id)), Some((id.as_str(), Part::Meta)));
for other in ["part.sc", "recovery.sc", "recovery.json", "keep-.sc", "keep-x.sc", "keep-a/b-1.sc", "keep-a-1.step", "keep-a-1sc"] {
assert_eq!(parse_name(other), None, "{other}");
}
// Hostile clocks still give a usable id.
for (now, random) in [(f64::NAN, f64::NAN), (f64::INFINITY, -1.0), (-5.0, 9.0)] {
assert!(valid_id(&entry_id(&page_id(now, random), 1)));
}
}
#[test]
fn metadata_round_trips_and_hostile_metadata_is_no_copy() {
let e = entry("a1-3", 1234.0, Some("/opfs/Bracket.sc"));
assert_eq!(Entry::parse("a1-3", e.to_json().as_bytes()), Some(e.clone()));
let never_saved = entry("a1-4", 99.0, None);
assert_eq!(Entry::parse("a1-4", never_saved.to_json().as_bytes()), Some(never_saved));
for bad in [
"",
"[]",
r#"{"name":"x","changed_at":"soon"}"#,
r#"{"name":"x","changed_at":-1}"#,
r#"{"name":"x","changed_at":1e999}"#,
r#"{"name":"x","path":7,"changed_at":1}"#,
r#"{"name":"x","path":"","changed_at":1}"#,
r#"{"changed_at":1}"#,
] {
assert_eq!(Entry::parse("a1-3", bad.as_bytes()), None, "{bad}");
}
assert_eq!(Entry::parse("../x", e.to_json().as_bytes()), None);
assert_eq!(Entry::parse("a1-3", &vec![b' '; MAX_META_BYTES + 1]), None);
let long = json!({"name": "n".repeat(10_000), "changed_at": 1}).to_string();
assert_eq!(Entry::parse("a1-3", long.as_bytes()).map(|e| e.name.len()), Some(MAX_NAME));
}
#[test]
fn a_copy_newer_than_the_last_save_is_offered_an_older_one_dropped_an_open_pages_left() {
let e = entry("p1-1", 2000.0, Some("/opfs/Bracket.sc"));
// Changed after the file was last written: offered.
assert_eq!(fate(&e, &[], Some(1000.0)), Fate::Offer);
// Saved since (in this tab or another): nothing to offer.
assert_eq!(fate(&e, &[], Some(2000.0)), Fate::Drop);
assert_eq!(fate(&e, &[], Some(3000.0)), Fate::Drop);
// Never saved, or the save time can't be read: kept rather than lost.
for saved in [None, Some(0.0), Some(f64::NAN), Some(f64::INFINITY), Some(-4.0)] {
assert_eq!(fate(&e, &[], saved), Fate::Offer, "{saved:?}");
}
// Its page is still open in another tab: neither offered nor removed.
assert_eq!(fate(&e, &["p1".into()], Some(1000.0)), Fate::Leave);
assert_eq!(fate(&e, &["p1".into()], Some(3000.0)), Fate::Leave);
assert_eq!(fate(&e, &["p2".into()], Some(1000.0)), Fate::Offer);
}
#[test]
fn the_newest_copies_are_offered_first_and_at_most_a_windowful() {
let mut list: Vec<Entry> = (0..40).map(|i| entry(&format!("p-{i}"), f64::from(i), None)).collect();
order_offered(&mut list);
assert_eq!(list.len(), MAX_OFFERED);
assert_eq!(list.first().map(|e| e.id.as_str()), Some("p-39"));
}
#[test]
fn a_copy_is_written_once_the_design_is_still_or_after_the_longest_wait() {
// Still changing, not waited long: not yet.
assert!(!due(1000.0, 900.0, 900.0));
// Still for QUIET_MS: due.
assert!(due(900.0 + QUIET_MS, 900.0, 900.0));
// Changing all the time: due after MAX_WAIT_MS anyway.
assert!(!due(5000.0, 2000.0, 4900.0));
assert!(due(2000.0 + MAX_WAIT_MS, 2000.0, 2000.0 + MAX_WAIT_MS - 1.0));
}
#[test]
fn the_browser_asks_before_unload_unless_told_not_to_and_every_copy_is_current() {
// Keeping off: exactly as before, unsaved work asks, whatever the second option says.
assert!(asks_before_unload(true, false, true, true, true));
assert!(asks_before_unload(true, false, true, true, false));
assert!(asks_before_unload(true, false, false, true, true));
// Keeping on, asking on (the default): both, the box and the copy.
assert!(asks_before_unload(true, true, true, true, true));
// Keeping on, asking off, every unsaved design kept: no browser box.
assert!(!asks_before_unload(true, true, false, true, true));
// Keeping on, asking off, a copy still being written (or failed): the browser still asks.
assert!(asks_before_unload(true, true, false, true, false));
// Nothing unsaved, or an embedded page that keeps nothing: never.
assert!(!asks_before_unload(true, true, true, false, false));
assert!(!asks_before_unload(true, false, true, false, false));
assert!(!asks_before_unload(false, false, true, true, false));
}
#[test]
fn restore_open_saved_and_discard_each_do_one_thing_and_always_remove_the_copy() {
let saved = entry("p-1", 5.0, Some("/opfs/Bracket.sc"));
assert_eq!(plan(Answer::Restore, &saved, true), Some(Plan { open_copy: true, open_saved: None, drop_copy: true }));
assert_eq!(
plan(Answer::OpenSaved, &saved, true),
Some(Plan { open_copy: false, open_saved: Some("/opfs/Bracket.sc".into()), drop_copy: true })
);
assert_eq!(plan(Answer::Discard, &saved, true), Some(Plan { open_copy: false, open_saved: None, drop_copy: true }));
assert_eq!(answers(&saved, true), [Answer::Restore, Answer::OpenSaved, Answer::Discard]);
// A design never saved, or whose file is gone, has no saved version to open.
let never = entry("p-2", 5.0, None);
assert_eq!(plan(Answer::OpenSaved, &never, true), None);
assert_eq!(plan(Answer::OpenSaved, &saved, false), None);
assert_eq!(answers(&never, false), [Answer::Restore, Answer::Discard]);
}
}
+280 -78
View File
@@ -3,7 +3,8 @@
//! Runs the same [`solidcraft_ui_egui::SolveApp`] as the desktop app through eframe's web runner
//! (wgpu: WebGPU where available, WebGL2 otherwise). Build with `trunk build --release` from this
//! directory (output in `dist/web`). URL flags: `?webgl` forces WebGL2 (remembered until
//! `?webgpu`), `?sample` opens the sample part.
//! `?webgpu`), `?sample` opens the sample part, `?embed=editor` runs it as an editing embed in a
//! host page (no application bar or start page; nothing kept in the browser; #8517).
#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)]
#[cfg(target_arch = "wasm32")]
@@ -18,16 +19,13 @@ mod easyprint;
mod files;
#[cfg(target_arch = "wasm32")]
mod install;
#[cfg(any(target_arch = "wasm32", test))]
mod keep;
#[cfg(target_arch = "wasm32")]
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.)
@@ -47,6 +45,8 @@ fn query_flag(query: &str, name: &str) -> bool {
struct Launch {
open: Option<String>,
embed: bool,
/// `?embed=editor` (#8517): an editing embed in a host page that keeps the files itself.
editor: bool,
}
#[cfg(any(target_arch = "wasm32", test))]
@@ -57,16 +57,19 @@ impl Launch {
for (key, value) in url::form_urlencoded::parse(query.trim_start_matches('?').as_bytes()) {
match key.as_ref() {
"open" if launch.open.is_none() => launch.open = Some(value.into_owned()),
"embed" if value == "editor" => launch.editor = true,
"embed" => embed = true,
_ => {}
}
}
launch.embed = embed && launch.open.is_some();
launch.embed = embed && launch.open.is_some() && !launch.editor;
launch
}
/// The browser's own storage (designs, autosave, recovery, preferences) is used: not when
/// embedded, where the host keeps what is kept.
fn persists(&self) -> bool {
!self.embed
!self.embed && !self.editor
}
fn full_app_url(&self, mut url: url::Url) -> String {
@@ -183,6 +186,7 @@ mod web {
use crate::api::Api;
use crate::disk::{self, SaveTarget};
use crate::files::{self, Files, Intent};
use crate::keep;
use crate::space_mouse::SpaceMouse;
const CANVAS_ID: &str = "solidcraft_canvas";
@@ -242,7 +246,9 @@ mod web {
}
/// The browser's "leave site? changes you made may not be saved" prompt while a design has
/// unsaved changes (the page can't show its own dialog when it is being closed).
/// unsaved changes (the page can't show its own dialog when it is being closed). With "keep
/// unsaved work in this browser" on and "still ask" off, there is nothing to ask once every
/// unsaved design's copy is current (`keep::asks_before_unload`, #10220).
fn ask_before_unload(window: &web_sys::Window) {
let on_unload = wasm_bindgen::closure::Closure::<dyn FnMut(web_sys::BeforeUnloadEvent)>::new(|e: web_sys::BeforeUnloadEvent| {
if UNSAVED.with(std::cell::Cell::get) {
@@ -255,6 +261,50 @@ mod web {
on_unload.forget();
}
/// With "keep unsaved work in this browser" on, the page brings every unsaved design's copy up
/// to date when it is hidden or goes (`pagehide`, `visibilitychange`): the browser may not
/// wait for that write, so the copies are written shortly after each change as well
/// (`Shell::keep_tick`, #10220).
fn keep_when_hidden(window: &web_sys::Window, runner: &eframe::WebRunner) {
let keep_now = {
let runner = runner.clone();
move || {
if let Some(mut shell) = runner.app_mut::<Shell>() {
shell.keep_tick(None, true);
}
}
};
let on_hide = {
let keep_now = keep_now.clone();
wasm_bindgen::closure::Closure::<dyn FnMut(web_sys::Event)>::new(move |_: web_sys::Event| keep_now())
};
let on_visibility = wasm_bindgen::closure::Closure::<dyn FnMut(web_sys::Event)>::new(move |_: web_sys::Event| {
let hidden = web_sys::window()
.and_then(|w| w.document())
.and_then(|d| js_sys::Reflect::get(&d, &"visibilityState".into()).ok())
.and_then(|v| v.as_string())
.is_some_and(|v| v == "hidden");
if hidden {
keep_now();
}
});
let _ = window.add_event_listener_with_callback("pagehide", on_hide.as_ref().unchecked_ref());
if let Some(document) = window.document() {
let _ = document.add_event_listener_with_callback("visibilitychange", on_visibility.as_ref().unchecked_ref());
}
// The listeners live as long as the page.
on_hide.forget();
on_visibility.forget();
}
/// One open design followed for its kept copy: its revision, when that last changed, and since
/// when its copy has lacked a change (`None`: current, or being written).
struct Track {
revision: u64,
last_change: f64,
first_unwritten: Option<f64>,
}
/// eframe cancels the browser's action for every Tab it gets, so the browser couldn't switch
/// tabs while the app has the keyboard. Its tab-switching keys stop here, on the way down to
/// the canvas, before eframe sees them; the browser then acts on them as usual.
@@ -292,8 +342,6 @@ 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,
@@ -324,14 +372,15 @@ 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).
prefs_writer: crate::PrefsWriter,
other_tab_prefs: PrefsInbox,
/// The open designs followed for their kept copy, by `Session::uid` (#10220).
keep_track: std::collections::HashMap<u64, Track>,
/// Every unsaved design's kept copy is current (and the preference is on).
all_kept: bool,
}
impl Shell {
@@ -446,8 +495,14 @@ mod web {
self.sync_recents();
}
let now = ctx.input(|i| i.time);
// With "keep unsaved work in this browser" on, each design's own copy takes over from
// the single autosave (#10220).
if self.app.preferences.keep_unsaved_in_browser && self.autosaved.is_some() {
files::autosave(&self.files, None);
self.autosaved = None;
}
// Unsaved work is kept (at most every AUTOSAVE_S); saved work needs no autosave.
if self.files.ready.get() && self.files.recovery.borrow().is_none() {
if self.files.ready.get() && self.files.recovery.borrow().is_none() && !self.app.preferences.keep_unsaved_in_browser {
let s = &self.app.session;
let changed = s.is_dirty() && self.autosaved != Some(s.revision);
let wait = AUTOSAVE_S - (now - self.autosaved_at);
@@ -466,6 +521,175 @@ mod web {
if !self.files.ready.get() || !self.files.incoming.borrow().is_empty() {
ctx.request_repaint_after(std::time::Duration::from_millis(100));
}
self.all_kept = self.keep_tick(Some(ctx), false);
}
/// Keep each open design's unsaved changes in the browser, when the preference asks for
/// it: a design's copy is written once it has been still a moment (`keep::due`), or at
/// once with `now` (the page is being hidden); a copy whose design is saved or closed, or
/// with the preference off, goes. Answers whether every unsaved design's copy is current.
fn keep_tick(&mut self, ctx: Option<&egui::Context>, now: bool) -> bool {
if !self.launch.persists() || !self.files.ready.get() {
return false;
}
let Shell { app, files, keep_track, .. } = self;
let on = app.preferences.keep_unsaved_in_browser;
let time = js_sys::Date::now();
let page = files.keep_page.borrow().clone();
let mut wanted = std::collections::HashSet::new();
let mut all_kept = true;
let mut wake: Option<f64> = None;
let mut error = None;
let mut soon = |ms: f64| wake = Some(wake.map_or(ms, |w: f64| w.min(ms)));
if on {
for s in solidcraft_ui_egui::documents::open_sessions(app).into_iter().filter(|s| s.is_dirty()) {
let id = keep::entry_id(&page, s.uid);
wanted.insert(s.uid);
let t = keep_track.entry(s.uid).or_insert(Track { revision: s.revision, last_change: time, first_unwritten: None });
if t.revision != s.revision {
t.revision = s.revision;
t.last_change = time;
}
if files.kept.borrow().get(&id).copied().flatten() == Some(s.revision) {
t.first_unwritten = None;
continue;
}
all_kept = false;
if files.keeping.borrow().contains(&id) {
soon(100.0);
continue;
}
let first = *t.first_unwritten.get_or_insert(time);
let failed = files.kept_failed.borrow().get(&id).copied();
if !keep::may_write(time, failed) {
// The last write failed: try again after a while.
soon(keep::RETRY_MS);
continue;
}
if now || keep::due(time, first, t.last_change) {
match solidcraft_engine::io::write_design_container(&s.design()) {
Ok(bytes) => {
let entry = keep::Entry { id, name: s.doc.name.clone(), path: s.path.clone(), changed_at: t.last_change };
files::write_kept(files, &entry, bytes, s.revision);
t.first_unwritten = None;
}
Err(e) => {
files.kept_failed.borrow_mut().insert(id, time);
error = Some(format!("keeping unsaved work in this browser: {e}"));
}
}
soon(100.0);
} else {
soon((keep::QUIET_MS - (time - t.last_change)).min(keep::MAX_WAIT_MS - (time - first)).max(16.0));
}
}
}
// This page's copies no design needs any more: saved, closed, or the preference off.
let stale: Vec<String> = files
.kept
.borrow()
.keys()
.filter(|id| keep::page_of(id) == Some(page.as_str()))
.filter(|id| !wanted.iter().any(|uid| keep::entry_id(&page, *uid) == **id))
.cloned()
.collect();
for id in stale {
files::remove_kept(files, &id);
soon(100.0);
}
keep_track.retain(|uid, _| wanted.contains(uid));
if let Some(e) = error {
app.set_status(e, true);
}
if let (Some(ctx), Some(ms)) = (ctx, wake) {
ctx.request_repaint_after(std::time::Duration::from_secs_f64(ms / 1000.0));
}
on && all_kept
}
/// Offer the copies an earlier visit kept (#10220): "Restore unsaved design: <name>,
/// changed <time>?", one window for all of them, each with Restore, Open saved version
/// (when it has a file) and Discard.
fn keep_prompt(&mut self, ctx: &egui::Context) {
let offered = self.files.keep_offered.borrow().clone();
if offered.is_empty() || !self.files.ready.get() {
return;
}
let now = (js_sys::Date::now() / 1000.0) as u64;
let rows: Vec<_> = offered
.iter()
.map(|e| solidcraft_ui_egui::recovery_ui::BrowserCopy {
name: &e.name,
when: solidcraft_ui_egui::recovery_ui::ago_between(now, (e.changed_at / 1000.0) as u64),
open_saved: keep::answers(e, self.saved_exists(e)).contains(&keep::Answer::OpenSaved),
})
.collect();
let act = solidcraft_ui_egui::recovery_ui::browser_copies_window(ctx, &self.app.preferences.language, &rows);
let Some((index, answer)) = act else { return };
let Some(entry) = offered.get(index) else { return };
let answer = match answer {
solidcraft_ui_egui::recovery_ui::CopyAnswer::Restore => keep::Answer::Restore,
solidcraft_ui_egui::recovery_ui::CopyAnswer::OpenSaved => keep::Answer::OpenSaved,
solidcraft_ui_egui::recovery_ui::CopyAnswer::Discard => keep::Answer::Discard,
};
self.keep_answer(entry, answer);
}
/// Whether a kept copy's design file can still be opened: in the browser's storage, or a
/// file on the computer this browser remembers.
fn saved_exists(&self, e: &keep::Entry) -> bool {
e.path.as_deref().is_some_and(|p| vfs::exists(p) || disk::parse(p).is_some_and(|(id, _)| self.files.disk_known.borrow().contains(id)))
}
/// Act on an answer in the "Restore unsaved design?" window (`keep::plan`).
fn keep_answer(&mut self, entry: &keep::Entry, answer: keep::Answer) {
let Some(plan) = keep::plan(answer, entry, self.saved_exists(entry)) else { return };
if plan.open_copy {
let copy = match files::prepare_kept(&entry.id, entry.path.as_deref()) {
Ok(copy) => copy,
Err(error) => {
self.app.set_status(error, true);
return;
}
};
self.app.open_path(&copy);
if self.app.session.path.as_deref() != Some(copy.as_str()) {
// Not opened (the status says why): it stays offered.
return;
}
// It is its own design again (saving goes to its file), with its changes unsaved.
self.app.session.path = entry.path.clone();
self.app.session.doc_mut().name = entry.name.clone();
self.app.session.mark_unsaved();
self.app.home.open = false;
}
if let Some(path) = &plan.open_saved {
if vfs::exists(path) {
self.app.open_path(path);
if !keep::is_open(self.app.session.path.as_deref(), path) {
// Not opened (the status says why): the copy stays offered.
return;
}
} else {
// A file on the computer: the browser asks for access again, in this click.
// The copy goes once the file has opened, not before: if access is refused,
// the picker is dismissed or the file is gone, it stays offered.
let (files, id) = (self.files.clone(), entry.id.clone());
disk::restore(
&self.files,
path,
Some(Box::new(move || {
files.keep_offered.borrow_mut().retain(|e| e.id != id);
files::remove_kept(&files, &id);
})),
);
return;
}
}
if plan.drop_copy {
self.files.keep_offered.borrow_mut().retain(|e| e.id != entry.id);
files::remove_kept(&self.files, &entry.id);
}
}
/// The start page's recent designs are the stored ones: the remembered order first (those
@@ -702,6 +926,13 @@ mod web {
files::autosave(&self.files, None);
}
/// Whether leaving the page asks through the browser's own box (`keep::asks_before_unload`).
fn asks_before_unload(&self) -> bool {
let any = solidcraft_ui_egui::documents::any_dirty(&self.app);
let prefs = &self.app.preferences;
keep::asks_before_unload(self.launch.persists(), prefs.keep_unsaved_in_browser, prefs.keep_unsaved_also_asks, any, self.all_kept)
}
/// Whether the shell already has the on-demand face the live language needs.
fn script_font_ready(&self) -> bool {
match solidcraft_ui_egui::fonts::web_script_font_kind(solidcraft_ui_egui::i18n::effective_language(&self.app.preferences.language)) {
@@ -730,10 +961,6 @@ 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);
}
}
@@ -747,9 +974,6 @@ 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
}
};
@@ -785,12 +1009,10 @@ mod web {
self.font_language = effective.to_owned();
}
/// 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).
/// 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).
fn watch_text_for_font(&mut self, ctx: &egui::Context) {
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() {
if self.font.watching_text() && solidcraft_ui_egui::fonts::painted_cjk(ctx) && self.font.text_needs_font() {
self.fetch_font(ctx, "ja");
}
}
@@ -811,33 +1033,6 @@ 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 {
@@ -845,12 +1040,11 @@ 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);
self.files_tick(ctx);
UNSAVED.with(|u| u.set(solidcraft_ui_egui::documents::any_dirty(&self.app)));
UNSAVED.with(|u| u.set(self.asks_before_unload()));
DESIGN_SHOWN.with(|d| d.set(solidcraft_ui_egui::toolbar::design_shown(&self.app)));
}
fn raw_input_hook(&mut self, ctx: &egui::Context, raw: &mut egui::RawInput) {
@@ -870,7 +1064,7 @@ mod web {
}
fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {
self.linked.tick(&mut self.app, ui.ctx());
UNSAVED.with(|u| u.set(self.launch.persists() && solidcraft_ui_egui::documents::any_dirty(&self.app)));
UNSAVED.with(|u| u.set(self.asks_before_unload()));
if self.launch.embed {
ui_kit::Container::new(ui.ctx(), ui_kit::Surface::Chrome).edge("embed_header", ui_kit::Edge::Top).show(ui, |ui| {
if ui_kit::button(ui, solidcraft_ui_egui::i18n::tr("web.open_full"), ui_kit::Button::Normal).clicked()
@@ -885,10 +1079,15 @@ mod web {
if self.launch.persists() {
self.file_panel(&ctx);
self.recovery_prompt(&ctx);
self.keep_prompt(&ctx);
}
self.watch_text_for_font(&ctx);
self.store_prefs(&ctx);
}
#[cfg(target_arch = "wasm32")]
fn as_any_mut(&mut self) -> Option<&mut dyn std::any::Any> {
Some(&mut *self)
}
fn persist_egui_memory(&self) -> bool {
// 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()
@@ -963,7 +1162,8 @@ mod web {
{
create.instance_descriptor.backends = eframe::wgpu::Backends::GL;
}
let result = eframe::WebRunner::new()
let runner = eframe::WebRunner::new();
let result = runner
.start(
canvas,
options,
@@ -975,7 +1175,7 @@ mod web {
}
let (panel, opener, saver, shown) = (files.clone(), files.clone(), files.clone(), files.clone());
let (restorable, restorer) = (files.clone(), files.clone());
let services = if launch.embed { Services::default() } else { Services {
let services = if !launch.persists() { Services::default() } else { Services {
// Open shows the browser's open dialog for a design where it has one
// (the file keeps its handle, so Save writes back to it), else the
// browser's files window. A command that reads another kind of file
@@ -1007,7 +1207,7 @@ mod web {
can_restore: Some(Box::new(move |path: &str| {
disk::parse(path).is_some_and(|(id, _)| restorable.disk_known.borrow().contains(id))
})),
restore_file: Some(Box::new(move |path: &str| disk::restore(&restorer, path))),
restore_file: Some(Box::new(move |path: &str| disk::restore(&restorer, path, None))),
download_design: Some(Box::new(files::download)),
easyprint_upload: Some(std::sync::Arc::new(crate::easyprint::upload)),
browser_files: Some(Box::new(move || shown.panel.set(true))),
@@ -1019,6 +1219,7 @@ mod web {
let (sender, receiver) = std::sync::mpsc::channel();
let mut app = SolveApp::new(Session::default(), services).with_control(receiver);
app.embedded = launch.embed;
app.embed_editor = launch.editor;
// Listening before reading: settings another tab writes from now on are
// newer than those read here.
let other_tab_prefs = PrefsInbox::default();
@@ -1069,16 +1270,22 @@ 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(),
other_tab_prefs,
keep_track: Default::default(),
all_kept: false,
}))
}),
)
.await;
if result.is_ok()
&& crate::Launch::parse(&query()).persists()
&& let Some(window) = web_sys::window()
{
keep_when_hidden(&window, &runner);
}
if let Some(el) = document.get_element_by_id(LOADING_ID) {
match result {
Ok(()) => dismiss_loading(el),
@@ -1131,6 +1338,17 @@ mod tests {
assert!(full.fragment().is_none());
}
#[test]
fn embed_editor_needs_no_design_link_and_keeps_nothing_in_the_browser() {
let editor = Launch::parse("?embed=editor");
assert!(editor.editor && !editor.embed && !editor.persists());
let linked = Launch::parse("?embed=editor&open=https://example.org/a.sc");
assert!(linked.editor && !linked.embed, "an editing embed may open a linked design, still editable");
let viewer = Launch::parse("?embed=viewer&open=https://example.org/a.sc");
assert!(viewer.embed && !viewer.editor, "any other value is the viewer embed, as before");
assert!(!Launch::parse("?other=editor").editor);
}
#[test]
fn design_links_refuse_hostile_schemes_credentials_and_sizes() {
assert!(design_url("https://example.org/arm.solidcraft?download=1", "http://solidcraft.io").is_ok());
@@ -1236,20 +1454,4 @@ mod tests {
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}");
}
}
}
+100
View File
@@ -0,0 +1,100 @@
//! Is the file chooser that opens the desktop's own? (#10159)
//!
//! `rfd` opens the file chooser through the XDG desktop portal; without the portal it falls
//! back to zenity, a window that does not belong to the app and that the app never mentions.
//! The desktop's own chooser is the portal one, so the app has to know when it is not the one
//! that opens, and say so in the session log.
//!
//! The session bus is asked once, on a thread of its own: a bus that answers late must not
//! hold the window. What it answered is kept, and read when a file is opened.
use std::sync::atomic::{AtomicBool, AtomicU8, Ordering};
/// The portal's bus name: the desktop's own file chooser lives behind it.
const PORTAL: &str = "org.freedesktop.portal.Desktop";
/// The bus has not answered yet.
const UNKNOWN: u8 = 0;
/// There is no session bus to reach the portal on.
const NO_SESSION_BUS: u8 = 1;
/// The bus is there but the portal does not answer on it.
const NO_PORTAL_SERVICE: u8 = 2;
/// The bus is there and the portal answers on it.
const PORTAL_THERE: u8 = 3;
/// What the session bus said about the portal.
static BUS: AtomicU8 = AtomicU8::new(UNKNOWN);
/// A file chooser is opening: whether it is the desktop's own is to be said.
static OPENING: AtomicBool = AtomicBool::new(false);
/// Ask the session bus, on a thread of its own, whether the portal is there.
pub fn start(ctx: &egui::Context) {
let thread_ctx = ctx.clone();
let probe = move || {
let answer = match session_bus() {
None => NO_SESSION_BUS,
Some(connection) => match portal_on(&connection) {
true => PORTAL_THERE,
false => NO_PORTAL_SERVICE,
},
};
BUS.store(answer, Ordering::Relaxed);
// The answer is in: a file chooser waiting for it can be told now.
thread_ctx.request_repaint();
};
if let Err(e) = std::thread::Builder::new().name("desktop-chooser".into()).spawn(probe) {
log::warn!("file chooser: no thread to ask the session bus: {e}");
// Nothing will answer: the desktop's own chooser cannot be told apart from the start.
BUS.store(PORTAL_THERE, Ordering::Relaxed);
}
}
/// The session bus, when there is one to talk to.
fn session_bus() -> Option<zbus::blocking::Connection> {
zbus::blocking::Connection::session().ok()
}
/// Whether the portal answers on the bus: it is either running on it or waiting to be started.
fn portal_on(connection: &zbus::blocking::Connection) -> bool {
let Ok(bus) = zbus::blocking::fdo::DBusProxy::new(connection) else { return false };
let named = |names: Vec<zbus::names::OwnedBusName>| names.iter().any(|name| name.as_str() == PORTAL);
// A portal that is running is on the bus; one that is not running yet is still there when
// the bus can start it, which is what the portal's service file is for.
match (bus.list_names().map(named), bus.list_activatable_names().map(named)) {
(Ok(running), Ok(startable)) => running || startable,
(Ok(running), Err(_)) => running,
(Err(_), Ok(startable)) => startable,
// The bus does not answer: nothing answers on it.
(Err(_), Err(_)) => false,
}
}
/// A file chooser is opening: ask for a frame, so that the reason it is not the desktop's own
/// is said (#10159).
pub fn note_opening(ctx: &egui::Context) {
OPENING.store(true, Ordering::Relaxed);
ctx.request_repaint();
}
/// The reason the file chooser that is opening is not the desktop's own, if it is not.
///
/// The decision is the one the test states: without a session bus there is no bus to reach the
/// portal on; with a bus but no portal on it, `rfd` falls back to zenity; and under XWayland the
/// window is an X11 one while the session is a Wayland one, which the portal refuses to parent
/// its chooser to.
pub fn take_reason() -> Option<&'static str> {
if !OPENING.load(Ordering::Relaxed) {
return None;
}
let (dbus_session, portal_service) = match BUS.load(Ordering::Relaxed) {
NO_SESSION_BUS => (false, false),
NO_PORTAL_SERVICE => (true, false),
PORTAL_THERE => (true, true),
// The bus has not answered yet: wait for it, and say nothing yet.
_ => return None,
};
OPENING.store(false, Ordering::Relaxed);
let wayland_display = std::env::var_os("WAYLAND_DISPLAY").is_some();
let x11_display = super::window_system_now() == Some("x11");
super::desktop_chooser_fallback(dbus_session, portal_service, wayland_display, x11_display)
}
+215 -11
View File
@@ -23,6 +23,8 @@ mod app_icon;
mod come_forward;
mod control_server;
#[cfg(target_os = "linux")]
mod desktop_chooser;
#[cfg(target_os = "linux")]
mod desktop_theme;
#[cfg(any(target_os = "windows", target_os = "macos"))]
mod desktop_transparency;
@@ -242,6 +244,12 @@ impl eframe::App for App {
menu.tick(&mut self.solve, ctx);
}
self.solve.logic(ctx);
// The file chooser that opened is not the desktop's own: the user has to be told (#10159).
#[cfg(target_os = "linux")]
if let Some(reason) = desktop_chooser::take_reason() {
let text = self.solve.tr(reason).to_string();
self.solve.set_status(text, false);
}
self.dialog_folder.design(self.solve.session.path.as_deref());
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
if !self.solve.file_picker_busy() {
@@ -295,6 +303,8 @@ impl eframe::App for App {
log::warn!("a picture was still being drawn as the app closed");
}
solidcraft_ui_egui::recovery_ui::close(&mut self.solve);
// A GPU path tracer setup still running did not stop the app (#9236).
solidcraft_ui_egui::render_workspace::close(&mut self.solve);
solidcraft_ui_egui::open_elsewhere::close(&mut self.solve);
// Before a restart (`Drop for App`) starts the next instance, which would otherwise get
// nothing from the 3D mouse driver (`solidcraft_3dx_client::stop`).
@@ -471,12 +481,19 @@ fn fullscreen_content_insets(raw: &mut egui::RawInput, title_height: f32, zoom:
fn services(cc: &eframe::CreationContext<'_>, folder: &DialogFolder) -> Services {
let parent = rfd::AsyncFileDialog::new().set_parent(cc);
// The file chooser that opens may not be the desktop's own: the user is told when it does
// not (#10159).
#[cfg(target_os = "linux")]
let chooser_ctx = cc.egui_ctx.clone();
let open_parent = parent.clone();
let program_parent = parent.clone();
let file_parent = parent.clone();
let (open_folder, file_folder, save_folder) = (folder.clone(), folder.clone(), folder.clone());
Services {
pick_open: Some(Box::new(move |first: &[&str]| {
// The chooser that opens may not be the desktop's own (#10159).
#[cfg(target_os = "linux")]
desktop_chooser::note_opening(&chooser_ctx);
// The file type the command reads comes first, so the picker opens on it (#1801).
let mut dialog = open_folder.start(open_parent.clone());
if !first.is_empty() {
@@ -785,6 +802,28 @@ fn graphics() -> Option<serde_json::Value> {
graphics_path().and_then(|p| std::fs::read_to_string(p).ok()).and_then(|s| serde_json::from_str::<serde_json::Value>(&s).ok())
}
fn restore_preferences(app: &mut SolveApp, stored_ui: Option<&str>, saved_graphics: Option<&serde_json::Value>, safe_mode: bool) {
if let Some(p) = stored_ui.filter(|_| !safe_mode) {
app.load_prefs(p);
}
// Even safe mode edits the real graphics.json. Keep its choices in Preferences so an
// anti-aliasing change cannot erase them; safe mode's renderer still uses its own defaults.
restore_graphics_preferences(app, saved_graphics);
}
/// Restore startup choices after loading the separately saved UI preferences.
fn restore_graphics_preferences(app: &mut SolveApp, saved: Option<&serde_json::Value>) {
app.preferences.graphics_adapter = saved.and_then(|v| v.get("adapter").and_then(serde_json::Value::as_str)).unwrap_or_default().to_string();
// graphics.json chooses the window before app.ron is loaded. Reflect that saved choice,
// not the current session's window system (which an environment override may change).
app.preferences.window_system = saved
.and_then(|v| v.get("window_system").and_then(serde_json::Value::as_str))
.map(str::trim)
.filter(|choice| matches!(*choice, "wayland" | "x11"))
.unwrap_or("auto")
.to_string();
}
/// The graphics backend preference (`graphics.json`'s `backend`: `auto`, `dx12`, `vulkan`, `gl`).
fn backend_preference() -> Option<String> {
graphics().and_then(|v| v.get("backend").and_then(serde_json::Value::as_str).map(str::to_string))
@@ -1003,6 +1042,8 @@ fn main() -> eframe::Result {
solidcraft_engine::crash::install(dir, build)
})
.unwrap_or_default();
// The recovery folder also holds the reopen guard marker (#5064); read once, reused below.
let recovery_dir = solidcraft_engine::recovery::default_dir();
// A crash in native code (a graphics driver) says where it happened: the exception or signal
// and the module on Windows and macOS, and on every system the startup step the app was in.
#[cfg(target_os = "windows")]
@@ -1085,6 +1126,10 @@ fn main() -> eframe::Result {
solidcraft_ui_egui::fonts::load_system_fonts_in_background(&cc.egui_ctx);
mark("fonts");
let dialog_folder = DialogFolder::default();
// The file chooser that opens may not be the desktop's own: the session bus is asked
// now, so the answer is there when a file is opened (#10159).
#[cfg(target_os = "linux")]
desktop_chooser::start(&cc.egui_ctx);
let mut app = SolveApp::new(Session::default(), services(cc, &dialog_folder));
app.apply_off_thread = Some(solidcraft_ui_egui::dialogs::APPLY_AT_ONCE);
app.custom_titlebar = custom_titlebar;
@@ -1105,9 +1150,8 @@ fn main() -> eframe::Result {
app.integrated_titlebar = cfg!(target_os = "macos");
app.cpu_meshing = safe_mode;
app.graphics_fallback = fallback_note.clone();
if let Some(p) = cc.storage.and_then(|s| solidcraft_ui_egui::prefs::stored(|k| s.get_string(k))).filter(|_| !safe_mode) {
app.load_prefs(&p);
}
let stored_ui = cc.storage.and_then(|s| solidcraft_ui_egui::prefs::stored(|k| s.get_string(k)));
restore_preferences(&mut app, stored_ui.as_deref(), graphics().as_ref(), safe_mode);
if let Some(path) = &theme_file
&& let Err(e) = solidcraft_ui_egui::prefs::follow_theme_file_for_run(&mut app, path)
{
@@ -1120,10 +1164,6 @@ fn main() -> eframe::Result {
lost_at: None,
reason: String::new(),
});
if !safe_mode {
app.preferences.graphics_adapter =
graphics().and_then(|v| v.get("adapter").and_then(serde_json::Value::as_str).map(str::to_string)).unwrap_or_default();
}
if let Some(rs) = &cc.wgpu_render_state {
app.services.graphics_adapters = rs
.available_adapters
@@ -1196,8 +1236,25 @@ fn main() -> eframe::Result {
};
// No design given: the designs open at the last quit, when the preference asks (#5542).
// Not when driven over the control channel, whose clients expect a fresh design.
let files =
if files.is_empty() && !sample && control_port.is_none() { solidcraft_ui_egui::documents::designs_to_reopen(&app) } else { files };
// A design that stopped the last session while it was being reopened is skipped this
// time, so one bad file cannot brick every launch (#5064).
app.reopen_guard_dir = recovery_dir.clone();
let files = if files.is_empty() && !sample && control_port.is_none() {
let (reopen, skipped) = solidcraft_ui_egui::documents::reopen_plan(&app, recovery_dir.as_deref());
if !skipped.is_empty() {
let names: Vec<String> = skipped
.iter()
.map(|p| std::path::Path::new(p).file_name().map(|n| n.to_string_lossy().into_owned()).unwrap_or_else(|| p.clone()))
.collect();
notes.push(format!(
"The last session stopped while reopening {}. It was not reopened this time; open it yourself if you need it.",
names.join(", ")
));
}
reopen
} else {
files
};
for f in &files {
app.open_path_async(f);
}
@@ -1213,9 +1270,9 @@ fn main() -> eframe::Result {
app.home.show(rev);
}
// Autosave, and offer what crashed sessions left behind.
if let Some(dir) = solidcraft_engine::recovery::default_dir() {
if let Some(dir) = recovery_dir.as_ref() {
let every = if app.autosave_minutes > 0.0 { app.autosave_minutes * 60.0 } else { f64::MAX / 4.0 };
solidcraft_ui_egui::recovery_ui::start(&mut app, &dir, std::time::Duration::from_secs_f64(every.min(1e9)));
solidcraft_ui_egui::recovery_ui::start(&mut app, dir, std::time::Duration::from_secs_f64(every.min(1e9)));
// Safe mode keeps autosaving but offers nothing: the entries wait for a normal start.
if safe_mode {
app.recoverable.clear();
@@ -1403,6 +1460,47 @@ fn prefer_x11(display: bool, wayland_display: bool, desktop: Option<&str>, wayla
display && !(niri && wayland_display)
}
#[cfg(all(unix, not(target_os = "macos")))]
const CHOOSER_NO_SESSION_BUS: &str = "file.open.portal_fallback.no_session_bus";
#[cfg(all(unix, not(target_os = "macos")))]
const CHOOSER_NO_PORTAL_SERVICE: &str = "file.open.portal_fallback.no_portal_service";
#[cfg(all(unix, not(target_os = "macos")))]
const CHOOSER_XWAYLAND: &str = "file.open.portal_fallback.xwayland";
/// Whether the chooser that opens is NOT the desktop's own, and why.
///
/// `rfd` opens the file chooser through the XDG desktop portal; when the portal is not there it
/// falls back to zenity, which opens a window that does not belong to SolidCraft and that the
/// application never mentions. The application has to know that: this decision is what the warning
/// is built on.
///
/// * `dbus_session`: the session bus is there (`DBUS_SESSION_BUS_ADDRESS`).
/// * `portal_service`: the file chooser portal answers on it.
/// * `wayland_display`: a Wayland session runs (`WAYLAND_DISPLAY`).
/// * `x11_display`: the window is an X11 one (`DISPLAY`, or XWayland).
///
/// `None` means the desktop's own chooser is the one that opens and there is nothing to say;
/// `Some` carries the reason to report.
#[cfg(all(unix, not(target_os = "macos")))]
#[cfg_attr(not(target_os = "linux"), allow(dead_code))]
fn desktop_chooser_fallback(dbus_session: bool, portal_service: bool, wayland_display: bool, x11_display: bool) -> Option<&'static str> {
// Without a session bus there is no bus to reach the portal on, so the chooser is not the
// desktop's own whatever else is there.
if !dbus_session {
return Some(CHOOSER_NO_SESSION_BUS);
}
// The bus is there but the portal does not answer on it: `rfd` falls back to zenity.
if !portal_service {
return Some(CHOOSER_NO_PORTAL_SERVICE);
}
// Under XWayland the window is an X11 one while the session is a Wayland one, and the portal
// refuses to parent its chooser to an X11 window: `rfd` falls back to zenity.
if wayland_display && x11_display {
return Some(CHOOSER_XWAYLAND);
}
None
}
#[cfg(all(test, unix, not(target_os = "macos")))]
mod backend_tests {
use super::prefer_x11;
@@ -1438,6 +1536,30 @@ mod backend_tests {
assert!(prefer_x11(true, true, Some("niri"), over(Some("x11"))));
}
/// The file chooser must say when it is NOT the desktop's own: `rfd` falls back to zenity
/// when the XDG portal is not there, and the user gets a foreign window with no explanation.
#[test]
fn the_file_picker_says_when_it_is_not_the_desktops_own_chooser() {
use super::desktop_chooser_fallback;
// The normal case: the portal is there, the desktop's own chooser opens, nothing to say.
assert_eq!(desktop_chooser_fallback(true, true, true, false), None);
// No session bus: the portal can not be reached at all, so `rfd` falls back to zenity.
assert!(
desktop_chooser_fallback(false, true, true, false).is_some(),
"without a D-Bus session bus the file chooser is not the desktop's own: the user has to be told"
);
// The bus is there but the portal service is not: same fallback, same warning.
assert!(
desktop_chooser_fallback(true, false, true, false).is_some(),
"without the desktop portal service the file chooser is not the desktop's own: the user has to be told"
);
// XWayland: the parent window is an X11 one and the portal refuses it, so `rfd` falls back.
assert!(
desktop_chooser_fallback(true, true, true, true).is_some(),
"under XWayland the portal rejects the parent window: the user has to be told"
);
}
#[test]
fn the_desktops_title_bar_only_when_asked_for() {
use super::titlebar_choice;
@@ -1715,6 +1837,88 @@ mod viewport_tests {
#[cfg(test)]
mod msaa_tests {
/// Safe mode uses default UI settings but edits the real graphics.json for the next start.
#[test]
fn safe_mode_preserves_graphics_choices_when_changing_antialiasing() {
use serde_json::{Value, json};
use solidcraft_ui_egui::scenario::Harness;
for choice in ["wayland", "x11"] {
let mut h = Harness::new();
let path = h.dir.join("graphics.json");
let saved = json!({"window_system": choice, "adapter": "Selected GPU", "backend": "vulkan", "msaa": 8});
std::fs::write(&path, saved.to_string()).unwrap();
h.app.services.graphics_path = Some(path.to_string_lossy().into_owned());
let defaults = h.app.preferences.clone();
let stored_ui = json!({"preferences": {"window_system": "auto", "graphics_adapter": "Stale GPU", "msaa": 2,
"display_quality": "fine"}})
.to_string();
super::restore_preferences(&mut h.app, Some(&stored_ui), Some(&saved), true);
assert_eq!(h.app.preferences.window_system, choice);
assert_eq!(h.app.preferences.graphics_adapter, "Selected GPU");
assert_eq!(h.app.preferences.msaa, defaults.msaa, "safe mode ignores saved anti-aliasing");
assert_eq!(h.app.preferences.display_quality, defaults.display_quality, "safe mode ignores saved UI settings");
assert_eq!(serde_json::from_str::<Value>(&std::fs::read_to_string(&path).unwrap()).unwrap(), saved, "startup does not write settings");
assert_eq!(h.call("ui.prefs", json!({"open": "Graphics"}))["ok"], true);
assert_eq!(h.call("ui.prefs", json!({"msaa": 1}))["ok"], true);
assert_eq!(h.call("ui.prefs", json!({"button": "ok"}))["ok"], true);
let after: Value = serde_json::from_str(&std::fs::read_to_string(&path).unwrap()).unwrap();
assert_eq!(after, json!({"window_system": choice, "adapter": "Selected GPU", "backend": "vulkan", "msaa": 1}));
let mut normal = Harness::new();
super::restore_preferences(&mut normal.app, Some(&stored_ui), Some(&after), false);
assert_eq!(normal.app.preferences.window_system, choice);
assert_eq!(normal.app.preferences.graphics_adapter, "Selected GPU");
assert_eq!(normal.app.preferences.display_quality, "fine", "normal startup still restores UI settings");
}
}
#[test]
fn missing_or_invalid_startup_window_system_uses_automatic() {
use serde_json::json;
for saved in [None, Some(json!(null)), Some(json!({})), Some(json!({"window_system": 7})), Some(json!({"window_system": "unknown"}))] {
let mut app = super::SolveApp::new(super::Session::default(), super::Services::default());
app.preferences.window_system = "wayland".into();
super::restore_graphics_preferences(&mut app, saved.as_ref());
assert_eq!(app.preferences.window_system, "auto", "{saved:?}");
}
}
/// A manual graphics.json choice wins over stale app.ron preferences and survives an
/// unrelated graphics edit (#10116), including on a fresh launch with no saved UI state.
#[test]
fn startup_window_system_survives_changing_antialiasing() {
use serde_json::{Value, json};
use solidcraft_ui_egui::scenario::Harness;
for choice in ["wayland", "x11", "auto", " wayland ", " x11 "] {
for stale in [None, Some("auto"), Some("wayland"), Some("x11")] {
let mut h = Harness::new();
if let Some(stale) = stale {
h.app.load_prefs(&json!({"preferences": {"window_system": stale}}).to_string());
}
let path = h.dir.join("graphics.json");
std::fs::write(&path, json!({"window_system": choice, "backend": "vulkan", "adapter": "Selected GPU", "msaa": 4}).to_string())
.unwrap();
h.app.services.graphics_path = Some(path.to_string_lossy().into_owned());
let saved: Value = serde_json::from_str(&std::fs::read_to_string(&path).unwrap()).unwrap();
super::restore_graphics_preferences(&mut h.app, Some(&saved));
let result = h.call("ui.prefs", json!({"msaa": 2}));
assert_eq!(result["ok"], true, "{result}");
let after: Value = serde_json::from_str(&std::fs::read_to_string(&path).unwrap()).unwrap();
assert_eq!(after["window_system"], choice.trim(), "saved choice {choice:?}, stale UI choice {stale:?}");
assert_eq!(result["result"]["window_system"], choice.trim());
assert_eq!(after["msaa"], 2);
assert_eq!(after["backend"], "vulkan");
assert_eq!(after["adapter"], "Selected GPU");
}
}
}
#[test]
fn first_launch_adopts_the_previous_release_settings() {
let d = std::env::temp_dir().join(format!("solidcraft-adopt-settings-{}", std::process::id()));
+23 -17
View File
@@ -40,10 +40,10 @@ fn connect_to(path: &Path) -> Option<UnixStream> {
/// The axes of one record, or `None` for a record that is not motion.
///
/// The daemon's frame is right-handed with x to the right, y away from the user and z up; ours is
/// the device's raw one, x right, y towards the user and z down, so y and z (and the rotations
/// about them) change sign. With nothing set in `spnavcfg` the two give the same motion; what
/// the user swaps or inverts there then carries over.
/// The daemon reports x to the right, y up and z away from the user; our raw HID frame is x
/// right, y towards the user and z down. Swap and negate y/z and ry/rz to match that frame.
/// With nothing set in `spnavcfg` the two readers give the same motion; what the user swaps
/// or inverts there then carries over.
fn decode(record: &[u8; RECORD_BYTES]) -> Option<Axes> {
let mut data = [0_i32; 8];
for (slot, bytes) in data.iter_mut().zip(record.as_chunks::<4>().0) {
@@ -51,8 +51,8 @@ fn decode(record: &[u8; RECORD_BYTES]) -> Option<Axes> {
}
let [kind, x, y, z, rx, ry, rz, _period] = data;
(kind == 0).then(|| Axes {
translation: [unit(x), unit(-i64::from(y)), unit(-i64::from(z))],
rotation: [unit(rx), unit(-i64::from(ry)), unit(-i64::from(rz))],
translation: [unit(x), unit(-i64::from(z)), unit(-i64::from(y))],
rotation: [unit(rx), unit(-i64::from(rz)), unit(-i64::from(ry))],
})
}
@@ -149,22 +149,28 @@ mod tests {
#[test]
fn motion_records_become_axes_in_the_devices_raw_frame() {
// Full deflection of the daemon's x, y and z is the device's full scale, and y and z flip.
assert_eq!(decode(&record([0, 500, 500, 500, 0, 0, 0, 8])), Some(Axes { translation: [350, -350, -350], rotation: [0; 3] }));
// Rotations follow translations: right-handed in both frames, so ry and rz flip as y and z do.
assert_eq!(decode(&record([0, 0, 0, 0, 250, 250, 250, 8])), Some(Axes { translation: [0; 3], rotation: [175, -175, -175] }));
// Each daemon axis reaches only its matching raw HID axis, in both directions.
for (daemon_axis, raw_axis, sign) in [(0, 0, 1), (1, 2, -1), (2, 1, -1), (3, 3, 1), (4, 5, -1), (5, 4, -1)] {
for value in [-500, 500] {
let mut data = [0; 8];
data[daemon_axis + 1] = value;
let mut expected = [0; 6];
expected[raw_axis] = sign * (value * 350 / 500) as i16;
assert_eq!(decode(&record(data)), Some(Axes::from(expected)), "daemon axis {daemon_axis}, value {value}");
}
}
// Rest is a motion record of zeros.
assert_eq!(decode(&record([0, 0, 0, 0, 0, 0, 0, 1200])), Some(Axes::default()));
}
#[test]
fn what_spnavcfg_swaps_reaches_the_view() {
// The daemon sends a push away as y. With Y and Z swapped in spnavcfg it sends the same push
// as z, and the view gets a lift (raw z) instead of the zoom (raw y).
let before = decode(&record([0, 0, 500, 0, 0, 0, 0, 8])).map(|axes| axes.translation);
let after = decode(&record([0, 0, 0, 500, 0, 0, 0, 8])).map(|axes| axes.translation);
assert_eq!(before, Some([0, -350, 0]));
assert_eq!(after, Some([0, 0, -350]));
// A push away is daemon z (raw y); swapping Y/Z makes it a lift (raw z).
// The same swap changes a sideways tilt (daemon rz, raw ry) into a twist (raw rz).
let before = decode(&record([0, 0, 0, 500, 0, 0, 250, 8]));
let after = decode(&record([0, 0, 500, 0, 0, 250, 0, 8]));
assert_eq!(before, Some(Axes { translation: [0, -350, 0], rotation: [0, -175, 0] }));
assert_eq!(after, Some(Axes { translation: [0, 0, -350], rotation: [0, 0, -175] }));
}
#[test]
@@ -179,7 +185,7 @@ mod tests {
// Nothing overflows on the extremes of `i32`.
let extreme = decode(&record([0, i32::MIN, i32::MIN, i32::MAX, i32::MAX, i32::MIN, i32::MIN, -1]));
assert_eq!(extreme.map(|axes| axes.translation[0]), Some(i16::MIN));
assert_eq!(extreme.map(|axes| axes.translation[1]), Some(i16::MAX));
assert_eq!(extreme.map(|axes| axes.translation), Some([i16::MIN, i16::MIN, i16::MAX]));
assert_eq!(extreme.map(|axes| axes.rotation[0]), Some(i16::MAX));
}
+10 -5
View File
@@ -311,13 +311,18 @@ pub const SYSTEM_VERSION_COMPAT_ENV: &str = "SYSTEM_VERSION_COMPAT";
#[cfg(target_os = "macos")]
pub const OCLP_NVRAM_KEY: &str = "4D1FDA02-38C7-4A6A-9CC6-4BCCA8B30102:OCLP-Version";
/// Where macOS keeps its `nvram` tool.
#[cfg(target_os = "macos")]
const NVRAM_TOOL: &str = "/usr/sbin/nvram";
/// `true` if the OCLP NVRAM key is present on this system.
///
/// Runs `nvram <key>` and treats exit 0 as present. Any error (missing binary,
/// permission denied, key absent) returns `false` — this is a best-effort probe.
/// Runs `/usr/sbin/nvram <key>` and treats exit 0 as present. The full path keeps a `PATH` that
/// lacks `/usr/sbin`, or lists a look-alike first, from changing the answer. Any error (missing
/// binary, permission denied, key absent) returns `false`: this is a best-effort probe.
#[cfg(target_os = "macos")]
pub fn oclp_nvram_present() -> bool {
std::process::Command::new("nvram").arg(OCLP_NVRAM_KEY).output().map(|o| o.status.success()).unwrap_or(false)
std::process::Command::new(NVRAM_TOOL).arg(OCLP_NVRAM_KEY).output().map(|o| o.status.success()).unwrap_or(false)
}
/// Whether to re-launch with `SYSTEM_VERSION_COMPAT=1`: it is not set yet and OCLP shows through
@@ -334,8 +339,8 @@ fn compat_wanted(compat_set: bool, nvram: impl FnOnce() -> bool) -> bool {
///
/// `SYSTEM_VERSION_COMPAT=1` must be present **before** the process starts: setting it
/// with `std::env::set_var` inside `main` is too late because the macOS frameworks have
/// already queried the OS version by then. Re-launching is the only safe approach that
/// works without `unsafe` (a true `execve` replacement would require it).
/// already queried the OS version by then, so the program starts itself again with the variable
/// set and waits for that child.
///
/// Returns `Ok(false)` immediately when:
/// - `SYSTEM_VERSION_COMPAT` is already set (re-launched process, no loop), or
+3 -2
View File
@@ -17,12 +17,13 @@ assets/
ui/design/tools/ # authoring bases, checks and included review app
ui/source/ # Rust viewport glyphs, markers and navigation
branding/web/source/og.png # shared social preview card
branding/logo/ # print/web logo files; guide in branding/README.md
styles/ui/palettes.rs # light/dark defaults
styles/web/loading.css # web loading/canvas styles
appearances/library.rs # built-in visual presets
materials/library.rs # physical defaults and colours
shaders/viewport.wgsl # original viewport shader
fonts/<family>/ # unmodified fonts and OFL licences
fonts/{liberation-sans,biz-udp-gothic}/ # 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 +80,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 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 |
| 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 |
| Docs and link previews | `xtask book` copies the shared card to the existing `/solidcraft/og.png` public path |
## Code and provenance
+73
View File
@@ -0,0 +1,73 @@
# SolidCraft brand kit
Logo files for stickers, shirts, the web and social posts. Everything in `logo/` is drawn
from the app icon (`assets/icons/app/macos/source/SolidCraft.icon/Assets/03-solid-blue-cutout.svg`),
which is also the last frame of the loading animation (`assets/styles/web/loading-logo.svg`).
The SVGs are plain paths: no scripts, fonts, animation or external links, so any printer can use them.
## Files
| File | Use it for |
|---|---|
| `logo/solidcraft-mark.svg` | The mark in full colour. First choice for stickers, print and the web. |
| `logo/solidcraft-mark-mono-black.svg` | One-colour print on light material: screen-printed shirts, stamps, engraving, vinyl. |
| `logo/solidcraft-mark-mono-white.svg` | One-colour print on dark material, such as a white print on a dark shirt. |
| `logo/solidcraft-mark-512.png`, `-1024.png`, `-2048.png` | Transparent square PNGs for places that don't take SVG: avatars, social posts, slides, print shops that ask for a bitmap (send the 2048). |
| `logo/solidcraft-wordmark.svg` | The name on its own, outlined to paths. |
| `logo/solidcraft-lockup-horizontal.svg` | Mark and name side by side: headers, banners, shirt fronts, wide stickers. |
| `web/source/og.png` | The existing link preview card for the docs site. |
The gaps between the faces are part of the logo. In the colour and one-colour files they are
transparent, so the material shows through. For a die-cut sticker, cut around the outer shape
and let the backing colour (white is best) fill the gaps.
## Colours
The mark's five faces each use a two-stop gradient from the icon source:
| Face | From | To |
|---|---|---|
| Top | `#51adff` | `#2b83f1` |
| Left | `#258df5` | `#1461da` |
| Right | `#1b66cf` | `#124596` |
| Lower step (top) | `#459fff` | `#2379e7` |
| Upper step (inner side) | `#1d63c6` | `#15499e` |
Other colours, all already used in the project:
| Colour | Hex | Where it comes from |
|---|---|---|
| Wordmark blue | `#15499e` | loading logo `--word` |
| Interface accent | `#2678da` | light theme `accent` token (`assets/styles/ui/palettes.rs`) |
| Light background | `#e8ebf0` | web loading screen (`assets/styles/web/loading.css`) |
| Dark background | `#1f232b` | dark theme `file_background` token |
| Black / white | `#000000` / `#ffffff` | one-colour versions only |
If a printer can't do gradients, use the one-colour files, or fill every face with `#2678da`.
## Clear space and size
- Keep empty space around the mark of at least one fifth of its height on every side. Treat
the lockup the same way, measured from the mark.
- Smallest mark: 16 px tall on screen, 12 mm tall in print. Below that the gaps fill in.
- Smallest lockup: 120 px wide on screen, 40 mm wide in print.
## Don'ts
- Don't recolour it outside the colours above.
- Don't stretch, squash, rotate, skew or crop it. Scale it evenly.
- Don't add shadows, outlines, glows, bevels or other effects.
- Don't fill or close the gaps between the faces, or move the faces apart.
- Don't rearrange the lockup, retype the name in another font, or put the mark on a busy
background where it loses contrast.
## Name and logo
The SolidCraft code and these files are licensed MIT OR Apache-2.0 (see the [README](../../README.md#licence)),
so you may copy and change them. The name SolidCraft and this logo identify the SolidCraft
project. Use them to refer to the project, for example in an article, a talk, or a sticker for a
meetup. If you publish a fork or a modified build, give it its own name and logo so people
don't mistake it for SolidCraft.
The wordmark is set in IBM Plex Sans Bold (SIL Open Font License 1.1, the interface font in
`assets/fonts/ibm-plex-sans/`) and outlined, so you don't need the font to print it.
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="116.406 53.364 2748.71 904.436" width="2749" height="904" role="img"><title>SolidCraft</title><defs><linearGradient id="sc1" x1="619.14" y1="179.734" x2="694.687" y2="396.761" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#1d63c6"/><stop offset="1" stop-color="#15499e"/></linearGradient><linearGradient id="sc2" x1="619.14" y1="413.244" x2="790.839" y2="605.547" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#459fff"/><stop offset="1" stop-color="#2379e7"/></linearGradient><linearGradient id="sc3" x1="632.876" y1="399.508" x2="783.971" y2="968.175" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#1b66cf"/><stop offset="1" stop-color="#124596"/></linearGradient><linearGradient id="sc4" x1="207.063" y1="303.357" x2="426.837" y2="968.175" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#258df5"/><stop offset="1" stop-color="#1461da"/></linearGradient><linearGradient id="sc5" x1="385.63" y1="53.364" x2="522.989" y2="510.769" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#51adff"/><stop offset="1" stop-color="#2b83f1"/></linearGradient></defs><g><path d="M547.883 282.66 L709.797 188.211 L709.797 375.985 Z" fill="url(#sc1)"/><path d="M727.797 593.626 L727.797 407.136 L889.571 500.381 Z" fill="url(#sc2)"/><path d="M530 521.17 L691.797 427.912 L691.797 655.928 L907.594 531.545 L907.594 740.159 L530 957.8 Z" fill="url(#sc3)"/><path d="M116.406 510.769 L116.406 303.529 L494 521.17 L494 957.8 L314.203 854.167 L314.203 624.777 Z" fill="url(#sc4)"/><path d="M134.374 272.334 L512 53.364 L691.828 157.015 L476.117 282.846 L673.752 396.761 L512 489.993 Z" fill="url(#sc5)"/></g><path fill="#15499e" transform="translate(1092.78 632.20) scale(0.36281)" d="M303 12Q212 12 147.5 -20Q83 -52 38 -101L138 -202Q173 -162 217.5 -142Q262 -122 313 -122Q369 -122 396 -145Q423 -168 423 -206Q423 -225 416.5 -240.5Q410 -256 392.5 -266.5Q375 -277 342 -281L273 -290Q200 -300 152.5 -326.5Q105 -353 82 -396.5Q59 -440 59 -498Q59 -561 90.5 -608.5Q122 -656 180.5 -683Q239 -710 321 -710Q400 -710 460 -684.5Q520 -659 563 -612L462 -510Q437 -539 401.5 -557.5Q366 -576 312 -576Q261 -576 236 -558Q211 -540 211 -508Q211 -484 219.5 -469Q228 -454 246.5 -446Q265 -438 294 -433L363 -422Q435 -411 482 -385Q529 -359 552 -317Q575 -275 575 -215Q575 -148 543.5 -97Q512 -46 451 -17Q390 12 303 12ZM926 12Q849 12 794 -21.5Q739 -55 709.5 -117Q680 -179 680 -263Q680 -348 709.5 -409Q739 -470 794 -503.5Q849 -537 926 -537Q1003 -537 1058 -503.5Q1113 -470 1142.5 -409Q1172 -348 1172 -263Q1172 -179 1142.5 -117Q1113 -55 1058 -21.5Q1003 12 926 12ZM926 -105Q970 -105 994 -132.5Q1018 -160 1018 -209V-316Q1018 -366 994 -393Q970 -420 926 -420Q883 -420 858.5 -393Q834 -366 834 -316V-209Q834 -160 858.5 -132.5Q883 -105 926 -105ZM1511 0H1444Q1368 0 1332.5 -37Q1297 -74 1297 -144V-740H1445V-115H1511ZM1694 -581Q1649 -581 1628.5 -602Q1608 -623 1608 -654V-676Q1608 -708 1628.5 -728.5Q1649 -749 1694 -749Q1739 -749 1759.5 -728.5Q1780 -708 1780 -676V-654Q1780 -623 1759.5 -602Q1739 -581 1694 -581ZM1620 0V-525H1768V0ZM2248 0V-88H2243Q2232 -45 2192.5 -16.5Q2153 12 2102 12Q2036 12 1991 -19.5Q1946 -51 1923 -112.5Q1900 -174 1900 -263Q1900 -352 1923 -413Q1946 -474 1991 -505.5Q2036 -537 2102 -537Q2153 -537 2192.5 -509Q2232 -481 2243 -437H2248V-740H2396V0ZM2151 -108Q2179 -108 2201 -117Q2223 -126 2235.5 -143.5Q2248 -161 2248 -185V-340Q2248 -364 2235.5 -381.5Q2223 -399 2201 -408Q2179 -417 2151 -417Q2109 -417 2081.5 -387.5Q2054 -358 2054 -308V-217Q2054 -168 2081.5 -138Q2109 -108 2151 -108ZM2844 12Q2750 12 2680.5 -28Q2611 -68 2573 -147Q2535 -226 2535 -344Q2535 -462 2573 -543.5Q2611 -625 2680.5 -667.5Q2750 -710 2844 -710Q2940 -710 3003.5 -670Q3067 -630 3105 -546L2974 -478Q2961 -522 2930.5 -548.5Q2900 -575 2844 -575Q2777 -575 2736.5 -531.5Q2696 -488 2696 -405V-293Q2696 -211 2736.5 -167Q2777 -123 2844 -123Q2900 -123 2933.5 -153.5Q2967 -184 2983 -227L3107 -155Q3068 -76 3004 -32Q2940 12 2844 12ZM3373 0H3225V-525H3373V-411H3378Q3384 -440 3400 -466Q3416 -492 3443.5 -508.5Q3471 -525 3512 -525H3538V-387H3501Q3458 -387 3429.5 -379.5Q3401 -372 3387 -355Q3373 -338 3373 -307ZM4105 0H4023Q3996 0 3972.5 -13.5Q3949 -27 3935 -53.5Q3921 -80 3921 -117V-130L3953 -92H3917Q3905 -41 3864 -14.5Q3823 12 3763 12Q3684 12 3642 -30.5Q3600 -73 3600 -141Q3600 -197 3627.5 -233Q3655 -269 3705.5 -287Q3756 -305 3825 -305H3908V-338Q3908 -376 3888 -398.5Q3868 -421 3821 -421Q3777 -421 3751 -402Q3725 -383 3708 -359L3620 -437Q3652 -484 3701 -510.5Q3750 -537 3832 -537Q3943 -537 3999.5 -487.5Q4056 -438 4056 -345V-115H4105ZM3908 -221H3836Q3793 -221 3771 -206Q3749 -191 3749 -162V-147Q3749 -119 3767 -105Q3785 -91 3818 -91Q3843 -91 3863 -98Q3883 -105 3895.5 -120Q3908 -135 3908 -159ZM4250 0V-410H4176V-525H4250V-587Q4250 -663 4290.5 -701.5Q4331 -740 4407 -740H4503V-625H4398V-525H4503V-410H4398V0ZM4877 0H4789Q4713 0 4673 -39Q4633 -78 4633 -153V-410H4559V-525H4596Q4626 -525 4637 -539.5Q4648 -554 4648 -581V-667H4781V-525H4885V-410H4781V-115H4877Z"/></svg>

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="116.406 53.364 791.188 904.436" width="791" height="904" role="img"><title>SolidCraft</title><g fill="#000000"><path d="M547.883 282.66 L709.797 188.211 L709.797 375.985 Z"/><path d="M727.797 593.626 L727.797 407.136 L889.571 500.381 Z"/><path d="M530 521.17 L691.797 427.912 L691.797 655.928 L907.594 531.545 L907.594 740.159 L530 957.8 Z"/><path d="M116.406 510.769 L116.406 303.529 L494 521.17 L494 957.8 L314.203 854.167 L314.203 624.777 Z"/><path d="M134.374 272.334 L512 53.364 L691.828 157.015 L476.117 282.846 L673.752 396.761 L512 489.993 Z"/></g></svg>

After

Width:  |  Height:  |  Size: 612 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="116.406 53.364 791.188 904.436" width="791" height="904" role="img"><title>SolidCraft</title><g fill="#ffffff"><path d="M547.883 282.66 L709.797 188.211 L709.797 375.985 Z"/><path d="M727.797 593.626 L727.797 407.136 L889.571 500.381 Z"/><path d="M530 521.17 L691.797 427.912 L691.797 655.928 L907.594 531.545 L907.594 740.159 L530 957.8 Z"/><path d="M116.406 510.769 L116.406 303.529 L494 521.17 L494 957.8 L314.203 854.167 L314.203 624.777 Z"/><path d="M134.374 272.334 L512 53.364 L691.828 157.015 L476.117 282.846 L673.752 396.761 L512 489.993 Z"/></g></svg>

After

Width:  |  Height:  |  Size: 612 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="116.406 53.364 791.188 904.436" width="791" height="904" role="img"><title>SolidCraft</title><defs><linearGradient id="sc1" x1="619.14" y1="179.734" x2="694.687" y2="396.761" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#1d63c6"/><stop offset="1" stop-color="#15499e"/></linearGradient><linearGradient id="sc2" x1="619.14" y1="413.244" x2="790.839" y2="605.547" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#459fff"/><stop offset="1" stop-color="#2379e7"/></linearGradient><linearGradient id="sc3" x1="632.876" y1="399.508" x2="783.971" y2="968.175" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#1b66cf"/><stop offset="1" stop-color="#124596"/></linearGradient><linearGradient id="sc4" x1="207.063" y1="303.357" x2="426.837" y2="968.175" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#258df5"/><stop offset="1" stop-color="#1461da"/></linearGradient><linearGradient id="sc5" x1="385.63" y1="53.364" x2="522.989" y2="510.769" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#51adff"/><stop offset="1" stop-color="#2b83f1"/></linearGradient></defs><g><path d="M547.883 282.66 L709.797 188.211 L709.797 375.985 Z" fill="url(#sc1)"/><path d="M727.797 593.626 L727.797 407.136 L889.571 500.381 Z" fill="url(#sc2)"/><path d="M530 521.17 L691.797 427.912 L691.797 655.928 L907.594 531.545 L907.594 740.159 L530 957.8 Z" fill="url(#sc3)"/><path d="M116.406 510.769 L116.406 303.529 L494 521.17 L494 957.8 L314.203 854.167 L314.203 624.777 Z" fill="url(#sc4)"/><path d="M134.374 272.334 L512 53.364 L691.828 157.015 L476.117 282.846 L673.752 396.761 L512 489.993 Z" fill="url(#sc5)"/></g></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="38.0 -749.0 4847.0 761.0" width="4847" height="761" role="img"><title>SolidCraft</title><path fill="#15499e" d="M303 12Q212 12 147.5 -20Q83 -52 38 -101L138 -202Q173 -162 217.5 -142Q262 -122 313 -122Q369 -122 396 -145Q423 -168 423 -206Q423 -225 416.5 -240.5Q410 -256 392.5 -266.5Q375 -277 342 -281L273 -290Q200 -300 152.5 -326.5Q105 -353 82 -396.5Q59 -440 59 -498Q59 -561 90.5 -608.5Q122 -656 180.5 -683Q239 -710 321 -710Q400 -710 460 -684.5Q520 -659 563 -612L462 -510Q437 -539 401.5 -557.5Q366 -576 312 -576Q261 -576 236 -558Q211 -540 211 -508Q211 -484 219.5 -469Q228 -454 246.5 -446Q265 -438 294 -433L363 -422Q435 -411 482 -385Q529 -359 552 -317Q575 -275 575 -215Q575 -148 543.5 -97Q512 -46 451 -17Q390 12 303 12ZM926 12Q849 12 794 -21.5Q739 -55 709.5 -117Q680 -179 680 -263Q680 -348 709.5 -409Q739 -470 794 -503.5Q849 -537 926 -537Q1003 -537 1058 -503.5Q1113 -470 1142.5 -409Q1172 -348 1172 -263Q1172 -179 1142.5 -117Q1113 -55 1058 -21.5Q1003 12 926 12ZM926 -105Q970 -105 994 -132.5Q1018 -160 1018 -209V-316Q1018 -366 994 -393Q970 -420 926 -420Q883 -420 858.5 -393Q834 -366 834 -316V-209Q834 -160 858.5 -132.5Q883 -105 926 -105ZM1511 0H1444Q1368 0 1332.5 -37Q1297 -74 1297 -144V-740H1445V-115H1511ZM1694 -581Q1649 -581 1628.5 -602Q1608 -623 1608 -654V-676Q1608 -708 1628.5 -728.5Q1649 -749 1694 -749Q1739 -749 1759.5 -728.5Q1780 -708 1780 -676V-654Q1780 -623 1759.5 -602Q1739 -581 1694 -581ZM1620 0V-525H1768V0ZM2248 0V-88H2243Q2232 -45 2192.5 -16.5Q2153 12 2102 12Q2036 12 1991 -19.5Q1946 -51 1923 -112.5Q1900 -174 1900 -263Q1900 -352 1923 -413Q1946 -474 1991 -505.5Q2036 -537 2102 -537Q2153 -537 2192.5 -509Q2232 -481 2243 -437H2248V-740H2396V0ZM2151 -108Q2179 -108 2201 -117Q2223 -126 2235.5 -143.5Q2248 -161 2248 -185V-340Q2248 -364 2235.5 -381.5Q2223 -399 2201 -408Q2179 -417 2151 -417Q2109 -417 2081.5 -387.5Q2054 -358 2054 -308V-217Q2054 -168 2081.5 -138Q2109 -108 2151 -108ZM2844 12Q2750 12 2680.5 -28Q2611 -68 2573 -147Q2535 -226 2535 -344Q2535 -462 2573 -543.5Q2611 -625 2680.5 -667.5Q2750 -710 2844 -710Q2940 -710 3003.5 -670Q3067 -630 3105 -546L2974 -478Q2961 -522 2930.5 -548.5Q2900 -575 2844 -575Q2777 -575 2736.5 -531.5Q2696 -488 2696 -405V-293Q2696 -211 2736.5 -167Q2777 -123 2844 -123Q2900 -123 2933.5 -153.5Q2967 -184 2983 -227L3107 -155Q3068 -76 3004 -32Q2940 12 2844 12ZM3373 0H3225V-525H3373V-411H3378Q3384 -440 3400 -466Q3416 -492 3443.5 -508.5Q3471 -525 3512 -525H3538V-387H3501Q3458 -387 3429.5 -379.5Q3401 -372 3387 -355Q3373 -338 3373 -307ZM4105 0H4023Q3996 0 3972.5 -13.5Q3949 -27 3935 -53.5Q3921 -80 3921 -117V-130L3953 -92H3917Q3905 -41 3864 -14.5Q3823 12 3763 12Q3684 12 3642 -30.5Q3600 -73 3600 -141Q3600 -197 3627.5 -233Q3655 -269 3705.5 -287Q3756 -305 3825 -305H3908V-338Q3908 -376 3888 -398.5Q3868 -421 3821 -421Q3777 -421 3751 -402Q3725 -383 3708 -359L3620 -437Q3652 -484 3701 -510.5Q3750 -537 3832 -537Q3943 -537 3999.5 -487.5Q4056 -438 4056 -345V-115H4105ZM3908 -221H3836Q3793 -221 3771 -206Q3749 -191 3749 -162V-147Q3749 -119 3767 -105Q3785 -91 3818 -91Q3843 -91 3863 -98Q3883 -105 3895.5 -120Q3908 -135 3908 -159ZM4250 0V-410H4176V-525H4250V-587Q4250 -663 4290.5 -701.5Q4331 -740 4407 -740H4503V-625H4398V-525H4503V-410H4398V0ZM4877 0H4789Q4713 0 4673 -39Q4633 -78 4633 -153V-410H4559V-525H4596Q4626 -525 4637 -539.5Q4648 -554 4648 -581V-667H4781V-525H4885V-410H4781V-115H4877Z"/></svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

-93
View File
@@ -1,93 +0,0 @@
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
@@ -1,92 +0,0 @@
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
@@ -1,92 +0,0 @@
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
@@ -1,92 +0,0 @@
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.
+1 -2
View File
@@ -60,7 +60,6 @@ circle_ttt false
close false
coil false
colored_components false
colored_components_off false
combine false
compare false
component false
@@ -161,6 +160,7 @@ min_radius false
mirror false
motion_link false
move false
nav_settings false
new false
normal false
offset false
@@ -178,7 +178,6 @@ pattern_rect false
perforate false
perspective false
pipe false
placeholder true
plane false
plane_along_path false
plane_angle false
@@ -1,21 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Disable component colors</title>
<path d="M9.235 17.706L15.059 20.882L15.059 48.941L9.235 44.926Z" fill="#328bd3"/>
<path d="M35.176 9.235L15.059 20.882L15.059 48.941L36.765 35.139Z" fill="#236ead"/>
<path d="M29.046 5L35.176 8.176L15.196 19.718L9.066 16.541Z" fill="#b9ddf8"/>
<path d="M29.046 5.529L35.176 8.706L35.176 36.235L15.059 49.471L9.235 45.765L9.066 17.071Z" fill="none" stroke="#34465b" stroke-width="2.55" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.066 17.6L15.059 20.882L34.647 9.235" fill="none" stroke="#34465b" stroke-width="2.55" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M15.059 20.882L15.059 47.067" fill="none" stroke="#34465b" stroke-width="2.55" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M32.222 18.595L44.949 25.944L44.949 51.122L32.222 43.774Z" fill="#328bd3"/>
<path d="M54.934 20.173L44.949 25.944L44.949 51.122L54.934 45.352Z" fill="#236ead"/>
<path d="M42.059 12.412L54.934 20.173L44.949 25.944L32 17.706Z" fill="#b9ddf8"/>
<path d="M41.159 11.766L54.934 20.173L54.934 45.352L44.949 51.122L31.164 42.715L31.164 17.536Z" fill="none" stroke="#34465b" stroke-width="2.55" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M32.222 18.595L44.949 25.944L54.934 20.173" fill="none" stroke="#34465b" stroke-width="2.55" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M44.949 25.944L44.949 51.122" fill="none" stroke="#34465b" stroke-width="2.55" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M18.299 39.062L31.015 46.411L31.015 59L18.299 51.652Z" fill="#328bd3"/>
<path d="M41.921 40.121L31.015 46.411L31.015 59L41.921 52.711Z" fill="#236ead"/>
<path d="M29.194 32.773L41.921 40.121L31.015 46.411L18.299 39.062Z" fill="#b9ddf8"/>
<path d="M29.194 32.773L41.921 40.121L41.921 52.711L31.015 59L18.299 51.652L18.299 39.062Z" fill="none" stroke="#34465b" stroke-width="2.55" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M18.299 39.062L31.015 46.411L41.921 40.121" fill="none" stroke="#34465b" stroke-width="2.55" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M31.015 46.411L31.015 59" fill="none" stroke="#34465b" stroke-width="2.55" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 2.3 KiB

@@ -1,7 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 962 B

@@ -1,7 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 846 B

@@ -1,7 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 651 B

@@ -1,7 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 851 B

@@ -1,5 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 420 B

@@ -1,7 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 730 B

@@ -1,7 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 631 B

@@ -1,9 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 1000 B

@@ -1,8 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 598 B

@@ -1,8 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 741 B

@@ -1,9 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 794 B

@@ -1,6 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 622 B

@@ -1,6 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 516 B

@@ -1,9 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 958 B

@@ -1,8 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 1.1 KiB

@@ -1,9 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 1.0 KiB

@@ -1,8 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 933 B

@@ -1,7 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 745 B

@@ -1,8 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 752 B

@@ -1,5 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 450 B

@@ -1,8 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 1.1 KiB

@@ -1,8 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 900 B

-11
View File
@@ -79,17 +79,6 @@ pub(crate) fn snap_hint(painter: &egui::Painter, c: Pos2, kind: SnapKind, col: C
painter.circle_filled(c, 1.3, col);
}
/// A placed hole's drag handle: a ring round the hole's centre with a cross through it, filled
/// while it is hovered or dragged.
pub(crate) fn hole_handle(painter: &egui::Painter, c: Pos2, col: Color32, hot: bool) {
let ic = Tokens::get().icons;
let r = 6.0;
painter.circle_filled(c, r + 3.5, ic.marker_halo);
painter.circle(c, r, if hot { col } else { col.gamma_multiply(0.35) }, Stroke::new(1.6, col));
let ink = Stroke::new(1.3, if hot { ic.highlight } else { col });
painter.line_segment([c - vec2(r + 3.0, 0.0), c + vec2(r + 3.0, 0.0)], ink);
painter.line_segment([c - vec2(0.0, r + 3.0), c + vec2(0.0, r + 3.0)], ink);
}
pub(crate) fn glyph_icon(name: &str) -> Option<&'static str> {
Some(match name {
"Coincident" => "c_coincident",
+89 -19
View File
@@ -85,6 +85,7 @@
"Bottom Right": "أسفل اليمين",
"Boundary": "الحد",
"Bounding box": "الصندوق المحيط",
"Bridge": "جسر",
"CHECK": "تحقق",
"COMPONENTS": "المكوّنات",
"CONFIGURATIONS": "التكوينات",
@@ -963,8 +964,7 @@
"cmd.inspect_analysis": "تحليل العرض",
"cmd.inspect_center_of_mass": "مركز الكتلة",
"cmd.inspect_clearance": "الخلوص",
"cmd.inspect_colored_components": "تفعيل ألوان المكوّنات",
"cmd.inspect_colored_components_disable": "تعطيل ألوان المكوّنات",
"cmd.inspect_colored_components": "ألوان المكوّنات",
"cmd.inspect_compare": "مقارنة الأجسام",
"cmd.inspect_compare_picture": "مقارنة بصورة مرجعية",
"cmd.inspect_continuity": "تحليل الاستمرارية",
@@ -1296,6 +1296,7 @@
"cmd.timeline_stop": "إيقاف السجل",
"cmd.timeline_suppress": "تعطيل الميزة",
"cmd.ui.assistant": "إظهار المساعد أو إخفاؤه",
"cmd.view.navigator": "متصفح العرض المخصص",
"cmd.view.render_browser": "متصفح التصيير",
"cmd.view.render_dock": "نقل لوحة التصيير",
"cmd.view.render_focus": "عرض تركيز التصيير",
@@ -1394,6 +1395,7 @@
"dialog.computing": "جارٍ الحساب… {seconds} ث",
"dialog.could_not_be_checked": "تعذّر فحصه",
"dialog.custom_axis": "↖ محور مخصص",
"dialog.cylinder_face_tip": "وجه النموذج أو رأسه الذي تقوم عليه الأسطوانة: انقر هنا ثم انقر عليه في العرض. إذا تُرك فارغًا، تحدد X وY وZ موضعها.",
"dialog.dimension_insert_hint": "انقر على بُعد قائد آخر لإدراج اسمه",
"dialog.dimension_name": "البُعد: {name}",
"dialog.dimension_step_down": "إنقاص بمقدار {step}",
@@ -1402,6 +1404,8 @@
"dialog.edit_title": "تعديل {title}",
"dialog.no_shown_body": "لا يوجد جسم ظاهر لتغييره: أظهر جسمًا، أو اختر «جسم جديد»",
"dialog.pattern_sketch_not_object": "اختر أجسامًا أو ميزات: الرسم ليس أيًّا منهما. كرّر منحنياته بأداة النمط أو المرآة داخل الرسم، أو ابثقه أولًا.",
"dialog.primitive_point_tip": "نقطة تملأ X وY وZ: انقر هنا ثم على رأس أو حافة أو وجه من النموذج، أو على نقطة من رسم، أو على نقطة الأصل.",
"dialog.reference_edge_tip": "حافة مستقيمة من الوجه المختار تُقاس منها المسافة أدناه: انقر هنا ثم على الحافة.",
"dialog.select": "↖ تحديد",
"dialog.selected_count": "↖ {count} محدد",
"dialog.selection_constraint": "قيد {id}",
@@ -1445,6 +1449,9 @@
"each rail's curves must be in one sketch": "يجب أن تكون منحنيات كل دليل في رسم واحد",
"feature computation ended without a result": "انتهى حساب الميزة دون نتيجة",
"file.open.drawing_filter": "التصاميم وملفات CAD والشبكات والرسومات",
"file.open.portal_fallback.no_portal_service": "منتقي الملفات الذي فُتح ليس تابعًا لسطح المكتب: خدمة منتقي الملفات في سطح المكتب غير متاحة.",
"file.open.portal_fallback.no_session_bus": "منتقي الملفات الذي فُتح ليس تابعًا لسطح المكتب: لا يوجد ناقل جلسات D-Bus.",
"file.open.portal_fallback.xwayland": "منتقي الملفات الذي فُتح ليس تابعًا لسطح المكتب: في XWayland ترفض البوابة النافذة الأصلية.",
"file_load.built": "بُني حتى الآن: {built} من {total} ميزة.",
"file_load.converting": "تحويل الجسم الصلب {done} من {total}",
"file_load.inserting": "إدراج الهندسة",
@@ -1605,7 +1612,7 @@
"help.parts.resize": "يغيّر مقاس قطعة قياسية مدرجة أو طولها ويعيد بناءها، وتبقيها مفاصلها مثبتة في مكانها. استخدمه عندما يتبين أن برغيًا أطول من اللازم أو أن ثقبًا يحتاج إلى مقاس آخر.",
"help.plastic.assign_rule": "يعيّن قاعدة بلاستيك لأجسام، فتأخذ ميزاتها البلاستيكية ميلها وخلوصها منها كقيم افتراضية. استخدمه لإعداد قطعة مقولبة مرة واحدة لكل ميزاتها.",
"help.plastic.boss": "يضيف نتوءًا لمسمار على وجه: عمودًا مع ثقب اختياري وميل وتقويس عند الجذر وأضلاع تقوية. استخدمه لنقاط البراغي في الهياكل المقولبة أو المطبوعة.",
"help.plastic.lip": "يضيف شفة أو أخدودًا مطابقًا على طول حافة جسم مفرَّغ. استخدمه ليتمركز نصفا الهيكل أحدهما على الآخر ويُغلقا بإحكام.",
"help.plastic.lip": "يضيف شفة أو أخدودًا مطابقًا على طول إطار جسم مفرَّغ (فرّغ الجسم بفتح أحد وجوهه أولًا، ثم اختر الإطار المحيط بتلك الفتحة). استخدمه ليتمركز نصفا الهيكل أحدهما على الآخر ويُغلقا بإحكام.",
"help.plastic.manage_rules": "ينشئ قواعد البلاستيك ويعدّلها: المادة وسماكة الجدار وأنصاف الأقطار والخلوص والميل. استخدمها للحفاظ على اتساق ميزات القطعة المقولبة.",
"help.plastic.rest": "يضيف مسندًا على وجه: وسادة مستديرة أو مستطيلة، مصمتة أو مجوفة، مع ميل. استخدمه كدعامة أو مُباعِد في هيكل مقولب.",
"help.plastic.snap_fit": "يضيف خطاف وصلة كبس على وجه، بطوله وسماكته ونتوء التثبيت. استخدمه لتثبيت القطع معًا بلا براغي.",
@@ -2035,6 +2042,8 @@
"nav.blender.help": "السحب بالزر الأوسط يدوّر العرض، وShift مع الأوسط يُزيحه، وCtrl مع الأوسط يكبّره؛ والسحب بالأيمن لا يحرك الكاميرا",
"nav.creo.help": "السحب بالزر الأوسط يدوّر العرض، وShift مع الأوسط يُزيحه، وCtrl مع السحب العمودي بالأوسط يكبّره أو يصغّره (السحب لأسفل للتكبير)؛ وتدوير العجلة يكبّره أو يصغّره",
"nav.creo.label": "Creo",
"nav.drag_keeps_mode.help": "يستمر السحب بالزر الأوسط أو الأيمن في التدوير أو التحريك أو التكبير كما بدأ حتى تحرير الزر، مهما ضُغطت المفاتيح أو حُرّرت في أثناء ذلك. عند الإيقاف: تقرر المفاتيح المضغوطة في كل لحظة من السحب.",
"nav.drag_keeps_mode.label": "يحتفظ السحب بطريقة التنقل التي بدأ بها",
"nav.free_orbit.help": "الفأرة والفأرة ثلاثية الأبعاد تدوّران العرض عبر القطبين وتحافظان على اللف. يُعطَّل التقاط المحاور بـ Shift.",
"nav.free_orbit.label": "تدوير حر",
"nav.free_orbit.snap_help": "الفأرة والفأرة ثلاثية الأبعاد تدوّران العرض عبر القطبين وتحافظان على اللف. اضغط مطولًا على Shift أثناء سحب التدوير لالتقاط اتجاه العرض واللف.",
@@ -2271,6 +2280,9 @@
"prefs.auto_operation_note": "ضم أو جسم جديد أو قص، حسب ما تلامسه المادة.",
"prefs.axes": "المحاور",
"prefs.axis": "المحور",
"prefs.background_choose": "اختيار صورة…",
"prefs.background_image": "الصورة",
"prefs.background_unreadable": "تعذرت قراءة صورة الخلفية",
"prefs.blur_cost": "تكلفة العرض النسبية. زيادة الأشرطة تعني عادةً مزيدًا من العمل.",
"prefs.blur_device": "غير متاح على جهاز الرسوميات أو مسار العرض هذا",
"prefs.blur_guard": "خُفّضت جودة التمويه للحفاظ على استجابة الإطارات. لم يتغير نصف القطر.",
@@ -2292,6 +2304,8 @@
"prefs.browser_eye_left": "أيقونة الإظهار بجانب سهم الطي",
"prefs.browser_eye_left_note": "تعرض صفوف المستعرض أيقونة الإظهار/الإخفاء بجانب سهم الطي كما كانت من قبل، بدلًا من عمود عند نهاية الصفوف.",
"prefs.browser_reveal_on_hover": "إظهار عند مرور المؤشر",
"prefs.browser_rows_start_closed": "صفوف المستعرض تبدأ مغلقة",
"prefs.browser_rows_start_closed_note": "تبدأ المواضع والمجلدات في المستعرض مغلقة، فيعرض التصميم الذي يضم مكونات كثيرة صفوفه العليا. يُفتح كل صف بسهمه أو بـ«توسيع الكل» أو بـ«البحث في المستعرض» الذي يفتح الصفوف المؤدية إلى الجسم فقط، ويغلق من جديد الصفوف التي فتحها البحث السابق. اختيار جسم في العرض ثلاثي الأبعاد يترك المستعرض كما هو. عند إيقافه تبدأ المجلدات مفتوحة، وتبدأ المواضع كما يحدّد خيار «المكوّنات مفتوحة من البداية»، ويفتح الاختيار في العرض ثلاثي الأبعاد المستعرضَ حتى صف الجسم، كما كان من قبل.",
"prefs.browser_show_note": "«ظاهر» يُبقي المستعرض على الشاشة. «إظهار عند مرور المؤشر» يُخفيه حتى يمر المؤشر فوق مكانه أو فوق الشريط الرفيع عند حافته اليسرى، ويبقى مفتوحًا ما دام المؤشر داخله أو كانت قائمة منه مفتوحة.",
"prefs.browser_shown": "ظاهر",
"prefs.built_in": "مدمج",
@@ -2337,8 +2351,8 @@
"prefs.dialogs_edge": "عند حافة العرض",
"prefs.dialogs_left_column": "في العمود الأيسر",
"prefs.dialogs_note": "مكان فتح حوارات الأوامر: مثبتة عند حافة العرض، أو في العمود الأيسر مكان المستعرض والسجل، اللذين يعودان كما كانا عند إغلاق الحوار.",
"prefs.dimension_step_angle": "ما تضيفه أسهم النافذة وأعلى/أسفل والعجلة إلى الزاوية",
"prefs.dimension_step_length": "ما تضيفه أسهم النافذة وأعلى/أسفل والعجلة إلى الطول",
"prefs.dimension_step_angle": "ما تضيفه أسهم النافذة والسهمان لأعلى ولأسفل والعجلة إلى الزاوية",
"prefs.dimension_step_length": "ما تضيفه أسهم النافذة والسهمان لأعلى ولأسفل والعجلة إلى الطول",
"prefs.dimension_steps": "خطوات النافذة",
"prefs.dimension_values": "قيم الأبعاد",
"prefs.dimension_values_note": "مكان كتابة قيمة البعد. للنافذة أسهم تغيّرها بخطوات؛ تضع أداة البعد النص أولًا.",
@@ -2423,6 +2437,10 @@
"prefs.invert_curved_roll_arrows_note": "سهما التدوير المنحنيان عند زاوية المكعب يدوران العرض داخل مستوى الشاشة. عند التشغيل: سهم التدوير مع عقارب الساعة يدور عكسها، وسهم العكس يدور معها. عند الإيقاف: يدور كل سهم بالعرض حسب انحنائه.",
"prefs.invert_navigator_arrows": "عكس أسهم تدوير مكعب العرض",
"prefs.invert_navigator_arrows_note": "تدوّر الأسهم المحيطة بالمكعب الكاميرا في الاتجاه الذي تشير إليه، كما في Fusion وInventor: السهم الموجود على اليسار يشير إلى اليمين ويدوّر الكاميرا إلى اليمين. عند الإيقاف: يدوّر السهم المكعب نحو الجهة التي يقع فيها، فيدوّر السهم الأيسر الكاميرا إلى اليسار. لا يتأثر سهما الدوران المنحنيان.",
"prefs.keep_unsaved_also_asks": "السؤال أيضًا عند إغلاق علامة التبويب",
"prefs.keep_unsaved_also_asks_tip": "عند إيقاف هذا الخيار، تُغلق علامة التبويب دون مربع المتصفح بعد تحديث نسخة كل تصميم غير محفوظ.",
"prefs.keep_unsaved_in_browser": "الاحتفاظ بالعمل غير المحفوظ في هذا المتصفح",
"prefs.keep_unsaved_in_browser_tip": "تُحدَّث التغييرات غير المحفوظة لكل تصميم مفتوح باستمرار في التخزين الخاص بهذا المتصفح. وتعرض الزيارة التالية استعادتها.",
"prefs.keep_work_on_tab_switch": "إبقاء مربعات الحوار والأدوات مفتوحة عند التبديل بين التصاميم",
"prefs.keep_work_on_tab_switch_note": "تحتفظ كل علامة تبويب تصميم بمربع الحوار أو الأداة المفتوحة، بما حددته وكتبته فيها، وبعلامة تبويب شريط الأدوات. وتجدها كما هي عند العودة إلى التصميم. عند الإيقاف: يؤدي التبديل بين التصاميم إلى إلغائها وعرض علامة التبويب «النموذج».",
"prefs.labels": "التسميات",
@@ -2554,6 +2572,8 @@
"prefs.section_hatch_by_part": "حسب الجزء",
"prefs.section_hatch_one": "نمط واحد",
"prefs.select": "تحديد",
"prefs.select_hidden_edges": "تحديد الحواف المخفية في نمط «مظلل مع الحواف المخفية»",
"prefs.select_hidden_edges_help": "يتيح تحديد الحواف عبر الأوجه عند تفعيل نمط «مظلل مع الحواف المخفية»، بما في ذلك عند استخدام التقويس. عند الإيقاف يبقى سلوك التحديد كما في v0.30.1. تظل الأجسام المخفية غير قابلة للتحديد.",
"prefs.select_one_item": "تحديد عنصر واحد",
"prefs.select_other_hidden": "تحديد العناصر الهندسية المخفية باستخدام «تحديد عنصر آخر»",
"prefs.select_other_hidden_help": "عند التفعيل: تضمين الأجسام والمخططات المخفية والحواف المحجوبة قرب المؤشر. عند التعطيل: استخدام القائمة الأصلية للعناصر الهندسية المرئية أسفل المؤشر.",
@@ -2578,6 +2598,8 @@
"prefs.sketch.click_selects": "النقر يحدد",
"prefs.sketch.conflict_colour": "لون التعارض",
"prefs.sketch.drawing": "الرسم",
"prefs.sketch.ellipse_axes": "رسم القطوع الناقصة مع محاورها",
"prefs.sketch.ellipse_axes_note": "المحوران الأكبر والأصغر كخطوط مساعدة، لتحديد أبعاد أنصاف المحاور والمحاور. عند الإيقاف: القطع الناقص وحده، ويعطي D عليه نصف قطره الأصغر أينما سُحبت القيمة، مرسومًا ومُعلَّمًا بـ R كما في السابق.",
"prefs.sketch.exit_keep": "إبقاء",
"prefs.sketch.exit_previous": "العودة إلى العرض السابق",
"prefs.sketch.exit_unless_rotated": "إلا إذا تم التدوير",
@@ -2747,6 +2769,12 @@
"recovery.just_now": "الآن",
"recovery.min_ago": "قبل {count} دقيقة",
"recovery.never_saved": "لم يُحفظ قط",
"recovery.open_saved": "فتح النسخة المحفوظة",
"recovery.restore": "استعادة",
"recovery.restore_intro": "التغييرات التي لم تُحفظ عند آخر إغلاق لهذه الصفحة محفوظة في هذا المتصفح.",
"recovery.restore_row": "{name}، تغيّر {when}",
"recovery.restore_title": "استعادة التصاميم غير المحفوظة؟",
"recovery.restore_title_one": "استعادة التصميم غير المحفوظ: {name}، تغيّر {when}؟",
"refused.change": "تعذّر تغيير {body}.",
"refused.cut": "تعذّر قطع {body}.",
"refused.face_curved": "أحد أوجهه المنحنية لا يمكن استخدامه في النمذجة.",
@@ -2761,9 +2789,9 @@
"render.adaptive_absolute": "الحد الأدنى المطلق لخطأ RGB",
"render.adaptive_coverage": "هدف خطأ التغطية",
"render.adaptive_enable": "توزيع العيّنات باستخدام عيّنات استكشافية مستقلة",
"render.adaptive_help": "على GPU فقط. تُستبعد العيّنات الاستكشافية المستقلة، ثم تُوزَّع 16 عيّنة إنتاجية أو أكثر لكل بكسل حتى قيمة «عيّنات لكل بكسل». يوجّه تباين RGB والتغطية هذا التوزيع؛ قد تفوت المسارات النادرة وقد تبقى البكسلات المحدودة مشوشة. يحصي التقرير النهائي العمل الفعلي. لا ضمان معتمد للتشويش أو للزمن.",
"render.adaptive_help": "تُهمل العينات الاستطلاعية المستقلة، ثم يُحدد لكل بكسل 16 عينة إنتاج على الأقل حتى قيمة العينات لكل بكسل. تشمل حدود موارد CPU المرحلتين. يوجه تباين RGB والتغطية التوزيع؛ وقد تبقى ضوضاء في البكسلات التي بلغت الحد. يحصي التقرير النهائي العينات الفعلية.",
"render.adaptive_minimum": "الحد الأدنى للعيّنات الإنتاجية",
"render.adaptive_pending": "التوزيع التكيّفي على GPU بعيّنات استكشافية مستقلة وطبقات فرينل الشرطية متاحة. التقارب المعتمد وضمانات الأحداث النادرة والنقل التكيّفي على CPU غير متاحة بعد.",
"render.adaptive_pending": "يتوفر التوزيع التكيفي بعينات استطلاعية مستقلة على CPU وGPU. تتطلب طبقات فرينل الشرطية GPU. لا يضمن التوزيع الإحصائي التقارب.",
"render.adaptive_pilots": "العيّنات الاستكشافية لكل بكسل",
"render.adaptive_relative": "هدف خطأ RGB النسبي",
"render.add_ground": "إضافة أرضية ملائمة إلى الإعداد",
@@ -2824,7 +2852,7 @@
"render.appearance_editor.replace": "استبدال الخصائص البصرية المحفوظة لهذا الجسم",
"render.appearance_editor.revalidate": "إعادة التحقق من هذا الجسم",
"render.appearance_editor.save": "حفظ هذه الخصائص البصرية",
"render.appearance_editor.save_reason": "يتطلب حفظ هذه الخصائص البصرية زجاجًا فيزيائيًا على جسم حالي في بطاقة الرسومات. تبقى الطبقة الشفافة وخرائط الصور المحلية مؤقتة.",
"render.appearance_editor.save_reason": "لحفظ الزجاج، فعّله على جسم حالي يستخدم CPU أو GPU. للطلاء الشفاف استخدم حفظ الطلاء مع اللقطة. تبقى ملفات الصور المحلية مؤقتة.",
"render.appearance_editor.saved": "محفوظ مع هذه اللقطة",
"render.appearance_editor.saved_state": "هذه الخصائص البصرية محفوظة مع هذه اللقطة.",
"render.appearance_editor.scatter_reason": "التشتت الداخلي يتطلب حدًّا لجسم مصمت مغلق.",
@@ -2920,7 +2948,7 @@
"render.children.copy_request": "نسخ JSON الطلب",
"render.children.coverage_error": "خطأ التغطية",
"render.children.cpu": "تتبّع الأشعة على المعالج",
"render.children.denoise": "تقليل التشويش في الصورة النهائية",
"render.children.denoise": "تقليل تشويش الصورة",
"render.children.depth": "عمق المسار",
"render.children.distortion": "التشويه",
"render.children.edge_samples": "عينات الحواف",
@@ -3052,7 +3080,7 @@
"render.cpu_point_help": "حتى ثمانية أضواء نقطية تُضاف إلى الإضاءة الاتجاهية المختارة. المسافات بالمليمتر؛ ويجب ألا يتجاوز الحد الأدنى المسافة المرجعية.",
"render.cpu_primary_samples": "عيّنات الحواف لكل بكسل",
"render.cpu_progressive": "CPU تدريجي",
"render.cpu_progressive_help": "يُراكم مُكامل CPU الحالي على تمريرات من عيّنة أساسية واحدة. الانعكاسات المختارة تتطلب عيّنة واحدة؛ والأضواء المساحية والعوازل وتباين الخواص تتطلب «صورة واحدة».",
"render.cpu_progressive_help": "تُجمع عينات كاميرا CPU الجديدة ويُعرض كل تمرير مكتمل. تستخدم الانعكاسات المحددة محاولة واحدة لكل عينة كاميرا؛ ويحتفظ الزجاج المعتمد والتباين الاتجاهي المستوي بحدودهما الحالية. تتشارك العينات الاستطلاعية التكيفية والإنتاج ميزانية ملف CPU.",
"render.cpu_progressive_limits": "بحد أقصى 1,048,576 بكسل و16,777,216 عملية تقييم بكسل × عيّنة. تغيير العيّنات لا يغيّر حجم الصورة تلقائيًا أبدًا.",
"render.cpu_progressive_seed": "بذرة عيّنات CPU",
"render.cpu_progressive_spp": "عيّنات لكل بكسل",
@@ -3228,7 +3256,7 @@
"render.export_display": "تصدير: PNG للعرض",
"render.export_image_label": "تصدير الصورة",
"render.export_linear": "تصدير: EXR خطي خام",
"render.exr_pending": "يستطيع GPU الاحتفاظ بـ EXR خطي خام. الضغط والصور متعددة الطبقات وتمريرات التصيير المساعدة غير متاحة بعد.",
"render.exr_pending": "يمكن لتتبع الأشعة على CPU وتتبع المسارات على GPU الاحتفاظ بـ EXR خطي خام. الضغط والصور متعددة الطبقات وتمريرات التصيير المساعدة غير متاحة بعد.",
"render.film_grain": "حبيبات فوتوغرافية",
"render.fit": "ملاءمة المنتج",
"render.fixed_camera": "الكاميرا",
@@ -3268,9 +3296,11 @@
"render.frame_wide": "16:9 · عريض",
"render.gpu_depth": "أقصى عمق للمسار",
"render.gpu_options_help": "تُحفظ العيّنات والعمق والبذرة والتعريض مع هذا الإعداد. تُحفظ طريقة الصورة النشطة فقط.",
"render.gpu_path_crashed": "أُغلق SolidCraft أثناء إعداده على محوّل الرسوميات هذا في المرة السابقة، على الأرجح داخل برنامج تشغيل الرسوميات، لذا لن يُعاد إعداده تلقائيًا. استخدم تتبع الأشعة على المعالج المركزي بدلًا منه، أو حاول مرة أخرى (يعيد تحديث برنامج التشغيل المحاولة أيضًا).",
"render.gpu_path_failed": "متتبع المسارات على GPU غير متاح",
"render.gpu_path_first_setup": "قد يستغرق الإعداد الأول على وحدة GPU هذه عدة دقائق؛ ويبقى SolidCraft قابلًا للاستخدام أثناءها. تعيد المرات اللاحقة استخدام المظللات المترجمة حيث يحتفظ بها برنامج التشغيل.",
"render.gpu_path_preparing": "متتبع المسارات على GPU: ترجمة المظللات…",
"render.gpu_path_retry": "حاول مرة أخرى",
"render.gpu_seed": "البذرة العشوائية",
"render.gpu_unavailable": "تتبع المسارات على GPU غير جاهز على هذا الجهاز.",
"render.ground_apply": "ملاءمة الأرضية وحفظها",
@@ -3589,7 +3619,7 @@
"render.method_cpu": "تتبع الأشعة على CPU",
"render.method_gpu": "تتبع المسارات على GPU",
"render.method_limit_raster": "التنقيط يستخدم معاينة الاستوديو المعتمة الثابتة. اختر طريقة تتبع أشعة صريحة لعناصر التحكم البصرية المدعومة.",
"render.method_raster": "تنقيط على CPU",
"render.method_raster": "تصيير نقطي على CPU (تخطيطي)",
"render.navigation_help": "تتحرك الكاميرا كما في التصميم (التفضيلات › التنقل). «حفظ الإعداد» يسجّل الكاميرا الحالية.",
"render.nested_enable": "عوازل متداخلة معتمدة",
"render.nested_help": "حتى 16 طبقة حاوية؛ يجب أن يكون كل أصل محدبًا وكل فرع داخله تمامًا. التقاطعات والأسطح المتلامسة والصفائح الرقيقة ونفاذ الظل المستقيم تُرفض. حالة وجود الكاميرا في الداخل تتبع التسلسل المعتمد. لا يُنشئ سطح سائل ولا يدمج حدود CAD المتلامسة.",
@@ -3617,7 +3647,7 @@
"render.output_exr_unavailable": "إعداد مخرجات EXR غير متاح هنا. تصدير EXR الخطي يتطلب صورة GPU مكتملة بنسخة رئيسية خطية محتفظ بها.",
"render.output_fit": "ملاءمة",
"render.output_format": "الصيغة",
"render.output_format_status": "PNG هو صورة العرض المكتملة بالضبط. EXR الاختياري على GPU يحتفظ بالإشعاع الخام الخطي للمشهد بصيغة FLOAT32 قبل مرشحات العرض. التصدير لا يستبدل أبدًا ذاكرة المعاينة المؤقتة أو مهمة أخرى.",
"render.output_format_status": "PNG هو صورة العرض المكتملة بالضبط. يحتفظ EXR الاختياري على CPU أو GPU بالإشعاع الخام الخطي للمشهد بصيغة FLOAT32 قبل مرشحات العرض. لا يستبدل التصدير أبدًا الصورة بذاكرة المعاينة المؤقتة أو بمهمة مختلفة.",
"render.output_grading": "تدرّج الألوان",
"render.output_image": "الصورة",
"render.output_largest": "الأقصى {width} × {height}",
@@ -3702,7 +3732,7 @@
"render.quality_custom": "مخصص",
"render.quality_draft": "مسودة",
"render.quality_high": "عالية",
"render.quality_projection_help": "إعدادات Q الجاهزة تتضمن متطلبات مستقبلية. هذا الإجراء يُسقط فقط ميزانية PNG مدعومة بعيّنات ثابتة، ويُغفل صراحةً الإيقاف التكيّفي والإخراج الخطي الخام وحدود النفاذ المستقلة.",
"render.quality_projection_help": "تغيّر هذه المراجعة الدقة وميزانية العينات وعمق المسار والبذرة وتقليل التشويش فقط. تُحفظ الإعدادات الأخرى؛ ولا تثبت الميزانية تقارب الصورة.",
"render.quality_review": "مراجعة ميزانية العيّنات",
"render.queue_pending": "لا توجد في هذه الواجهة قائمة إنتاج تلقائية ولا مُصيّر للرسوم المتحركة. تبقى الصور الثابتة المكتملة قابلة للتصدير كلٌّ على حدة.",
"render.radial_assign": "تعيين مظهر",
@@ -3733,7 +3763,7 @@
"render.result_navigation": "الإزاحة والتكبير يتبعان تفضيلات التنقل. Space مع السحب يُزيح العرض.",
"render.result_open": "فتح الصورة المكتملة في اللوحة الرئيسية.",
"render.result_preview": "المعاينة الحية",
"render.result_provisional": "صورة CPU مؤقتة",
"render.result_provisional": "صورة مؤقتة",
"render.result_provisional_help": "نقطة التحقق هذه غير قابلة للتصدير. يحتفظ التصدير بآخر نتيجة مكتملة؛ والإلغاء يعود إلى تلك النتيجة.",
"render.result_reduced_display": "قُلّصت معاينة العرض إلى حد الجهاز أو الذاكرة المؤقتة لمنطقة العرض. يحتفظ PNG المحفوظ والتصدير بالدقة الكاملة؛ والفحص الأصلي بنسبة 100% غير متاح لهذا العرض المقلّص.",
"render.result_retained": "تُعرض آخر صورة مكتملة؛ ولم يستبدلها تصيير أحدث.",
@@ -3897,7 +3927,7 @@
"render.stage_panels.prepare_plan": "تجهيز خطة الإنشاء",
"render.stage_panels.projects": "ما يُسقط",
"render.stage_panels.quality_ack": "أقرّ بالميزانية الثابتة وما تُغفله",
"render.stage_panels.quality_omissions": "التوقف التكيّفي وأهداف الخطأ، وعمق نفاذ منفصل، وقواعد الروليت الروسية، والإخراج الخطي الخام، ونسخة EXR الأصلية، وشهادة التقارب.",
"render.stage_panels.quality_omissions": "لا تضبط هذه الميزانية أهداف الخطأ التكيفي أو حدود النفاذ المستقلة أو قواعد الروليت الروسي أو الاحتفاظ بالأصل الخام، ولا تضمن تقارب الصورة.",
"render.stage_panels.reference": "مرجع",
"render.stage_panels.references": "مراجع",
"render.stage_panels.remove_optics": "إزالة البصريات",
@@ -3941,7 +3971,7 @@
"render.stage_panels.subtitle_environment": "بيئة HDR أصلية · المشهد بأكمله",
"render.stage_panels.subtitle_maps": "خرائط مكانية مُعدّة · معاينة مؤقتة",
"render.stage_panels.subtitle_physical": "خطة للقراءة فقط · لا تُنشأ مادة ولا تُعيَّن",
"render.stage_panels.subtitle_quality": "ميزانية GPU ثابتة · تُغفل المتطلبات المستقبلية",
"render.stage_panels.subtitle_quality": "ميزانية عينات GPU · الحقول المدرجة فقط",
"render.stage_panels.subtitle_rig": "طقم إضاءة مُعدّ · باعثات وبطاقات سلبية",
"render.stage_panels.subtitle_saved_maps": "خرائط مُعدّة · محفوظة مع هذه اللقطة",
"render.stage_panels.subtitle_stage": "أرضية ومنصة فيزيائيتان · ملائمتان للمنتج",
@@ -3966,7 +3996,7 @@
"render.studio_area_help": "حتى أربعة مستطيلات أحادية الجانب. نصفا الضلعين المتعامدين U وV يحددان الحجم؛ وحاصل U × V يواجه الجهة المضاءة. الإحداثيات بمليمترات فضاء العالم.",
"render.studio_center": "المركز X, Y, Z (mm)",
"render.studio_denoise": "تقليل التشويش في الصورة النهائية",
"render.studio_denoise_help": "مرشح نهائي يراعي الحواف؛ وقد تلين التفاصيل الدقيقة. الزجاج والأسطح المرآوية الحادة لا يمر عليها المرشح.",
"render.studio_denoise_help": "تستخدم الصور التدريجية والنهائية مرشحًا يحافظ على الحواف؛ وقد تصبح التفاصيل الدقيقة أنعم. لا يُطبّق المرشح على الزجاج والأسطح ذات الانعكاس الحاد.",
"render.studio_direction": "الاتجاه نحو الشمس (X, Y, Z)",
"render.studio_environment": "البيئة",
"render.studio_environment_bounds": "يجب أن يكون دوران البيئة قيمة محدودة ضمن ±1,000,000 راديان والشدة بين 0 و100.",
@@ -4117,7 +4147,7 @@
"settings.display_quality_note": "المستويات الأدق ترسم الأوجه والحواف المنحنية بنعومة أكبر وتستغرق وقتًا أطول لإنشاء الشبكة. يتغير العرض فقط، لا التصميم ولا الملفات المصدّرة.",
"settings.double_click_components": "النقر المزدوج على مكوّن لتنشيطه",
"settings.frame_rate_details_note": "يضيف رسمًا لأزمنة الإطارات الأخيرة مع المثلثات المرسومة فوقه، ومدرّجًا تكراريًا لأزمنة الإطارات، وما رسمته وحدة GPU. انقر معدل الإطارات في العرض لإظهارها أو إخفائها.",
"settings.frame_rate_note": "يعرض الإطارات في الثانية في الزاوية اليمنى السفلية من العرض. أثناء تفعيله يُعاد رسم العرض باستمرار.",
"settings.frame_rate_note": "يعرض الإطارات في الثانية في الزاوية اليمنى السفلية من العرض.",
"settings.insert_lists_open_designs": "إدراج تصميم يسرد التصاميم المفتوحة أولًا",
"settings.measure_axis_distances": "مسافات المحاور في القياس",
"settings.measure_axis_distances_help": "إظهار مركّبات الأبعاد على محاور العالم بجانب المسافة الكلية أثناء فتح أداة القياس.",
@@ -4168,6 +4198,32 @@
"sketch.dof_left": "تبقّت {count} درجات حرية",
"sketch.dof_left_one": "تبقّت {count} درجة حرية",
"sketch.draw_curves": "الرسم كـ {linetype}",
"sketch.error.closed_curve_two_crossings": "يحتاج المنحنى المغلق إلى نقطتي تقاطع لقصّه",
"sketch.error.nothing_to_break": "لا يوجد هنا ما يمكن كسره",
"sketch.error.offset_fillet_chamfer": "`{id}` منحنى إزاحة، وزواياه تتبع منحنياته المصدر: قوّس زوايا المنحنيات المصدر أو اشطفها قبل الإزاحة، أو فك ارتباط الإزاحة أولًا",
"sketch.error.offset_trim": "`{id}` منحنى إزاحة، وأطرافه تتبع منحنياته المصدر: عدّل المنحنيات المصدر بدلًا من ذلك، أو فك ارتباط الإزاحة أولًا",
"sketch.hint.arc.center_point": "قوس: انقر المركز ثم البداية، ودُر في أي اتجاه وانقر النهاية، أو اكتب زاوية القوس بإشارة موجبة أو سالبة",
"sketch.hint.arc.three_point": "قوس: انقر البداية ثم النهاية، ثم نقطة على القوس",
"sketch.hint.arc3d": "قوس ثلاثي الأبعاد: انقر البداية ثم النهاية، ثم نقطة على القوس (يمكن أن تقع النقاط خارج مستوى الرسم)",
"sketch.hint.break": "كسر: انقر على منحنى في الموضع الذي يُقسم عنده",
"sketch.hint.circle.center": "دائرة: انقر المركز، ثم نقطة على الدائرة",
"sketch.hint.constraint.generic": "قيد: اختر عناصر الرسم",
"sketch.hint.constraint.horizontal_vertical": "أفقي/رأسي: اختر خطًا، أو نقطتين، أو نقطة وخطًا؛ اضغط Shift مع الاستمرار لاختيار منتصف خط",
"sketch.hint.constraint.midpoint": "نقطة المنتصف: اختر نقطة وخطًا، أو خطين لتوسيط الثاني على الأول",
"sketch.hint.dimension": "البُعد: اختر عنصرًا أو عنصرين واكتب القيمة (أو انقر لوضعه أولًا)؛ انقر على بُعد لتعديله؛ اضغط Enter للتطبيق",
"sketch.hint.extend": "تمديد: انقر على منحنى قرب الطرف المراد تمديده (Shift: انقر منحنيين لتمديدهما معًا حتى نقطة التقائهما)",
"sketch.hint.fallback": "انقر في الرسم",
"sketch.hint.line": "خط: انقر النقاط؛ انقر بالزر الأيمن أو اضغط Esc للإنهاء",
"sketch.hint.line3d": "خط ثلاثي الأبعاد: انقر نقاطًا في الفضاء؛ انقر بالزر الأيمن أو اضغط Esc للإنهاء (يمكن أن تقع النقاط خارج مستوى الرسم)",
"sketch.hint.open_done": "تم الفتح",
"sketch.hint.point3d": "نقطة ثلاثية الأبعاد: انقر حيث توضع النقطة ثلاثية الأبعاد",
"sketch.hint.rectangle.center": "مستطيل من المركز: انقر المركز، ثم زاوية",
"sketch.hint.rectangle.rounded": "مستطيل مستدير الزوايا: انقر زاويتين متقابلتين (اضغط Tab للانتقال إلى خانة نصف القطر وكتابة نصف قطر الزوايا)",
"sketch.hint.rectangle.three_point": "مستطيل بثلاث نقاط: انقر نقطتين على أحد الأضلاع، ثم العرض",
"sketch.hint.rectangle.two_point": "مستطيل: انقر زاويتين متقابلتين",
"sketch.hint.slot": "شق: انقر الطرفين، ثم العرض",
"sketch.hint.spline3d": "منحنى شريحي ثلاثي الأبعاد: انقر نقاط الملاءمة في الفضاء ثلاثي الأبعاد؛ انقر بالزر الأيمن أو اضغط Esc للإنهاء",
"sketch.hint.trim": "قص: انقر على الجزء المراد إزالته من المنحنى، أو اسحب عبر الأجزاء المراد إزالتها",
"sketch.make_selection": "تحويل التحديد إلى {linetype}",
"sketch.open_ends": "مفتوحة {count}",
"sketch.status_conflict": "تعارض القيود",
@@ -4389,6 +4445,7 @@
"ui.3d_sketch": "رسم ثلاثي الأبعاد",
"ui.activate": "تنشيط",
"ui.active_component": "المكوّن النشط",
"ui.addToQuickAccess": "إضافة إلى الوصول السريع",
"ui.add_configuration": "إضافة تكوين",
"ui.add_parameter": "إضافة معامل",
"ui.agent_chat": "محادثة الوكيل",
@@ -5017,6 +5074,7 @@
"ui.heading_construct": "إنشاء مرجعي",
"ui.heading_create": "إنشاء",
"ui.heading_create_sketch": "إنشاء رسم",
"ui.heading_custom_view_navigator": "متصفح العرض المخصص",
"ui.heading_cylinder": "أسطوانة",
"ui.heading_design": "التصميم",
"ui.heading_draft": "ميل السحب",
@@ -5303,6 +5361,7 @@
"ui.quality": "الجودة",
"ui.quantity": "الكمية",
"ui.quantity_2": "الكمية 2",
"ui.quick_access_full": "يتسع شريط الوصول السريع لـ {count} أمرًا؛ أزل أحدها لإضافة آخر",
"ui.quit": "خروج",
"ui.radial_context_menu": "قائمة سياقية دائرية",
"ui.radius": "نصف القطر",
@@ -5320,6 +5379,7 @@
"ui.relink_not_linked": "هذا المكوّن غير مرتبط بتصميم مصدر.",
"ui.relink_select_one": "حدد مكوّنًا مرتبطًا واحدًا أو إحدى نسخه أولًا.",
"ui.relink_source_all": "إعادة ربط المصدر (كل النسخ)…",
"ui.removeFromQuickAccess": "إزالة من الوصول السريع",
"ui.removeFromToolbar": "إزالة من شريط الأدوات",
"ui.removeSection": "إزالة المقطع",
"ui.render": "التصيير",
@@ -5328,6 +5388,8 @@
"ui.repeat_nothing": "لا يوجد ما يُكرَّر بعد: ابدأ أداة أو مربع حوار أولًا.",
"ui.replace_with_link_done": "أصبحت النسخ الآن مواضع للتصميم المرتبط.",
"ui.report_a_bug": "الإبلاغ عن خطأ",
"ui.report_characters_left_one": "الأحرف المتبقية: {count}",
"ui.report_characters_left_other": "الأحرف المتبقية: {count}",
"ui.reset": "إعادة تعيين",
"ui.reset_all": "إعادة تعيين الكل",
"ui.reset_the_settings_on_this_page": "إعادة تعيين إعدادات هذه الصفحة",
@@ -5612,6 +5674,14 @@
"values must be numbers or expressions": "يجب أن تكون القيم أعدادًا أو تعابير",
"view.auto_ortho_faces": "منظوري أثناء التدوير، ومتعامد على الأوجه",
"view.render_inspector": "مفتش التصيير",
"view_gizmo.arrows": "أسهم التدوير",
"view_gizmo.home": "زر العرض الرئيسي",
"view_gizmo.no_such_body": "لا يوجد في هذا التصميم جسم بهذا الاسم.",
"view_gizmo.note": "اختر جسمًا في العرض. يُحفظ مع تفضيلاتك وملفات تعريف الإعدادات، ويظهر في كل تصميم. انقر أحد أوجهه للنظر إليه مباشرة.",
"view_gizmo.texture": "الخامة",
"view_gizmo.themed": "حسب السمة",
"view_gizmo.too_detailed": "مفصّل أكثر مما يناسب متصفح العرض (أكثر من 10,000 مثلث)",
"view_gizmo.too_thin": "طويل ورفيع أكثر مما يناسب متصفح العرض: يجب ألا يقل أرفع جانب عن خُمس أطول جانب",
"web.files.insert_drawings_hint": "ملفات CAD أو شبكات أو DXF أو SVG إلى التصميم المفتوح",
"web.files.insert_stored_drawings_hint": "إلى التصميم المفتوح (ملفات CAD، شبكات، DXF، SVG)",
"web.open_full": "فتح في SolidCraft",
-2
View File
@@ -957,8 +957,6 @@
"cmd.inspect_accessibility": "Analiza dostupnosti",
"cmd.inspect_center_of_mass": "Centar mase",
"cmd.inspect_clearance": "Provjera zazora",
"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",
-2
View File
@@ -957,8 +957,6 @@
"cmd.inspect_accessibility": "Analýza přístupnosti",
"cmd.inspect_center_of_mass": "Těžiště",
"cmd.inspect_clearance": "Vůle",
"cmd.inspect_colored_components": "Zapnout barvy komponent",
"cmd.inspect_colored_components_disable": "Vypnout barvy komponent",
"cmd.inspect_compare": "Porovnat tělesa",
"cmd.inspect_compare_picture": "Porovnat obrázek",
"cmd.inspect_continuity": "Analýza spojitosti",
-2
View File
@@ -957,8 +957,6 @@
"cmd.inspect_accessibility": "Tilgængelighedsanalyse",
"cmd.inspect_center_of_mass": "Massecentrum",
"cmd.inspect_clearance": "Frigangskontrol",
"cmd.inspect_colored_components": "Aktivér komponentfarver",
"cmd.inspect_colored_components_disable": "Deaktivér komponentfarver",
"cmd.inspect_compare": "Sammenlign emner",
"cmd.inspect_compare_picture": "Sammenlign billede",
"cmd.inspect_continuity": "Kontinuitetsanalyse",
+41 -22
View File
@@ -833,8 +833,6 @@
"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",
@@ -962,8 +960,6 @@
"cmd.inspect_accessibility": "Zugänglichkeitsanalyse",
"cmd.inspect_center_of_mass": "Massenschwerpunkt",
"cmd.inspect_clearance": "Freiraum",
"cmd.inspect_colored_components": "Komponentenfarben einschalten",
"cmd.inspect_colored_components_disable": "Komponentenfarben ausschalten",
"cmd.inspect_compare": "Körper vergleichen",
"cmd.inspect_continuity": "Kontinuitätsprüfung",
"cmd.inspect_curvature_comb": "Krümmungskamm-Analyse",
@@ -1026,7 +1022,6 @@
"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",
@@ -1294,6 +1289,7 @@
"cmd.timeline_stop": "Verlauf anhalten",
"cmd.timeline_suppress": "Merkmal unterdrücken",
"cmd.ui.assistant": "Assistenten ein-/ausblenden",
"cmd.view.navigator": "Eigener Ansichtsnavigator",
"cmd.view.render_browser": "Render-Browser",
"cmd.view.render_dock": "Render-Panel verschieben",
"cmd.view.render_focus": "Render-Fokusansicht",
@@ -1392,6 +1388,7 @@
"dialog.computing": "Wird berechnet… {seconds} s",
"dialog.could_not_be_checked": "nicht prüfbar",
"dialog.custom_axis": "↖ Eigene Achse",
"dialog.cylinder_face_tip": "Die Fläche oder der Eckpunkt des Modells, auf dem der Zylinder steht: hier klicken, dann in der Ansicht darauf klicken. Leer gelassen platzieren ihn X, Y und Z.",
"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",
@@ -1400,6 +1397,8 @@
"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.",
"dialog.primitive_point_tip": "Ein Punkt, der X, Y und Z füllt: hier klicken, dann einen Eckpunkt, eine Kante oder Fläche des Modells, einen Punkt einer Skizze oder den Ursprung anklicken.",
"dialog.reference_edge_tip": "Eine gerade Kante der gewählten Fläche, von der aus der Abstand darunter gemessen wird: hier klicken, dann die Kante anklicken.",
"dialog.select": "↖ Auswählen",
"dialog.selected_count": "↖ {count} ausgewählt",
"dialog.selection_constraint": "Bedingung {id}",
@@ -1442,6 +1441,9 @@
"each rail's curves must be in one sketch": "die Kurven jeder Führungskurve müssen in derselben Skizze liegen",
"feature computation ended without a result": "die Berechnung des Merkmals wurde ohne Ergebnis beendet",
"file.open.drawing_filter": "Designs, CAD-Dateien, Netze und Zeichnungen",
"file.open.portal_fallback.no_portal_service": "Die geöffnete Dateiauswahl ist nicht die des Desktops: Der Dateiauswahl-Dienst des Desktops ist nicht verfügbar.",
"file.open.portal_fallback.no_session_bus": "Die geöffnete Dateiauswahl ist nicht die des Desktops: Es gibt keinen D-Bus-Sitzungsbus.",
"file.open.portal_fallback.xwayland": "Die geöffnete Dateiauswahl ist nicht die des Desktops: Unter XWayland lehnt das Portal das Elternfenster ab.",
"file_load.built": "Bisher erstellt: {built} von {total} Merkmalen.",
"file_load.converting": "Volumenkörper {done} von {total} wird konvertiert",
"file_load.inserting": "Geometrie wird eingefügt",
@@ -2020,6 +2022,8 @@
"nav.blender.help": "Mittlere Maustaste dreht, Umschalt+Mitte verschiebt, Strg+Mitte zoomt; rechtes Ziehen lässt die Kamera unverändert",
"nav.creo.help": "Ziehen mit der mittleren Maustaste dreht, Umschalt+Mitte verschiebt, Strg+Mitte zoomt beim vertikalen Ziehen (nach unten zum Vergrößern); das Mausrad zoomt",
"nav.creo.label": "Creo",
"nav.drag_keeps_mode.help": "Ein Ziehen mit der mittleren oder rechten Taste dreht, verschiebt oder zoomt weiter wie begonnen, bis die Taste losgelassen wird, egal welche Tasten dabei gedrückt oder losgelassen werden. Aus: Die gehaltenen Tasten entscheiden in jedem Moment des Ziehens.",
"nav.drag_keeps_mode.label": "Ziehen behält die Navigation bei, mit der es begann",
"nav.free_orbit.help": "Maus und 3D-Maus drehen über beide Pole hinweg und behalten die Rollneigung bei. Das Einrasten mit Umschalt ist deaktiviert.",
"nav.free_orbit.label": "Freie Drehung",
"nav.free_orbit.snap_help": "Maus und 3D-Maus drehen über beide Pole hinweg und behalten die Rollausrichtung bei. Halten Sie beim Ziehen zum Drehen die Umschalttaste gedrückt, um Blickrichtung und Rollausrichtung einrasten zu lassen.",
@@ -2277,6 +2281,8 @@
"prefs.browser_eye_left": "Auge neben dem Aufklapppfeil",
"prefs.browser_eye_left_note": "Browserzeilen zeigen ihr Auge zum Ein- und Ausblenden wie bisher neben dem Aufklapppfeil statt in einer Spalte am rechten Zeilenende.",
"prefs.browser_reveal_on_hover": "Beim Überfahren einblenden",
"prefs.browser_rows_start_closed": "Browser-Zeilen beginnen geschlossen",
"prefs.browser_rows_start_closed_note": "Platzierungen und Ordner im Browser beginnen geschlossen, sodass ein Entwurf mit vielen Komponenten seine obersten Zeilen zeigt. Jede öffnet sich mit ihrem Pfeil, „Alle ausklappen“ oder „Im Browser suchen“, das nur die Zeilen bis zu seinem Körper öffnet und die von der letzten Suche geöffneten wieder schließt. Ein in der 3D-Ansicht gewählter Körper lässt den Browser, wie er ist. Ist es aus, beginnen die Ordner geöffnet, Platzierungen so, wie „Komponenten zu Beginn aufgeklappt“ es festlegt, und eine Auswahl in der 3D-Ansicht öffnet den Browser bis zu ihrem Körper, wie bisher.",
"prefs.browser_show_note": "Angezeigt lässt den Browser auf dem Bildschirm. Beim Überfahren einblenden blendet ihn aus, bis der Zeiger über seinen Platz oder die schmale Lasche an seinem linken Rand kommt; er bleibt offen, solange der Zeiger darin ist oder ein Menü daraus offen ist.",
"prefs.browser_shown": "Angezeigt",
"prefs.built_in": "Integriert",
@@ -2330,7 +2336,6 @@
"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.",
@@ -2535,6 +2540,8 @@
"prefs.search": "Einstellungen suchen",
"prefs.search_empty": "Keine passenden Einstellungen",
"prefs.select": "Auswählen",
"prefs.select_hidden_edges": "Verdeckte Kanten in der schattierten Ansicht mit verdeckten Kanten auswählen",
"prefs.select_hidden_edges_help": "Kanten durch Flächen auswählen, wenn die schattierte Ansicht mit verdeckten Kanten aktiv ist, auch zum Verrunden. Aus behält das Auswahlverhalten von v0.30.1 bei. Ausgeblendete Körper bleiben ausgeschlossen.",
"prefs.select_one_item": "Ein Element auswählen",
"prefs.select_other_hidden": "Verborgene Geometrie mit „Andere auswählen“ auswählen",
"prefs.select_other_hidden_help": "Ein: Verborgene Körper, Skizzen und verdeckte Kanten nahe dem Zeiger einbeziehen. Aus: Die ursprüngliche Liste sichtbarer Geometrie direkt unter dem Zeiger verwenden.",
@@ -2559,6 +2566,8 @@
"prefs.sketch.click_selects": "Klick wählt",
"prefs.sketch.conflict_colour": "Konfliktfarbe",
"prefs.sketch.drawing": "Zeichnen",
"prefs.sketch.ellipse_axes": "Ellipsen mit ihren Achsen zeichnen",
"prefs.sketch.ellipse_axes_note": "Haupt- und Nebenachse als Konstruktionslinien, damit Halbachsen und Achsen bemaßt werden können. Aus: nur die Ellipse, und D darauf ergibt ihren Nebenradius, wohin der Wert auch gezogen wird, gezeichnet und mit R beschriftet wie bisher.",
"prefs.sketch.exit_keep": "Beibehalten",
"prefs.sketch.exit_previous": "Zur Ansicht davor zurückkehren",
"prefs.sketch.exit_unless_rotated": "Außer bei Drehung",
@@ -2742,9 +2751,9 @@
"render.adaptive_absolute": "Absolute RGB-Fehleruntergrenze",
"render.adaptive_coverage": "Zielwert für den Abdeckungsfehler",
"render.adaptive_enable": "Samples mithilfe unabhängiger Pilot-Samples zuteilen",
"render.adaptive_help": "Nur GPU. Unabhängige Pilot-Samples werden verworfen. Anschließend werden pro Pixel 16 oder mehr Produktions-Samples zugeteilt, bis zu den Samples pro Pixel. Die Zuteilung richtet sich nach der RGB- und Abdeckungsvarianz; seltene Pfade können übersehen werden, und Pixel, die ihr Limit erreicht haben, können verrauscht bleiben. Der Abschlussbericht zählt den tatsächlichen Aufwand. Keine zertifizierte Garantie für ein bestimmtes Rauschniveau oder eine bestimmte Renderzeit.",
"render.adaptive_help": "Unabhängige Pilotsamples werden verworfen und bestimmen dann mindestens 16 Produktionssamples pro Pixel bis zum Wert Samples pro Pixel. CPU-Ressourcengrenzen umfassen beide Phasen. RGB- und Abdeckungsvarianz steuern die Verteilung; Pixel am Limit können verrauscht bleiben. Der Abschlussbericht zählt die tatsächlichen Samples.",
"render.adaptive_minimum": "Minimale Produktions-Samples",
"render.adaptive_pending": "Die adaptive Zuteilung mit unabhängigen GPU-Pilot-Samples und bedingte Fresnel-Schichten sind verfügbar. Zertifizierte Konvergenz, Garantien für seltene Ereignisse und adaptiver Lichttransport auf der CPU bleiben nicht verfügbar.",
"render.adaptive_pending": "Adaptive Verteilung mit unabhängigen Pilotsamples ist für CPU und GPU verfügbar. Bedingte Fresnel-Schichten erfordern die GPU. Die statistische Verteilung bestätigt keine Konvergenz.",
"render.adaptive_pilots": "Pilot-Samples pro Pixel",
"render.adaptive_relative": "Zielwert für den relativen RGB-Fehler",
"render.add_ground": "Add fitted ground to setup",
@@ -2805,7 +2814,7 @@
"render.appearance_editor.replace": "Gespeicherte Optik dieses Körpers ersetzen",
"render.appearance_editor.revalidate": "Diesen Körper erneut prüfen",
"render.appearance_editor.save": "Diese Optik speichern",
"render.appearance_editor.save_reason": "Zum Speichern wird physikalisches Glas auf einem aktuellen GPU-Körper benötigt. Klarlack und lokale Bildtexturen bleiben vorübergehend.",
"render.appearance_editor.save_reason": "Zum Speichern Glas an einem aktuellen CPU- oder GPU-Körper aktivieren. Für Klarlack „Klarlack in Aufnahme speichern“ verwenden. Lokale Bilddateien bleiben temporär.",
"render.appearance_editor.saved": "Mit dieser Aufnahme gespeichert",
"render.appearance_editor.saved_state": "Diese Optik ist mit dieser Aufnahme gespeichert.",
"render.appearance_editor.scatter_reason": "Innere Streuung benötigt einen geschlossenen Körper.",
@@ -2901,7 +2910,7 @@
"render.children.copy_request": "Anfrage-JSON kopieren",
"render.children.coverage_error": "Abdeckungsfehler",
"render.children.cpu": "CPU-Raytracing",
"render.children.denoise": "Rauschen im fertigen Bild reduzieren",
"render.children.denoise": "Bildrauschen reduzieren",
"render.children.depth": "Pfadtiefe",
"render.children.distortion": "Verzeichnung",
"render.children.edge_samples": "Kanten-Samples",
@@ -3033,7 +3042,7 @@
"render.cpu_point_help": "Bis zu acht Punktlichter, zusätzlich zur gewählten Richtungsbeleuchtung. Abstände in mm; der Mindestabstand darf den Referenzabstand nicht überschreiten.",
"render.cpu_primary_samples": "Kanten-Samples pro Pixel",
"render.cpu_progressive": "CPU progressiv",
"render.cpu_progressive_help": "Den vorhandenen CPU-Integrator in Durchgängen mit je einem Primär-Sample akkumulieren. Ausgewählte Reflexionen erfordern ein Sample; Flächenlichter, Dielektrika und Anisotropie erfordern „Einzelbild“.",
"render.cpu_progressive_help": "Neue CPU-Kamerasamples sammeln und jeden vollständigen Durchgang zeigen. Ausgewählte Reflexionen verwenden einen Versuch pro Kamerasample; zertifiziertes Glas und planare Anisotropie behalten ihre bisherigen Grenzen. Adaptive Pilot- und Produktionssamples teilen sich das CPU-Profilbudget.",
"render.cpu_progressive_limits": "Höchstens 1.048.576 Pixel und 16.777.216 Auswertungen von Pixel × Sample. Das Ändern der Samples passt die Bildgröße nie automatisch an.",
"render.cpu_progressive_seed": "Startwert für das CPU-Sampling",
"render.cpu_progressive_spp": "Samples pro Pixel",
@@ -3209,7 +3218,7 @@
"render.export_display": "Export: Anzeige-PNG",
"render.export_image_label": "Bild exportieren",
"render.export_linear": "Export: lineares Roh-EXR",
"render.exr_pending": "Die GPU kann rohe lineare EXR-Dateien behalten. Kompression, mehrschichtige Bilder und zusätzliche Renderpässe bleiben nicht verfügbar.",
"render.exr_pending": "CPU-Raytracing und GPU-Pathtracing können rohe lineare EXR-Dateien behalten. Kompression, mehrschichtige Bilder und zusätzliche Renderpässe bleiben nicht verfügbar.",
"render.film_grain": "Fotografisches Korn",
"render.fit": "Produkt einpassen",
"render.fixed_camera": "Kamera",
@@ -3249,9 +3258,11 @@
"render.frame_wide": "16:9 · Breitbild",
"render.gpu_depth": "Maximale Pfadtiefe",
"render.gpu_options_help": "Samples, Tiefe, Startwert und Belichtung werden mit diesem Setup gespeichert. Es wird nur die aktive Bildmethode gespeichert.",
"render.gpu_path_crashed": "SolidCraft wurde beim letzten Einrichten auf diesem Grafikadapter beendet, wahrscheinlich im Grafiktreiber, und richtet ihn deshalb nicht von selbst erneut ein. Verwenden Sie stattdessen Raytracing auf der CPU oder versuchen Sie es erneut (nach einem Treiberupdate wird es ebenfalls erneut versucht).",
"render.gpu_path_failed": "GPU-Pathtracer nicht verfügbar",
"render.gpu_path_first_setup": "Das erste Setup auf dieser GPU kann mehrere Minuten dauern; SolidCraft bleibt währenddessen bedienbar. Spätere Starts verwenden die kompilierten Shader erneut, sofern der Treiber sie behält.",
"render.gpu_path_preparing": "GPU-Pathtracer: Shader werden kompiliert…",
"render.gpu_path_retry": "Erneut versuchen",
"render.gpu_seed": "Zufallsstartwert",
"render.gpu_unavailable": "GPU-Pathtracing ist auf diesem Gerät nicht bereit.",
"render.ground_apply": "Boden anpassen und speichern",
@@ -3570,7 +3581,7 @@
"render.method_cpu": "CPU-Raytracing",
"render.method_gpu": "GPU-Pathtracing",
"render.method_limit_raster": "Die Rasterung verwendet die feste undurchsichtige Studiovorschau. Wählen Sie ausdrücklich eine Raytracing-Methode für die unterstützten optischen Einstellungen.",
"render.method_raster": "CPU-Rasterung",
"render.method_raster": "CPU-Rasterung (schematisch)",
"render.navigation_help": "Die Kamera bewegt sich wie im Konstruieren (Einstellungen › Navigation). Setup speichern übernimmt die aktuelle Kamera.",
"render.nested_enable": "Zertifizierte verschachtelte Dielektrika",
"render.nested_help": "Bis zu 16 umschließende Schichten; jedes übergeordnete Element muss konvex und jedes untergeordnete strikt darin enthalten sein. Überschneidungen, sich berührende Flächen, dünne Schichten und die Transmission gerader Schattenstrahlen werden abgelehnt. Der Zustand „Kamera im Inneren“ folgt der zertifizierten Hierarchie. Erzeugt keine Flüssigkeitsoberfläche und verschmilzt keine sich berührenden CAD-Grenzflächen.",
@@ -3598,7 +3609,7 @@
"render.output_exr_unavailable": "EXR-Ausgabeeinstellungen sind hier nicht verfügbar. Linearer EXR-Export erfordert ein fertiges GPU-Bild mit gespeichertem linearem Original.",
"render.output_fit": "Einpassen",
"render.output_format": "Format",
"render.output_format_status": "PNG enthält das exakte fertige Anzeigebild. Optionales GPU-EXR bewahrt rohe lineare FLOAT32-Strahldichte vor Filtern. Export ersetzt nie durch Vorschaucache oder anderen Auftrag.",
"render.output_format_status": "PNG enthält das exakte fertige Anzeigebild. Optionales CPU/GPU-EXR bewahrt rohe lineare FLOAT32-Strahldichte vor Anzeigefiltern. Der Export verwendet nie den Vorschaucache oder einen anderen Auftrag.",
"render.output_grading": "Farbkorrektur",
"render.output_image": "Bild",
"render.output_largest": "{width} × {height} max.",
@@ -3683,7 +3694,7 @@
"render.quality_custom": "Benutzerdefiniert",
"render.quality_draft": "Entwurf",
"render.quality_high": "Hoch",
"render.quality_projection_help": "Q-Vorgaben enthalten künftige Anforderungen. Diese Aktion projiziert nur ein unterstütztes PNG-Budget mit festen Samples und lässt adaptives Stoppen, rohe lineare Ausgabe und unabhängige Transmissionslimits ausdrücklich aus.",
"render.quality_projection_help": "Diese Prüfung ändert nur Auflösung, Abtastbudget, Pfadtiefe, Startwert und Rauschreduzierung. Andere Einstellungen bleiben erhalten; das Budget garantiert keine Konvergenz.",
"render.quality_review": "Sampling-Budget prüfen",
"render.queue_pending": "In dieser Oberfläche gibt es keine automatische Produktionswarteschlange und keinen Animations-Renderer. Fertige Einzelbilder bleiben separat exportierbar.",
"render.radial_assign": "Erscheinungsbild zuweisen",
@@ -3714,7 +3725,7 @@
"render.result_navigation": "Verschieben und Zoomen folgen den Navigationseinstellungen. Leertaste + Ziehen verschiebt.",
"render.result_open": "Das fertige Bild in der Hauptansicht öffnen.",
"render.result_preview": "Live-Vorschau",
"render.result_provisional": "Vorläufiges CPU-Bild",
"render.result_provisional": "Vorläufiges Bild",
"render.result_provisional_help": "Dieser Zwischenstand ist nicht exportierbar. Der Export behält das letzte fertige Ergebnis; ein Abbruch kehrt zu diesem Ergebnis zurück.",
"render.result_reduced_display": "Anzeigevorschau auf die Geräte-/Cachegrenze reduziert. PNG und Export behalten die volle Auflösung; native 100%-Ansicht ist hier nicht verfügbar.",
"render.result_retained": "Das zuletzt fertiggestellte Bild wird angezeigt; ein neueres Rendering hat es noch nicht ersetzt.",
@@ -3878,7 +3889,7 @@
"render.stage_panels.prepare_plan": "Erstellungsplan vorbereiten",
"render.stage_panels.projects": "Projiziert",
"render.stage_panels.quality_ack": "Ich bestätige das feste Budget und seine Auslassungen",
"render.stage_panels.quality_omissions": "Adaptives Stoppen und Fehlerziele, separate Transmissionstiefe, Russian-Roulette-Regeln, lineare Rohausgabe, EXR-Master und Konvergenznachweis.",
"render.stage_panels.quality_omissions": "Dieses Budget legt keine adaptiven Fehlerziele, eigenen Transmissionsgrenzen, Russian-Roulette-Regeln oder Speicherung des linearen Masters fest und garantiert keine Konvergenz.",
"render.stage_panels.reference": "Referenz",
"render.stage_panels.references": "Referenzen",
"render.stage_panels.remove_optics": "Optik entfernen",
@@ -3922,7 +3933,7 @@
"render.stage_panels.subtitle_environment": "Originale HDR-Umgebung · gesamte Szene",
"render.stage_panels.subtitle_maps": "Erstellte räumliche Texturen · temporäre Vorschau",
"render.stage_panels.subtitle_physical": "Schreibgeschützter Plan · kein Material erstellt oder zugewiesen",
"render.stage_panels.subtitle_quality": "Festes GPU-Budget · zukünftige Anforderungen ausgelassen",
"render.stage_panels.subtitle_quality": "GPU-Abtastbudget · nur aufgeführte Einstellungen",
"render.stage_panels.subtitle_rig": "Erstellter Lichtaufbau · Lichtquellen und passive Flächen",
"render.stage_panels.subtitle_saved_maps": "Erstellte Texturen · mit dieser Aufnahme gespeichert",
"render.stage_panels.subtitle_stage": "Physischer Boden und Bühne · an das Produkt angepasst",
@@ -3947,7 +3958,7 @@
"render.studio_area_help": "Bis zu vier einseitige Rechtecke. Orthogonale Halbkanten U und V bestimmen die Größe; U × V zeigt zur beleuchteten Seite. Die Koordinaten sind Weltmillimeter.",
"render.studio_center": "Mittelpunkt X, Y, Z (mm)",
"render.studio_denoise": "Rauschen im fertigen Bild reduzieren",
"render.studio_denoise_help": "Abschließender kantenerhaltender Filter; feine Details können weicher werden. Glas und scharfe spiegelnde Oberflächen umgehen den Filter.",
"render.studio_denoise_help": "Kantenerhaltender Filter für progressive und fertige Bilder; feine Details können weicher werden. Glas und scharfe spiegelnde Oberflächen umgehen den Filter.",
"render.studio_direction": "Richtung zur Sonne (X, Y, Z)",
"render.studio_environment": "Umgebung",
"render.studio_environment_bounds": "Die Umgebungsdrehung muss endlich und innerhalb von ±1.000.000 Radiant liegen, die Intensität zwischen 0 und 100.",
@@ -4957,7 +4968,6 @@
"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",
@@ -4994,6 +5004,7 @@
"ui.heading_construct": "KONSTRUKTION",
"ui.heading_create": "ERSTELLEN",
"ui.heading_create_sketch": "SKIZZE ERSTELLEN",
"ui.heading_custom_view_navigator": "EIGENER ANSICHTSNAVIGATOR",
"ui.heading_cylinder": "ZYLINDER",
"ui.heading_design": "DESIGN",
"ui.heading_draft": "ABZIEHEN",
@@ -5258,7 +5269,6 @@
"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",
@@ -5302,6 +5312,8 @@
"ui.repeat_nothing": "Noch nichts zu wiederholen: Starten Sie zuerst ein Werkzeug oder einen Dialog.",
"ui.replace_with_link_done": "Die Kopien sind jetzt Platzierungen des verknüpften Designs.",
"ui.report_a_bug": "Fehler melden",
"ui.report_characters_left_one": "{count} Zeichen übrig",
"ui.report_characters_left_other": "{count} Zeichen übrig",
"ui.reset": "Zurücksetzen",
"ui.reset_all": "Alle zurücksetzen",
"ui.reset_the_settings_on_this_page": "Einstellungen auf dieser Seite zurücksetzen",
@@ -5441,7 +5453,6 @@
"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",
@@ -5584,6 +5595,14 @@
"values must be numbers or expressions": "Werte müssen Zahlen oder Ausdrücke sein",
"view.auto_ortho_faces": "Perspektivisch beim Drehen, orthografisch auf Flächen",
"view.render_inspector": "Render-Inspektor",
"view_gizmo.arrows": "Drehpfeile",
"view_gizmo.home": "Schaltfläche „Ausgangsansicht“",
"view_gizmo.no_such_body": "Diese Konstruktion hat keinen Körper mit diesem Namen.",
"view_gizmo.note": "Wählen Sie einen Körper in der Ansicht. Er wird mit Ihren Einstellungen und Einstellungsprofilen gespeichert und in jeder Konstruktion angezeigt. Klicken Sie auf eine seiner Flächen, um direkt darauf zu blicken.",
"view_gizmo.texture": "Textur",
"view_gizmo.themed": "Design",
"view_gizmo.too_detailed": "Zu detailliert für den Ansichtsnavigator (über 10.000 Dreiecke)",
"view_gizmo.too_thin": "Zu lang und dünn für den Ansichtsnavigator: Die dünnste Seite muss mindestens ein Fünftel der längsten sein",
"web.files.insert_drawings_hint": "CAD-Dateien, Netze, DXF oder SVG in das geöffnete Design",
"web.files.insert_stored_drawings_hint": "In das geöffnete Design (CAD-Dateien, Netze, DXF, SVG)",
"web.open_full": "In SolidCraft öffnen",
+6 -2
View File
@@ -22,8 +22,7 @@
"cmd.component_cycle_color": "Cycle Component Colour",
"cmd.construct_point_center": "Point At Centre Of Circle/Sphere/Torus",
"cmd.inspect_center_of_mass": "Centre of Mass",
"cmd.inspect_colored_components": "Enable Component Colours",
"cmd.inspect_colored_components_disable": "Disable Component Colours",
"cmd.inspect_colored_components": "Component Colours",
"cmd.parameters_favorite": "Favourite Parameter",
"cmd.render_aging_describe": "Layered Surface Aging",
"cmd.render_effects_describe": "Lens and Creative Effects",
@@ -87,6 +86,7 @@
"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.select_hidden_edges_help": "Select edges through faces when Shaded with hidden edges is active, including for Fillet. Off keeps v0.30.1 selection behaviour. Hidden bodies stay unselectable.",
"prefs.sketch.center_arc": "Centre-point arc follows the cursor",
"prefs.sketch.conflict_colour": "Conflict colour",
"prefs.sketch.tracking_guides": "Centre and midpoint guides",
@@ -235,6 +235,10 @@
"render.studio_area_help": "Up to four one-sided rectangles. Orthogonal half-edges U and V define size; U × V faces the lit side. Coordinates are world-space millimetres.",
"render.studio_center": "Centre X, Y, Z (mm)",
"render.studio_hdr_help": "Radiance .hdr, 2:1 panorama, up to 2048 × 1024. EXR is unavailable. The colour assumption applies only when primaries are unspecified.",
"sketch.hint.arc.center_point": "Arc: click centre and start, go round either way and click the end, or type a signed sweep",
"sketch.hint.circle.center": "Circle: click the centre, then a point on the circle",
"sketch.hint.constraint.midpoint": "MidPoint: pick a point and a line, or two lines to centre the second on the first",
"sketch.hint.rectangle.center": "Centre rectangle: click the centre, then a corner",
"sketch_text.align_center": "Centre",
"ui.3d_mouse_kind_cap": "Return to Centre",
"ui.3d_mouse_looks_like_cap": "The reading stays put while you hold it, the way a Return to Centre device does.",
+93 -60
View File
@@ -85,6 +85,7 @@
"Bottom Right": "Bottom Right",
"Boundary": "Boundary",
"Bounding box": "Bounding box",
"Bridge": "Bridge",
"CHECK": "CHECK",
"COMPONENTS": "COMPONENTS",
"CONFIGURATIONS": "CONFIGURATIONS",
@@ -833,8 +834,6 @@
"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",
@@ -934,36 +933,6 @@
"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",
@@ -995,8 +964,7 @@
"cmd.inspect_analysis": "View Analysis",
"cmd.inspect_center_of_mass": "Center of Mass",
"cmd.inspect_clearance": "Clearance",
"cmd.inspect_colored_components": "Enable Component Colors",
"cmd.inspect_colored_components_disable": "Disable Component Colors",
"cmd.inspect_colored_components": "Component Colors",
"cmd.inspect_compare": "Compare Bodies",
"cmd.inspect_compare_picture": "Compare Picture",
"cmd.inspect_continuity": "Continuity Analysis",
@@ -1061,7 +1029,6 @@
"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",
@@ -1205,7 +1172,7 @@
"cmd.sketch_arc_tangent": "Tangent Arc",
"cmd.sketch_arc_three_point": "3-Point Arc",
"cmd.sketch_auto_constrain": "AutoConstrain",
"cmd.sketch_auto_constrain_from_datum": "AutoConstrain from datum",
"cmd.sketch_auto_constrain_from_datum": "AutoConstrain from Datum",
"cmd.sketch_auto_project": "Auto-Project Edges on Reference",
"cmd.sketch_blend_curve": "Blend Curve",
"cmd.sketch_cancel": "Cancel Sketch",
@@ -1219,7 +1186,7 @@
"cmd.sketch_circle_two_point": "2-Point Circle",
"cmd.sketch_circle_two_tangent": "2-Tangent Circle",
"cmd.sketch_constrainer": "Sketch Constrainer",
"cmd.sketch_constraint_fix": "Fix/UnFix",
"cmd.sketch_constraint_fix": "Fix/Unfix",
"cmd.sketch_constraint_midpoint": "MidPoint",
"cmd.sketch_construction": "Normal/Construction",
"cmd.sketch_copy": "Copy Sketch Entities",
@@ -1329,6 +1296,7 @@
"cmd.timeline_stop": "Stop History",
"cmd.timeline_suppress": "Suppress Feature",
"cmd.ui.assistant": "Toggle Assistant",
"cmd.view.navigator": "Custom View Navigator",
"cmd.view.render_browser": "Render Browser",
"cmd.view.render_dock": "Move Render Dock",
"cmd.view.render_focus": "Render Focus View",
@@ -1344,13 +1312,13 @@
"cmd.view_front": "Front View",
"cmd.view_graphics_preset": "Graphics Preset",
"cmd.view_grid": "Grid",
"cmd.view_ground_shadow": "Ground shadow",
"cmd.view_home": "Home view",
"cmd.view_ground_shadow": "Ground Shadow",
"cmd.view_home": "Home View",
"cmd.view_isometric": "Isometric View",
"cmd.view_left": "Left View",
"cmd.view_list": "List Named Views",
"cmd.view_navigate": "Navigate View",
"cmd.view_navigation_bar": "Navigation bar",
"cmd.view_navigation_bar": "Navigation Bar",
"cmd.view_next_tab": "Next Tab",
"cmd.view_normal_to": "Normal to Selection",
"cmd.view_previous_tab": "Previous Tab",
@@ -1359,10 +1327,10 @@
"cmd.view_right": "Right View",
"cmd.view_save": "New Named View",
"cmd.view_shots_strip": "Shots Strip",
"cmd.view_spin": "Continuous orbit",
"cmd.view_tool": "Navigation tool",
"cmd.view_spin": "Continuous Orbit",
"cmd.view_tool": "Navigation Tool",
"cmd.view_top": "Top View",
"cmd.view_visual_style": "Visual style",
"cmd.view_visual_style": "Visual Style",
"color.blue": "Blue",
"color.copy": "Click to copy color values",
"color.edit": "Click to edit color",
@@ -1427,6 +1395,7 @@
"dialog.computing": "Computing… {seconds} s",
"dialog.could_not_be_checked": "could not be checked",
"dialog.custom_axis": "↖ Custom Axis",
"dialog.cylinder_face_tip": "The face or vertex of the model the cylinder stands on: click here, then click it in the view. Left empty, X, Y and Z place it.",
"dialog.dimension_insert_hint": "Click another driving dimension to insert its name",
"dialog.dimension_name": "Dimension: {name}",
"dialog.dimension_step_down": "Down by {step}",
@@ -1435,6 +1404,8 @@
"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.",
"dialog.primitive_point_tip": "A point that fills X, Y and Z: click here, then click a vertex, edge or face of the model, a point of a sketch, or the origin.",
"dialog.reference_edge_tip": "A straight edge of the picked face to measure the distance below from: click here, then click the edge.",
"dialog.select": "↖ Select",
"dialog.selected_count": "↖ {count} selected",
"dialog.selection_constraint": "Constraint {id}",
@@ -1478,6 +1449,9 @@
"each rail's curves must be in one sketch": "each rail's curves must be in one sketch",
"feature computation ended without a result": "feature computation ended without a result",
"file.open.drawing_filter": "Designs, CAD files, meshes and drawings",
"file.open.portal_fallback.no_portal_service": "The file chooser that opened is not the desktop's own: the desktop portal service is not available.",
"file.open.portal_fallback.no_session_bus": "The file chooser that opened is not the desktop's own: there is no D-Bus session bus.",
"file.open.portal_fallback.xwayland": "The file chooser that opened is not the desktop's own: under XWayland the portal refuses the parent window.",
"file_load.built": "Built so far: {built} of {total} features.",
"file_load.converting": "Converting solid {done} of {total}",
"file_load.inserting": "Inserting geometry",
@@ -2068,6 +2042,8 @@
"nav.blender.help": "Middle drag orbits, Shift+middle pans, Ctrl+middle zooms; right drag leaves the camera alone",
"nav.creo.help": "Middle drag orbits, Shift+middle pans, Ctrl+middle drag vertically zooms (down to zoom in); scroll the wheel to zoom",
"nav.creo.label": "Creo",
"nav.drag_keeps_mode.help": "A middle or right drag goes on orbiting, panning or zooming as it began until the button is released, whatever keys are pressed or let go meanwhile. Off: the keys held decide at every moment of the drag.",
"nav.drag_keeps_mode.label": "A drag keeps the navigation it started with",
"nav.free_orbit.help": "Mouse and 3D mouse rotate through both poles and preserve roll. Shift axis snapping is disabled.",
"nav.free_orbit.label": "Free orbit",
"nav.free_orbit.snap_help": "Mouse and 3D mouse rotate through both poles and preserve roll. Hold Shift during an orbit drag to snap the view direction and roll.",
@@ -2304,6 +2280,9 @@
"prefs.auto_operation_note": "Join, New Body or Cut, from what the material touches.",
"prefs.axes": "Axes",
"prefs.axis": "Axis",
"prefs.background_choose": "Choose picture…",
"prefs.background_image": "Picture",
"prefs.background_unreadable": "Could not read the background picture",
"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.",
@@ -2325,6 +2304,8 @@
"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_rows_start_closed": "Browser rows start closed",
"prefs.browser_rows_start_closed_note": "Placements and folders in the Browser start closed, so a design with many components shows its top-level rows. Each opens with its arrow, Expand All or Find in Browser, which opens just the rows down to its body and closes again the ones the last find opened. A body picked in the 3D view leaves the Browser as it is. When off, folders start open, placements start as Components start open says, and a pick in the 3D view opens the Browser down to its body, as before.",
"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.",
"prefs.browser_shown": "Shown",
"prefs.built_in": "Built in",
@@ -2378,7 +2359,6 @@
"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.",
@@ -2457,6 +2437,10 @@
"prefs.invert_curved_roll_arrows_note": "The two curved arrows at the cube's corner roll the view within the screen plane. On: the clockwise arrow rolls counterclockwise and the counterclockwise arrow rolls clockwise. Off: each arrow rolls the view the way it curls.",
"prefs.invert_navigator_arrows": "Invert the view cube rotation arrows",
"prefs.invert_navigator_arrows_note": "The arrows around the cube orbit the camera the way they point, as in Fusion and Inventor: the arrow on the left points right and swings the camera right. Off: an arrow rotates the cube toward the side it sits on, so the left arrow swings the camera left. The curved roll arrows are not affected.",
"prefs.keep_unsaved_also_asks": "Still ask when the tab closes",
"prefs.keep_unsaved_also_asks_tip": "With this off, the tab closes without the browser's box once every unsaved design's copy is current.",
"prefs.keep_unsaved_in_browser": "Keep unsaved work in this browser",
"prefs.keep_unsaved_in_browser_tip": "Each open design's unsaved changes are kept up to date in this browser's private storage. The next visit offers to restore them.",
"prefs.keep_work_on_tab_switch": "Keep dialogs and tools open when switching designs",
"prefs.keep_work_on_tab_switch_note": "Each design tab keeps its open dialog or tool, with what you picked and typed in it, and its toolbar tab. They are there again when you come back to the design. Off: switching designs cancels them and shows the Model tab.",
"prefs.labels": "Labels",
@@ -2588,6 +2572,8 @@
"prefs.section_hatch_by_part": "By part",
"prefs.section_hatch_one": "One style",
"prefs.select": "Select",
"prefs.select_hidden_edges": "Select hidden edges in Shaded with hidden edges",
"prefs.select_hidden_edges_help": "Select edges through faces when Shaded with hidden edges is active, including for Fillet. Off keeps v0.30.1 selection behavior. Hidden bodies stay unselectable.",
"prefs.select_one_item": "Select one item",
"prefs.select_other_hidden": "Select hidden geometry with Select Other",
"prefs.select_other_hidden_help": "On: include hidden bodies, sketches and occluded edges near the pointer. Off: use the original list of visible geometry under the pointer.",
@@ -2612,6 +2598,8 @@
"prefs.sketch.click_selects": "Click selects",
"prefs.sketch.conflict_colour": "Conflict color",
"prefs.sketch.drawing": "Drawing",
"prefs.sketch.ellipse_axes": "Draw ellipses with their axes",
"prefs.sketch.ellipse_axes_note": "The major and minor axes as construction lines, so the half-axes and axes can be dimensioned. Off: the ellipse alone, and D on it gives its minor radius wherever the value is pulled, drawn and labelled R as before.",
"prefs.sketch.exit_keep": "Keep",
"prefs.sketch.exit_previous": "Return to the view before",
"prefs.sketch.exit_unless_rotated": "Unless turned",
@@ -2781,6 +2769,12 @@
"recovery.just_now": "just now",
"recovery.min_ago": "{count} min ago",
"recovery.never_saved": "never saved",
"recovery.open_saved": "Open saved version",
"recovery.restore": "Restore",
"recovery.restore_intro": "Changes that weren't saved when this page last closed are kept in this browser.",
"recovery.restore_row": "{name}, changed {when}",
"recovery.restore_title": "Restore unsaved designs?",
"recovery.restore_title_one": "Restore unsaved design: {name}, changed {when}?",
"refused.change": "Couldn't change {body}.",
"refused.cut": "Couldn't cut {body}.",
"refused.face_curved": "One of its curved faces can't be used for modelling.",
@@ -2795,9 +2789,9 @@
"render.adaptive_absolute": "Absolute RGB error floor",
"render.adaptive_coverage": "Coverage error target",
"render.adaptive_enable": "Allocate samples with independent pilots",
"render.adaptive_help": "GPU only. Independent pilot samples are discarded, then allocate 16 or more production samples per pixel up to Samples per pixel. RGB and coverage variance guide allocation; rare paths may be missed and capped pixels may remain noisy. The final report counts actual work. No certified noise or time guarantee.",
"render.adaptive_help": "Independent pilot samples are discarded, then choose 16 or more production samples per pixel up to Samples per pixel. CPU resource limits include both stages. RGB and coverage variance guide allocation; capped pixels can remain noisy. The final report counts actual samples.",
"render.adaptive_minimum": "Minimum production samples",
"render.adaptive_pending": "GPU independent-pilot adaptive allocation and conditional Fresnel strata are available. Certified convergence, rare-event guarantees and CPU adaptive transport remain unavailable.",
"render.adaptive_pending": "Independent-pilot adaptive allocation is available for CPU and GPU. Conditional Fresnel strata require GPU. Statistical allocation does not certify convergence.",
"render.adaptive_pilots": "Pilot samples per pixel",
"render.adaptive_relative": "Relative RGB error target",
"render.add_ground": "Add fitted ground to setup",
@@ -2858,7 +2852,7 @@
"render.appearance_editor.replace": "Replace this body's saved optics",
"render.appearance_editor.revalidate": "Revalidate this body",
"render.appearance_editor.save": "Save these optics",
"render.appearance_editor.save_reason": "Saving these optics needs physical glass on a current GPU body. Coat and local image maps remain temporary.",
"render.appearance_editor.save_reason": "To save Glass, enable it on a current CPU or GPU body. Use Save coat to shot for clear coat. Local image files remain temporary.",
"render.appearance_editor.saved": "Saved with this shot",
"render.appearance_editor.saved_state": "These optics are saved with this shot.",
"render.appearance_editor.scatter_reason": "Interior scattering needs a closed solid boundary.",
@@ -2954,7 +2948,7 @@
"render.children.copy_request": "Copy request JSON",
"render.children.coverage_error": "Coverage error",
"render.children.cpu": "CPU ray tracing",
"render.children.denoise": "Reduce noise in the finished image",
"render.children.denoise": "Reduce image noise",
"render.children.depth": "Path depth",
"render.children.distortion": "Distortion",
"render.children.edge_samples": "Edge samples",
@@ -3086,7 +3080,7 @@
"render.cpu_point_help": "Up to eight point lights, added to the selected directional lighting. Distances are in mm; the minimum must not exceed the reference distance.",
"render.cpu_primary_samples": "Edge samples per pixel",
"render.cpu_progressive": "CPU progressive",
"render.cpu_progressive_help": "Accumulate the existing CPU integrator in passes of one primary sample. Selected reflections require one sample; area lights, dielectrics and anisotropy require Single image.",
"render.cpu_progressive_help": "Accumulate fresh CPU camera samples and show every complete pass. Selected reflections use one attempt per camera sample; certified glass and planar anisotropy keep their existing limits. Adaptive pilots and production share the CPU profile budget.",
"render.cpu_progressive_limits": "At most 1,048,576 pixels and 16,777,216 pixel × sample evaluations. Changing samples never resizes the image automatically.",
"render.cpu_progressive_seed": "CPU sampling seed",
"render.cpu_progressive_spp": "samples per pixel",
@@ -3262,7 +3256,7 @@
"render.export_display": "Export: display PNG",
"render.export_image_label": "Export Image",
"render.export_linear": "Export: raw linear EXR",
"render.exr_pending": "GPU can retain raw linear EXR. Compression, multilayer images and auxiliary render passes remain unavailable.",
"render.exr_pending": "CPU ray tracing and GPU path tracing can retain raw linear EXR. Compression, multilayer images and auxiliary render passes remain unavailable.",
"render.film_grain": "Photographic grain",
"render.fit": "Fit Product",
"render.fixed_camera": "Camera",
@@ -3302,9 +3296,11 @@
"render.frame_wide": "16:9 · Widescreen",
"render.gpu_depth": "Maximum path depth",
"render.gpu_options_help": "Samples, depth, seed and exposure are saved with this setup. Only the active image method is saved.",
"render.gpu_path_crashed": "SolidCraft closed while setting it up on this graphics adapter last time, most likely inside the graphics driver, so it is not set up again by itself. Choose CPU ray tracing instead, or try again (a driver update also tries again).",
"render.gpu_path_failed": "GPU path tracer unavailable",
"render.gpu_path_first_setup": "The first setup on this GPU can take several minutes; SolidCraft stays usable meanwhile. Later starts reuse the compiled shaders where the driver keeps them.",
"render.gpu_path_preparing": "GPU path tracer: compiling shaders…",
"render.gpu_path_retry": "Try again",
"render.gpu_seed": "Random seed",
"render.gpu_unavailable": "GPU path tracing is not ready on this device.",
"render.ground_apply": "Fit and save ground",
@@ -3623,7 +3619,7 @@
"render.method_cpu": "CPU ray tracing",
"render.method_gpu": "GPU path tracing",
"render.method_limit_raster": "Raster uses the fixed opaque studio preview. Select an explicit raytracing method for supported optical controls.",
"render.method_raster": "CPU raster",
"render.method_raster": "CPU raster (schematic)",
"render.navigation_help": "The camera moves as in Design (Preferences › Navigation). Save Setup records the current camera.",
"render.nested_enable": "Certified nested dielectrics",
"render.nested_help": "Up to 16 containing layers; every parent must be convex and every child strictly inside it. Intersections, touching surfaces, thin sheets and straight-shadow transmission refuse. Camera-inside state follows the certified hierarchy. Does not create a liquid surface or merge touching CAD boundaries.",
@@ -3651,7 +3647,7 @@
"render.output_exr_unavailable": "EXR output setup is unavailable here. Linear EXR export requires a completed GPU image with a retained linear master.",
"render.output_fit": "Fit",
"render.output_format": "Format",
"render.output_format_status": "PNG is the exact completed display image. Optional GPU EXR retains raw scene-linear FLOAT32 radiance before display filters. Export never substitutes the preview cache or a different job.",
"render.output_format_status": "PNG is the exact completed display image. Optional CPU/GPU EXR retains raw scene-linear FLOAT32 radiance before display filters. Export never substitutes the preview cache or a different job.",
"render.output_grading": "Grading",
"render.output_image": "Image",
"render.output_largest": "{width} × {height} max",
@@ -3736,7 +3732,7 @@
"render.quality_custom": "Custom",
"render.quality_draft": "Draft",
"render.quality_high": "High",
"render.quality_projection_help": "Q presets include future requirements. This action projects only a supported fixed-sample PNG budget and explicitly omits adaptive stopping, raw linear output and independent transmission limits.",
"render.quality_projection_help": "This review changes only resolution, sample budget, path depth, seed and noise reduction. Other settings are kept; the budget does not certify convergence.",
"render.quality_review": "Review sampling budget",
"render.queue_pending": "There is no automatic production queue or animation renderer in this UI. Completed still images remain separately exportable.",
"render.radial_assign": "Assign Appearance",
@@ -3767,7 +3763,7 @@
"render.result_navigation": "Pan and zoom use Navigation preferences. Space + drag pans.",
"render.result_open": "Open the completed image in the main canvas.",
"render.result_preview": "Live Preview",
"render.result_provisional": "Provisional CPU image",
"render.result_provisional": "Provisional image",
"render.result_provisional_help": "This checkpoint is not exportable. Export keeps the last completed result; cancellation returns to that result.",
"render.result_reduced_display": "Display preview reduced to the viewport device/cache limit. The retained PNG and export keep full resolution; native 100% inspection is unavailable for this reduced view.",
"render.result_retained": "Showing the last completed image; a newer render has not replaced it.",
@@ -3931,7 +3927,7 @@
"render.stage_panels.prepare_plan": "Prepare creation plan",
"render.stage_panels.projects": "Projects",
"render.stage_panels.quality_ack": "I acknowledge the fixed budget and its omissions",
"render.stage_panels.quality_omissions": "Adaptive stopping and error targets, separate transmission depth, Russian roulette rules, raw linear output, EXR master and convergence certification.",
"render.stage_panels.quality_omissions": "This budget does not set adaptive error targets, independent transmission limits, Russian roulette rules or raw-master retention, and does not guarantee convergence.",
"render.stage_panels.reference": "Reference",
"render.stage_panels.references": "References",
"render.stage_panels.remove_optics": "Remove optics",
@@ -3975,7 +3971,7 @@
"render.stage_panels.subtitle_environment": "Original HDR environment · whole scene",
"render.stage_panels.subtitle_maps": "Authored spatial maps · temporary preview",
"render.stage_panels.subtitle_physical": "Read-only plan · no material created or assigned",
"render.stage_panels.subtitle_quality": "Fixed GPU budget · future requirements omitted",
"render.stage_panels.subtitle_quality": "GPU sampling budget · listed fields only",
"render.stage_panels.subtitle_rig": "Authored light rig · emitters and passive cards",
"render.stage_panels.subtitle_saved_maps": "Authored maps · saved with this shot",
"render.stage_panels.subtitle_stage": "Physical ground and stage · fitted to the product",
@@ -4000,7 +3996,7 @@
"render.studio_area_help": "Up to four one-sided rectangles. Orthogonal half-edges U and V define size; U × V faces the lit side. Coordinates are world-space millimeters.",
"render.studio_center": "Center X, Y, Z (mm)",
"render.studio_denoise": "Reduce noise in the finished image",
"render.studio_denoise_help": "Final edge-aware filter; fine detail can soften. Glass and sharp specular surfaces bypass the filter.",
"render.studio_denoise_help": "Progressive and final images use an edge-aware filter; fine detail may soften. Glass and sharp specular surfaces bypass it.",
"render.studio_direction": "Direction toward sun (X, Y, Z)",
"render.studio_environment": "Environment",
"render.studio_environment_bounds": "Environment rotation must be finite within ±1,000,000 radians and intensity between 0 and 100.",
@@ -4202,6 +4198,32 @@
"sketch.dof_left": "{count} degrees of freedom left",
"sketch.dof_left_one": "{count} degree of freedom left",
"sketch.draw_curves": "Draw as {linetype}",
"sketch.error.closed_curve_two_crossings": "a closed curve needs two crossings to trim",
"sketch.error.nothing_to_break": "nothing to break here",
"sketch.error.offset_fillet_chamfer": "`{id}` is an offset, and its corners follow its source curves: round or cut the source corners before offsetting, or break the offset association first",
"sketch.error.offset_trim": "`{id}` is an offset, and its ends follow its source curves: edit the source curves instead, or break the offset association first",
"sketch.hint.arc.center_point": "Arc: click center and start, go round either way and click the end, or type a signed sweep",
"sketch.hint.arc.three_point": "Arc: click start, end, then a point on the arc",
"sketch.hint.arc3d": "3D Arc: click start, end, then a point on the arc (points may leave the sketch plane)",
"sketch.hint.break": "Break: click a curve where it should split",
"sketch.hint.circle.center": "Circle: click the center, then a point on the circle",
"sketch.hint.constraint.generic": "Constraint: pick the sketch entities",
"sketch.hint.constraint.horizontal_vertical": "Horizontal/Vertical: pick a line, two points, or a point and a line; hold Shift to pick a line midpoint",
"sketch.hint.constraint.midpoint": "MidPoint: pick a point and a line, or two lines to center the second on the first",
"sketch.hint.dimension": "Dimension: pick one or two entities and type the value (or click to place it first); click a dimension to edit it; Enter applies",
"sketch.hint.extend": "Extend: click a curve near the end to extend (Shift: click two curves to extend both to where they meet)",
"sketch.hint.fallback": "Click in the sketch",
"sketch.hint.line": "Line: click points; right-click or Esc to finish",
"sketch.hint.line3d": "3D Line: click world points; right-click or Esc to finish (points may leave the sketch plane)",
"sketch.hint.open_done": "Open done",
"sketch.hint.point3d": "3D Point: click where a 3D point goes",
"sketch.hint.rectangle.center": "Center rectangle: click the center, then a corner",
"sketch.hint.rectangle.rounded": "Rounded rectangle: click two opposite corners (Tab to the radius box to type the corner radius)",
"sketch.hint.rectangle.three_point": "3-point rectangle: click two points of one edge, then the width",
"sketch.hint.rectangle.two_point": "Rectangle: click two opposite corners",
"sketch.hint.slot": "Slot: click both ends, then the width",
"sketch.hint.spline3d": "3D Spline: click fit points in 3D; right-click or Esc to finish",
"sketch.hint.trim": "Trim: click the piece of a curve to remove, or drag across the pieces to remove",
"sketch.make_selection": "Convert the selection to {linetype}",
"sketch.open_ends": "Open {count}",
"sketch.status_conflict": "constraints conflict",
@@ -4423,6 +4445,7 @@
"ui.3d_sketch": "3D Sketch",
"ui.activate": "Activate",
"ui.active_component": "Active component",
"ui.addToQuickAccess": "Add to Quick Access",
"ui.add_configuration": "Add configuration",
"ui.add_parameter": "Add parameter",
"ui.agent_chat": "Agent chat",
@@ -5015,7 +5038,6 @@
"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",
@@ -5052,6 +5074,7 @@
"ui.heading_construct": "CONSTRUCT",
"ui.heading_create": "CREATE",
"ui.heading_create_sketch": "CREATE SKETCH",
"ui.heading_custom_view_navigator": "CUSTOM VIEW NAVIGATOR",
"ui.heading_cylinder": "CYLINDER",
"ui.heading_design": "DESIGN",
"ui.heading_draft": "DRAFT",
@@ -5320,7 +5343,6 @@
"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",
@@ -5339,6 +5361,7 @@
"ui.quality": "Quality",
"ui.quantity": "Quantity",
"ui.quantity_2": "Quantity 2",
"ui.quick_access_full": "The quick-access bar holds {count} commands; remove one to add another",
"ui.quit": "Quit",
"ui.radial_context_menu": "Radial context menu",
"ui.radius": "Radius",
@@ -5356,6 +5379,7 @@
"ui.relink_not_linked": "This component is not linked to a source design.",
"ui.relink_select_one": "Select one linked component or one of its instances first.",
"ui.relink_source_all": "Relink Source (All Instances)…",
"ui.removeFromQuickAccess": "Remove from Quick Access",
"ui.removeFromToolbar": "Remove from Toolbar",
"ui.removeSection": "Remove Section",
"ui.render": "Render",
@@ -5364,6 +5388,8 @@
"ui.repeat_nothing": "Nothing to repeat yet: start a tool or dialog first.",
"ui.replace_with_link_done": "The copies are now placements of the linked design.",
"ui.report_a_bug": "Report a bug",
"ui.report_characters_left_one": "{count} character left",
"ui.report_characters_left_other": "{count} characters left",
"ui.reset": "Reset",
"ui.reset_all": "Reset All",
"ui.reset_the_settings_on_this_page": "Reset the settings on this page",
@@ -5506,7 +5532,6 @@
"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",
@@ -5649,6 +5674,14 @@
"values must be numbers or expressions": "values must be numbers or expressions",
"view.auto_ortho_faces": "Perspective while orbiting, ortho on faces",
"view.render_inspector": "Render Inspector",
"view_gizmo.arrows": "Rotation arrows",
"view_gizmo.home": "Home button",
"view_gizmo.no_such_body": "This design has no body of that name.",
"view_gizmo.note": "Pick a body in the view. It is kept with your preferences and settings profiles, and shown in every design. Click one of its faces to look straight at it.",
"view_gizmo.texture": "Texture",
"view_gizmo.themed": "Themed",
"view_gizmo.too_detailed": "Too detailed for the view navigator (over 10,000 triangles)",
"view_gizmo.too_thin": "Too long and thin for the view navigator: its thinnest side must be at least a fifth of its longest",
"web.files.insert_drawings_hint": "CAD files, meshes, DXF or SVG into the open design",
"web.files.insert_stored_drawings_hint": "Into the open design (CAD files, meshes, DXF, SVG)",
"web.open_full": "Open in SolidCraft",
+794 -3
View File
File diff suppressed because it is too large Load Diff
+822 -2
View File
File diff suppressed because it is too large Load Diff
+5 -2
View File
@@ -956,8 +956,6 @@
"cmd.inspect_accessibility": "Analyse d'accessibilité",
"cmd.inspect_center_of_mass": "Centre de gravité",
"cmd.inspect_clearance": "Jeu",
"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é",
@@ -1365,11 +1363,14 @@
"dialog.computing": "Calcul… {seconds} s",
"dialog.could_not_be_checked": "non vérifiable",
"dialog.custom_axis": "↖ Axe personnalisé",
"dialog.cylinder_face_tip": "La face ou le sommet du modèle sur lequel le cylindre est posé : cliquer ici, puis dessus dans la vue. Laissé vide, X, Y et Z le placent.",
"dialog.dimension_insert_hint": "Double-cliquez sur une autre cote pilotante pour insérer son nom",
"dialog.dimension_name": "Cote : {name}",
"dialog.edit_title": "MODIFIER {title}",
"dialog.no_shown_body": "Aucun corps affiché à modifier : affichez-en un ou choisissez Nouveau corps",
"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.primitive_point_tip": "Un point qui remplit X, Y et Z : cliquer ici, puis sur un sommet, une arête ou une face du modèle, sur un point d'une esquisse ou sur l'origine.",
"dialog.reference_edge_tip": "Une arête droite de la face choisie, depuis laquelle la distance ci-dessous est mesurée : cliquer ici, puis sur l'arête.",
"dialog.select": "↖ Sélectionner",
"dialog.selected_count": "↖ {count} sélectionné(s)",
"dialog.selection_constraint": "Contrainte {id}",
@@ -3001,9 +3002,11 @@
"render.frame_wide": "16:9 · Écran large",
"render.gpu_depth": "Profondeur maximale des chemins",
"render.gpu_options_help": "Les échantillons, la profondeur, la graine et l'exposition sont enregistrés avec ce préréglage. Seule la méthode d'image active est enregistrée.",
"render.gpu_path_crashed": "SolidCraft s’est fermé pendant sa dernière préparation sur cet adaptateur graphique, très probablement dans le pilote graphique ; il n’est donc plus préparé de lui-même. Utilisez le lancer de rayons sur le CPU, ou réessayez (une mise à jour du pilote réessaie aussi).",
"render.gpu_path_failed": "Traceur de chemins GPU indisponible",
"render.gpu_path_first_setup": "La première initialisation sur ce GPU peut prendre plusieurs minutes ; SolidCraft reste utilisable pendant ce temps. Les démarrages suivants réutilisent les shaders compilés lorsque le pilote les conserve.",
"render.gpu_path_preparing": "Traceur de chemins GPU : compilation des shaders…",
"render.gpu_path_retry": "Réessayer",
"render.gpu_seed": "Graine aléatoire",
"render.gpu_unavailable": "Le tracé de chemins GPU n'est pas prêt sur cet appareil.",
"render.ground_apply": "Ajuster et enregistrer le sol",
-2
View File
@@ -957,8 +957,6 @@
"cmd.inspect_accessibility": "Análise de accesibilidade",
"cmd.inspect_center_of_mass": "Centro de masas",
"cmd.inspect_clearance": "Folga",
"cmd.inspect_colored_components": "Activar as cores dos compoñentes",
"cmd.inspect_colored_components_disable": "Desactivar as cores dos compoñentes",
"cmd.inspect_compare": "Comparar corpos",
"cmd.inspect_compare_picture": "Comparar a imaxe",
"cmd.inspect_continuity": "Análise de continuidade",
+85 -15
View File
@@ -85,6 +85,7 @@
"Bottom Right": "למטה מימין",
"Boundary": "גבול",
"Bounding box": "תיבה תוחמת",
"Bridge": "גשר",
"CHECK": "בדיקה",
"COMPONENTS": "רכיבים",
"CONFIGURATIONS": "תצורות",
@@ -963,8 +964,7 @@
"cmd.inspect_analysis": "ניתוח תצוגה",
"cmd.inspect_center_of_mass": "מרכז מסה",
"cmd.inspect_clearance": "מרווח",
"cmd.inspect_colored_components": "הפעלת צבעי רכיבים",
"cmd.inspect_colored_components_disable": "ביטול צבעי רכיבים",
"cmd.inspect_colored_components": "צבעי רכיבים",
"cmd.inspect_compare": "השוואת גופים",
"cmd.inspect_compare_picture": "השוואה לתמונת ייחוס",
"cmd.inspect_continuity": "ניתוח רציפות",
@@ -1296,6 +1296,7 @@
"cmd.timeline_stop": "עצירת ציר הזמן",
"cmd.timeline_suppress": "השבתת פיצ'ר",
"cmd.ui.assistant": "הצגה או הסתרה של העוזר",
"cmd.view.navigator": "ניווט תצוגה מותאם אישית",
"cmd.view.render_browser": "דפדפן רינדור",
"cmd.view.render_dock": "הזז את עגינת הרינדור",
"cmd.view.render_focus": "תצוגת מיקוד רינדור",
@@ -1394,6 +1395,7 @@
"dialog.computing": "מחשב… {seconds} שנ'",
"dialog.could_not_be_checked": "לא ניתן היה לבדוק",
"dialog.custom_axis": "↖ ציר מותאם",
"dialog.cylinder_face_tip": "הפאה או הקודקוד של המודל שעליהם הגליל ניצב: יש ללחוץ כאן ואז עליהם בתצוגה. אם נשאר ריק, X, Y ו-Z ממקמים אותו.",
"dialog.dimension_insert_hint": "יש ללחוץ על מידה מניעה אחרת כדי להוסיף את שמה",
"dialog.dimension_name": "מידה: {name}",
"dialog.dimension_step_down": "הפחתה ב־{step}",
@@ -1402,6 +1404,8 @@
"dialog.edit_title": "עריכת {title}",
"dialog.no_shown_body": "אין גוף מוצג לשינוי: הציגו גוף, או בחרו „גוף חדש”",
"dialog.pattern_sketch_not_object": "בחרו גופים או פיצ'רים: סקיצה אינה אף אחד מהם. חזרו על העקומות שלה בעזרת המערך או השיקוף של הסקיצה עצמה, או בצעו לה שליפה קודם.",
"dialog.primitive_point_tip": "נקודה שממלאת את X, Y ו-Z: יש ללחוץ כאן ואז על קודקוד, מקצוע או פאה של המודל, על נקודה של סקיצה, או על הראשית.",
"dialog.reference_edge_tip": "מקצוע ישר של הפאה שנבחרה שממנו נמדד המרחק שלמטה: יש ללחוץ כאן ואז על המקצוע.",
"dialog.select": "↖ בחירה",
"dialog.selected_count": "↖ {count} נבחרו",
"dialog.selection_constraint": "אילוץ {id}",
@@ -1445,6 +1449,9 @@
"each rail's curves must be in one sketch": "עקומות כל מסילה חייבות להיות בסקיצה אחת",
"feature computation ended without a result": "חישוב הפיצ'ר הסתיים ללא תוצאה",
"file.open.drawing_filter": "תכנים, קובצי CAD, רשתות משולשים ושרטוטים",
"file.open.portal_fallback.no_portal_service": "בורר הקבצים שנפתח אינו של שולחן העבודה: שירות בחירת הקבצים של שולחן העבודה אינו זמין.",
"file.open.portal_fallback.no_session_bus": "בורר הקבצים שנפתח אינו של שולחן העבודה: אין אוטובוס הפעלה של D-Bus.",
"file.open.portal_fallback.xwayland": "בורר הקבצים שנפתח אינו של שולחן העבודה: תחת XWayland הפורטל דוחה את חלון האב.",
"file_load.built": "נבנו עד כה: {built} מתוך {total} פיצ'רים.",
"file_load.converting": "המרת גוף מוצק {done} מתוך {total}",
"file_load.inserting": "הוספת גאומטריה",
@@ -2035,6 +2042,8 @@
"nav.blender.help": "גרירה אמצעית מסובבת, Shift וגרירה אמצעית מזיזים, Ctrl וגרירה אמצעית מבצעים זום; גרירה ימנית לא מזיזה את המצלמה",
"nav.creo.help": "גרירה בלחצן האמצעי מסובבת את התצוגה, Shift והלחצן האמצעי מזיזים אותה, Ctrl וגרירה אנכית בלחצן האמצעי משנים את הזום (גרירה מטה להגדלה); גלילת הגלגלת משנה גם היא את הזום",
"nav.creo.label": "Creo",
"nav.drag_keeps_mode.help": "גרירה בכפתור האמצעי או הימני ממשיכה לסובב, להזיז או לשנות זום כפי שהתחילה עד שמשחררים את הכפתור, בלי קשר למקשים שנלחצים או משתחררים בינתיים. כבוי: המקשים הלחוצים קובעים בכל רגע של הגרירה.",
"nav.drag_keeps_mode.label": "גרירה שומרת על הניווט שבו התחילה",
"nav.free_orbit.help": "העכבר והעכבר התלת־ממדי מסובבים דרך שני הקטבים ושומרים על הגלגול. הצמדת הצירים עם Shift מושבתת.",
"nav.free_orbit.label": "סיבוב חופשי",
"nav.free_orbit.snap_help": "העכבר והעכבר התלת־ממדי מסובבים דרך שני הקטבים ושומרים על הגלגול. יש להחזיק את Shift בזמן גרירת סיבוב כדי להצמיד את כיוון התצוגה ואת הגלגול.",
@@ -2271,6 +2280,9 @@
"prefs.auto_operation_note": "איחוד, גוף חדש או חיתוך, לפי מה שהחומר נוגע בו.",
"prefs.axes": "צירים",
"prefs.axis": "ציר",
"prefs.background_choose": "בחירת תמונה…",
"prefs.background_image": "תמונה",
"prefs.background_unreadable": "לא ניתן לקרוא את תמונת הרקע",
"prefs.blur_cost": "עלות עיבוד יחסית. יותר פסים מציינים בדרך כלל יותר עבודה.",
"prefs.blur_device": "לא זמין בהתקן הגרפי או בנתיב העיבוד הזה",
"prefs.blur_guard": "איכות הטשטוש הופחתה כדי לשמור על תגובתיות הפריימים. הרדיוס לא השתנה.",
@@ -2292,6 +2304,8 @@
"prefs.browser_eye_left": "סמל העין ליד חץ הקיפול",
"prefs.browser_eye_left_note": "שורות עץ התכן מציגות את סמל העין ליד חץ הקיפול, כמו קודם, במקום בעמודה בקצה הימני של השורות.",
"prefs.browser_reveal_on_hover": "הצגה בריחוף",
"prefs.browser_rows_start_closed": "שורות עץ התכן מוצגות סגורות",
"prefs.browser_rows_start_closed_note": "מיקומים ותיקיות בעץ התכן מוצגים סגורים, כך שתכן עם רכיבים רבים מציג את השורות העליונות שלו. כל שורה נפתחת בחץ שלה, ב„הרחבת הכול” או ב„איתור בעץ התכן”, שפותח רק את השורות שמובילות לגוף שלו וסוגר שוב את אלה שהאיתור הקודם פתח. גוף שנבחר בתצוגה התלת־ממדית משאיר את עץ התכן כפי שהוא. כשהאפשרות כבויה התיקיות מוצגות פתוחות, המיקומים מוצגים כפי שקובעת האפשרות „רכיבים פתוחים מההתחלה”, ובחירה בתצוגה התלת־ממדית פותחת את עץ התכן עד לגוף שלה, כמו קודם.",
"prefs.browser_show_note": "״מוצג״ משאיר את הדפדפן על המסך. ״הצגה בריחוף״ מסתיר אותו עד שהמצביע מגיע למקומו או ללשונית הדקה בקצהו השמאלי, והוא נשאר פתוח כל עוד המצביע בתוכו או שתפריט ממנו פתוח.",
"prefs.browser_shown": "מוצג",
"prefs.built_in": "מובנה",
@@ -2423,6 +2437,10 @@
"prefs.invert_curved_roll_arrows_note": "שני החצים המעוגלים בפינת הקובייה מסובבים את התצוגה במישור המסך. פועל: החץ עם כיוון השעון מסובב נגד כיוון השעון, והחץ נגד כיוון השעון מסובב עם כיוון השעון. כבוי: כל חץ מסובב את התצוגה לפי העקום שלו.",
"prefs.invert_navigator_arrows": "היפוך חצי סיבוב קוביית התצוגה",
"prefs.invert_navigator_arrows_note": "החצים סביב הקובייה מחוויים את המצלמה לכיוון שהחץ מצביע, כמו ב-Fusion ו-Inventor: החץ שמשמאל מצביע ימימה ומניע את המצלמה ימימה. כבוי: החץ מסובב את הקובייה אל הצד שבו הוא יושב, כך שחץ שמאל מניע את המצלמה שמאלה. חצי הגלילה המעוגלים אינם מושפעים.",
"prefs.keep_unsaved_also_asks": "לשאול גם כשהכרטיסייה נסגרת",
"prefs.keep_unsaved_also_asks_tip": "כשאפשרות זו כבויה, הכרטיסייה נסגרת ללא חלון הדפדפן לאחר שהעותק של כל תכן שלא נשמר עודכן.",
"prefs.keep_unsaved_in_browser": "לשמור עבודה שלא נשמרה בדפדפן זה",
"prefs.keep_unsaved_in_browser_tip": "השינויים שלא נשמרו בכל תכן פתוח נשמרים מעודכנים באחסון הפרטי של דפדפן זה. בביקור הבא תוצע שחזורם.",
"prefs.keep_work_on_tab_switch": "השארת תיבות דו־שיח וכלים פתוחים במעבר בין עיצובים",
"prefs.keep_work_on_tab_switch_note": "כל לשונית עיצוב שומרת את תיבת הדו־שיח או הכלי הפתוחים בה, עם מה שבחרת והקלדת בהם, ואת לשונית סרגל הכלים שלה. הם מחכים לך כשתחזור לעיצוב. כבוי: מעבר בין עיצובים מבטל אותם ומציג את הלשונית מודל.",
"prefs.labels": "תוויות",
@@ -2554,6 +2572,8 @@
"prefs.section_hatch_by_part": "לפי חלק",
"prefs.section_hatch_one": "סגנון אחד",
"prefs.select": "בחירה",
"prefs.select_hidden_edges": "בחירת קצוות מוסתרים בתצוגה מוצללת עם קצוות מוסתרים",
"prefs.select_hidden_edges_help": "מאפשר לבחור קצוות דרך פאות בתצוגה מוצללת עם קצוות מוסתרים, גם עבור Fillet. כיבוי שומר על אופן הבחירה של v0.30.1. גופים שהוסתרו עדיין אינם ניתנים לבחירה.",
"prefs.select_one_item": "בחירת פריט אחד",
"prefs.select_other_hidden": "בחירת גאומטריה מוסתרת באמצעות בחירה אחרת",
"prefs.select_other_hidden_help": "מופעל: כולל גופים, סקיצות וקצוות מוסתרים בקרבת הסמן. כבוי: שימוש ברשימה המקורית של גאומטריה גלויה מתחת לסמן.",
@@ -2578,6 +2598,8 @@
"prefs.sketch.click_selects": "לחיצה בוחרת",
"prefs.sketch.conflict_colour": "צבע התנגשות",
"prefs.sketch.drawing": "שרטוט",
"prefs.sketch.ellipse_axes": "שרטוט אליפסות עם הצירים שלהן",
"prefs.sketch.ellipse_axes_note": "הציר הראשי והציר המשני כקווי עזר, כדי שאפשר יהיה לתת מידות לחצאי הצירים ולצירים. כבוי: האליפסה בלבד, ו-D עליה נותן את הרדיוס המשני שלה בכל מקום שאליו נגרר הערך, משורטט ומסומן R כמו קודם.",
"prefs.sketch.exit_keep": "שמירה",
"prefs.sketch.exit_previous": "חזרה לתצוגה הקודמת",
"prefs.sketch.exit_unless_rotated": "אלא אם סובבה",
@@ -2747,6 +2769,12 @@
"recovery.just_now": "זה עתה",
"recovery.min_ago": "לפני {count} דק'",
"recovery.never_saved": "לא נשמר מעולם",
"recovery.open_saved": "פתיחת הגרסה השמורה",
"recovery.restore": "שחזור",
"recovery.restore_intro": "שינויים שלא נשמרו כשדף זה נסגר לאחרונה שמורים בדפדפן זה.",
"recovery.restore_row": "{name}, שונה {when}",
"recovery.restore_title": "לשחזר תכנים שלא נשמרו?",
"recovery.restore_title_one": "לשחזר תכן שלא נשמר: {name}, שונה {when}?",
"refused.change": "לא ניתן היה לשנות את {body}.",
"refused.cut": "לא ניתן היה לחתוך את {body}.",
"refused.face_curved": "אחת הפאות המעוקלות שלו אינה ניתנת לשימוש במידול.",
@@ -2761,9 +2789,9 @@
"render.adaptive_absolute": "רצפת שגיאת RGB מוחלטת",
"render.adaptive_coverage": "יעד שגיאת כיסוי",
"render.adaptive_enable": "הקצאת דגימות באמצעות דגימות גישוש עצמאיות",
"render.adaptive_help": "GPU בלבד. דגימות הגישוש העצמאיות מושלכות, ואז מוקצות 16 דגימות הפקה או יותר לכל פיקסל עד לערך „דגימות לכל פיקסל”. השונות של RGB ושל הכיסוי מכוונת את ההקצאה; מסלולים נדירים עלולים להיות מוחמצים ופיקסלים שהגיעו לתקרה עשויים להישאר רועשים. הדוח הסופי סופר את העבודה בפועל. אין ערובה מאושרת לרמת הרעש או לזמן.",
"render.adaptive_help": "דגימות החלוץ העצמאיות נזרקות, ואז נקבעות לכל פיקסל לפחות 16 דגימות הפקה, עד לערך דגימות לפיקסל. מגבלות משאבי CPU כוללות את שני השלבים. השונות ב-RGB ובכיסוי מנחה את ההקצאה; פיקסלים שהגיעו לתקרה עשויים להישאר רועשים. הדוח הסופי מונה את הדגימות בפועל.",
"render.adaptive_minimum": "מינימום דגימות הפקה",
"render.adaptive_pending": "הקצאה אדפטיבית ב־GPU עם דגימות גישוש עצמאיות ושכבות פרנל מותנות זמינות. התכנסות מאושרת, ערובה לאירועים נדירים והעברה אדפטיבית ב־CPU עדיין אינן זמינות.",
"render.adaptive_pending": "הקצאה מסתגלת עם דגימות חלוץ עצמאיות זמינה ב-CPU וב-GPU. שכבות פרנל מותנות דורשות GPU. ההקצאה הסטטיסטית אינה מבטיחה התכנסות.",
"render.adaptive_pilots": "דגימות גישוש לכל פיקסל",
"render.adaptive_relative": "יעד שגיאת RGB יחסית",
"render.add_ground": "הוספת קרקע מותאמת להגדרה",
@@ -2824,7 +2852,7 @@
"render.appearance_editor.replace": "החלף את האופטיקה השמורה של גוף זה",
"render.appearance_editor.revalidate": "אמת מחדש גוף זה",
"render.appearance_editor.save": "שמור אופטיקה זו",
"render.appearance_editor.save_reason": "שמירת האופטיקה הזו דורשת זכוכית פיזיקלית בגוף GPU עדכני. הציפוי ומפות התמונה המקומיות נשארים זמניים.",
"render.appearance_editor.save_reason": "כדי לשמור זכוכית, יש להפעיל אותה בגוף CPU או GPU עדכני. לציפוי שקוף יש להשתמש בשמירת הציפוי בתצלום. קובצי תמונה מקומיים נשארים זמניים.",
"render.appearance_editor.saved": "נשמר עם הצילום הזה",
"render.appearance_editor.saved_state": "האופטיקה הזו נשמרת עם הצילום הזה.",
"render.appearance_editor.scatter_reason": "פיזור פנימי דורש גבול של גוף מלא סגור.",
@@ -2920,7 +2948,7 @@
"render.children.copy_request": "העתק JSON של הבקשה",
"render.children.coverage_error": "שגיאת כיסוי",
"render.children.cpu": "מעקב קרניים במעבד",
"render.children.denoise": "הפחת רעש בתמונה המוגמרת",
"render.children.denoise": "הפחתת רעש בתמונה",
"render.children.depth": "עומק נתיב",
"render.children.distortion": "עיוות",
"render.children.edge_samples": "דגימות קצה",
@@ -3052,7 +3080,7 @@
"render.cpu_point_help": "עד שמונה אורות נקודתיים, המתווספים לתאורה הכיוונית שנבחרה. המרחקים במילימטרים; המינימום אינו יכול לעלות על מרחק הייחוס.",
"render.cpu_primary_samples": "דגימות קצה לכל פיקסל",
"render.cpu_progressive": "CPU הדרגתי",
"render.cpu_progressive_help": "צבירה של אינטגרטור ה־CPU הקיים במעברים של דגימה ראשית אחת. החזרות שנבחרו דורשות דגימה אחת; אורות שטח, דיאלקטריים ואנאיזוטרופיה דורשים תמונה בודדת.",
"render.cpu_progressive_help": "נאספות דגימות מצלמת CPU חדשות ומוצג כל מעבר שהושלם. השתקפויות נבחרות משתמשות בניסיון אחד לכל דגימת מצלמה; זכוכית מאושרת ואנאיזוטרופיה מישורית שומרות על מגבלותיהן הקיימות. דגימות החלוץ וההפקה המסתגלות חולקות את תקציב פרופיל CPU.",
"render.cpu_progressive_limits": "לכל היותר 1,048,576 פיקסלים ו־16,777,216 הערכות פיקסל × דגימה. שינוי הדגימות לעולם אינו משנה את גודל התמונה אוטומטית.",
"render.cpu_progressive_seed": "זרע דגימה ב־CPU",
"render.cpu_progressive_spp": "דגימות לכל פיקסל",
@@ -3228,7 +3256,7 @@
"render.export_display": "ייצוא: PNG לתצוגה",
"render.export_image_label": "ייצוא תמונה",
"render.export_linear": "ייצוא: EXR ליניארי גולמי",
"render.exr_pending": "GPU יכול לשמור EXR ליניארי גולמי. דחיסה, תמונות רב־שכבתיות ומעברי הדמיה נלווים עדיין אינם זמינים.",
"render.exr_pending": "עקיבת קרניים ב־CPU ועקיבת נתיבים ב־GPU יכולות לשמור EXR ליניארי גולמי. דחיסה, תמונות רב־שכבתיות ומעברי הדמיה נלווים עדיין אינם זמינים.",
"render.film_grain": "גרעיניות צילומית",
"render.fit": "התאמת המוצר לתצוגה",
"render.fixed_camera": "מצלמה",
@@ -3268,9 +3296,11 @@
"render.frame_wide": "16:9 · רחב",
"render.gpu_depth": "עומק מסלול מרבי",
"render.gpu_options_help": "הדגימות, העומק, הזרע והחשיפה נשמרים עם ההגדרה הזאת. נשמרת רק שיטת התמונה הפעילה.",
"render.gpu_path_crashed": "SolidCraft נסגר בזמן ההכנה על מתאם גרפי זה בפעם הקודמת, ככל הנראה בתוך מנהל ההתקן הגרפי, ולכן ההכנה לא תתבצע שוב מעצמה. יש להשתמש במעקב קרניים על המעבד במקום זאת, או לנסות שוב (עדכון מנהל ההתקן גם מנסה שוב).",
"render.gpu_path_failed": "מעקב המסלולים ב־GPU אינו זמין",
"render.gpu_path_first_setup": "ההגדרה הראשונה ב־GPU הזה עשויה להימשך כמה דקות; SolidCraft נשאר זמין לשימוש בינתיים. הפעלות מאוחרות יותר משתמשות שוב בשיידרים שהודרו, כשמנהל ההתקן שומר אותן.",
"render.gpu_path_preparing": "מעקב מסלולים ב־GPU: הידור שיידרים…",
"render.gpu_path_retry": "לנסות שוב",
"render.gpu_seed": "זרע אקראי",
"render.gpu_unavailable": "מעקב מסלולים ב־GPU אינו מוכן במכשיר הזה.",
"render.ground_apply": "התאמת הקרקע ושמירתה",
@@ -3589,7 +3619,7 @@
"render.method_cpu": "מעקב קרניים ב־CPU",
"render.method_gpu": "מעקב מסלולים ב־GPU",
"render.method_limit_raster": "רסטר משתמש בתצוגה המקדימה הקבועה של הסטודיו האטום. לפקדים האופטיים הנתמכים יש לבחור שיטת מעקב קרניים מפורשת.",
"render.method_raster": "רסטר ב־CPU",
"render.method_raster": "רסטר ב־CPU (סכמטי)",
"render.navigation_help": "המצלמה נעה כמו בתכן (העדפות › ניווט). שמירת ההגדרה מתעדת את המצלמה הנוכחית.",
"render.nested_enable": "דיאלקטריים מקוננים מאושרים",
"render.nested_help": "עד 16 שכבות מכילות; כל הורה חייב להיות קמור וכל צאצא חייב להיות בתוכו לגמרי. חיתוכים, משטחים נוגעים, יריעות דקות והעברת צל ישרה נדחים. מצב מצלמה בפנים עוקב אחר ההיררכיה המאושרת. אינו יוצר משטח נוזל ואינו ממזג גבולות CAD נוגעים.",
@@ -3617,7 +3647,7 @@
"render.output_exr_unavailable": "הגדרת פלט EXR אינה זמינה כאן. ייצוא EXR לינארי דורש תמונת GPU שהושלמה עם מאסטר לינארי שנשמר.",
"render.output_fit": "התאם",
"render.output_format": "פורמט",
"render.output_format_status": "PNG הוא תמונת התצוגה הגמורה המדויקת. EXR אופציונלי ב־GPU שומר קרינה גולמית ליניארית של הסצנה ב־FLOAT32 לפני מסנני התצוגה. הייצוא לעולם אינו מחליף אותה במטמון התצוגה המקדימה או במשימה אחרת.",
"render.output_format_status": "PNG הוא תמונת התצוגה הגמורה המדויקת. EXR אופציונלי ב־CPU או ב־GPU שומר קרינה גולמית ליניארית של הסצנה ב־FLOAT32 לפני מסנני התצוגה. הייצוא לעולם אינו מחליף אותה במטמון התצוגה המקדימה או במשימה אחרת.",
"render.output_grading": "גריידינג",
"render.output_image": "תמונה",
"render.output_largest": "מקס׳ {width} × {height}",
@@ -3702,7 +3732,7 @@
"render.quality_custom": "מותאם",
"render.quality_draft": "טיוטה",
"render.quality_high": "גבוהה",
"render.quality_projection_help": "פרופילי Q מוכנים כוללים דרישות עתידיות. פעולה זו משליכה רק תקציב PNG נתמך בדגימות קבועות, ומשמיטה במפורש עצירה אדפטיבית, פלט ליניארי גולמי ומגבלות העברה עצמאיות.",
"render.quality_projection_help": "בדיקה זו משנה רק רזולוציה, תקציב דגימות, עומק נתיב, זרע והפחתת רעש. ההגדרות האחרות נשמרות; התקציב אינו מעיד על התכנסות התמונה.",
"render.quality_review": "סקירת תקציב הדגימה",
"render.queue_pending": "בממשק זה אין תור הפקה אוטומטי ואין מנוע הדמיה לאנימציה. תמונות סטילס גמורות נשארות ניתנות לייצוא כל אחת בנפרד.",
"render.radial_assign": "הקצה מראה",
@@ -3733,7 +3763,7 @@
"render.result_navigation": "הזזה וזום משתמשים בהעדפות הניווט. רווח + גרירה מזיזים.",
"render.result_open": "פתיחת התמונה הגמורה בקנבס הראשי.",
"render.result_preview": "תצוגה מקדימה חיה",
"render.result_provisional": "תמונת CPU זמנית",
"render.result_provisional": "תמונה זמנית",
"render.result_provisional_help": "נקודת ביקורת זו אינה ניתנת לייצוא. הייצוא שומר את התוצאה הגמורה האחרונה; ביטול חוזר לתוצאה הזאת.",
"render.result_reduced_display": "התצוגה המקדימה הוקטנה למגבלת המכשיר או המטמון של אזור התצוגה. ה־PNG השמור והייצוא שומרים על רזולוציה מלאה; בדיקה ברזולוציה המקורית (100%) אינה זמינה בתצוגה המוקטנת הזאת.",
"render.result_retained": "מוצגת התמונה הגמורה האחרונה; הדמיה חדשה יותר לא החליפה אותה.",
@@ -3897,7 +3927,7 @@
"render.stage_panels.prepare_plan": "הכנת תוכנית יצירה",
"render.stage_panels.projects": "מקרין",
"render.stage_panels.quality_ack": "ידוע לי על התקציב הקבוע והפרטים המושמטים בו",
"render.stage_panels.quality_omissions": "עצירה מסתגלת ויעדי שגיאה, עומק העברה נפרד, כללי רולטה רוסית, פלט ליניארי גולמי, קובץ מקור EXR ואישור התכנסות.",
"render.stage_panels.quality_omissions": "תקציב זה אינו קובע יעדי שגיאה אדפטיביים, מגבלות מעבר אור נפרדות, כללי רולטה רוסית או שמירת מקור גולמי, ואינו מבטיח התכנסות של התמונה.",
"render.stage_panels.reference": "ייחוס",
"render.stage_panels.references": "ייחוסים",
"render.stage_panels.remove_optics": "הסרת האופטיקה",
@@ -3941,7 +3971,7 @@
"render.stage_panels.subtitle_environment": "סביבת HDR מקורית · הסצנה כולה",
"render.stage_panels.subtitle_maps": "מפות מרחביות מעוצבות · תצוגה מקדימה זמנית",
"render.stage_panels.subtitle_physical": "תוכנית לקריאה בלבד · לא נוצר או משויך חומר",
"render.stage_panels.subtitle_quality": "תקציב GPU קבוע · דרישות עתידיות מושמטות",
"render.stage_panels.subtitle_quality": "תקציב דגימה ב־GPU · השדות המפורטים בלבד",
"render.stage_panels.subtitle_rig": "מערך תאורה מעוצב · פולטי אור וכרטיסים סבילים",
"render.stage_panels.subtitle_saved_maps": "מפות מעוצבות · נשמרות עם צילום זה",
"render.stage_panels.subtitle_stage": "קרקע ובמה פיזיות · מותאמות למוצר",
@@ -3966,7 +3996,7 @@
"render.studio_area_help": "עד ארבעה מלבנים חד־צדדיים. חצאי הצלעות הניצבים U ו־V קובעים את הגודל; U × V פונה אל הצד המואר. הקואורדינטות במילימטרים של מרחב העולם.",
"render.studio_center": "מרכז X, Y, Z (mm)",
"render.studio_denoise": "הפחתת רעש בתמונה הסופית",
"render.studio_denoise_help": "מסנן סופי שמתחשב בקצוות; פרטים עדינים עלולים להתרכך. זכוכית ומשטחים ספקולריים חדים עוקפים את המסנן.",
"render.studio_denoise_help": "תמונות הדרגתיות וסופיות משתמשות במסנן שומר קצוות; פרטים עדינים עלולים להתרכך. זכוכית ומשטחים בעלי החזר חד אינם עוברים סינון.",
"render.studio_direction": "כיוון אל השמש (X, Y, Z)",
"render.studio_environment": "סביבה",
"render.studio_environment_bounds": "סיבוב הסביבה חייב להיות סופי בטווח ±1,000,000 רדיאנים והעוצמה בין 0 ל־100.",
@@ -4168,6 +4198,32 @@
"sketch.dof_left": "נותרו {count} דרגות חופש",
"sketch.dof_left_one": "נותרה {count} דרגת חופש",
"sketch.draw_curves": "שרטוט כ־{linetype}",
"sketch.error.closed_curve_two_crossings": "a closed curve needs two crossings to trim",
"sketch.error.nothing_to_break": "nothing to break here",
"sketch.error.offset_fillet_chamfer": "`{id}` is an offset, and its corners follow its source curves: round or cut the source corners before offsetting, or break the offset association first",
"sketch.error.offset_trim": "`{id}` is an offset, and its ends follow its source curves: edit the source curves instead, or break the offset association first",
"sketch.hint.arc.center_point": "Arc: click center and start, go round either way and click the end, or type a signed sweep",
"sketch.hint.arc.three_point": "Arc: click start, end, then a point on the arc",
"sketch.hint.arc3d": "3D Arc: click start, end, then a point on the arc (points may leave the sketch plane)",
"sketch.hint.break": "Break: click a curve where it should split",
"sketch.hint.circle.center": "Circle: click the center, then a point on the circle",
"sketch.hint.constraint.generic": "Constraint: pick the sketch entities",
"sketch.hint.constraint.horizontal_vertical": "Horizontal/Vertical: pick a line, two points, or a point and a line; hold Shift to pick a line midpoint",
"sketch.hint.constraint.midpoint": "MidPoint: pick a point and a line, or two lines to center the second on the first",
"sketch.hint.dimension": "Dimension: pick one or two entities and type the value (or click to place it first); click a dimension to edit it; Enter applies",
"sketch.hint.extend": "Extend: click a curve near the end to extend (Shift: click two curves to extend both to where they meet)",
"sketch.hint.fallback": "Click in the sketch",
"sketch.hint.line": "Line: click points; right-click or Esc to finish",
"sketch.hint.line3d": "3D Line: click world points; right-click or Esc to finish (points may leave the sketch plane)",
"sketch.hint.open_done": "Open done",
"sketch.hint.point3d": "3D Point: click where a 3D point goes",
"sketch.hint.rectangle.center": "Center rectangle: click the center, then a corner",
"sketch.hint.rectangle.rounded": "Rounded rectangle: click two opposite corners (Tab to the radius box to type the corner radius)",
"sketch.hint.rectangle.three_point": "3-point rectangle: click two points of one edge, then the width",
"sketch.hint.rectangle.two_point": "Rectangle: click two opposite corners",
"sketch.hint.slot": "Slot: click both ends, then the width",
"sketch.hint.spline3d": "3D Spline: click fit points in 3D; right-click or Esc to finish",
"sketch.hint.trim": "Trim: click the piece of a curve to remove, or drag across the pieces to remove",
"sketch.make_selection": "המרת הבחירה ל־{linetype}",
"sketch.open_ends": "קצוות פתוחים: {count}",
"sketch.status_conflict": "אילוצים סותרים",
@@ -4389,6 +4445,7 @@
"ui.3d_sketch": "סקיצה תלת־ממדית",
"ui.activate": "הפעלה",
"ui.active_component": "רכיב פעיל",
"ui.addToQuickAccess": "הוספה לגישה מהירה",
"ui.add_configuration": "הוספת תצורה",
"ui.add_parameter": "הוספת פרמטר",
"ui.agent_chat": "צ'אט עם סוכן",
@@ -5017,6 +5074,7 @@
"ui.heading_construct": "עזר",
"ui.heading_create": "יצירה",
"ui.heading_create_sketch": "יצירת סקיצה",
"ui.heading_custom_view_navigator": "ניווט תצוגה מותאם אישית",
"ui.heading_cylinder": "גליל",
"ui.heading_design": "תכן",
"ui.heading_draft": "שיפוע יציקה",
@@ -5303,6 +5361,7 @@
"ui.quality": "איכות",
"ui.quantity": "כמות",
"ui.quantity_2": "כמות 2",
"ui.quick_access_full": "סרגל הגישה המהירה מכיל {count} פקודות; יש להסיר אחת כדי להוסיף אחרת",
"ui.quit": "יציאה",
"ui.radial_context_menu": "תפריט הקשר מעגלי",
"ui.radius": "רדיוס",
@@ -5320,6 +5379,7 @@
"ui.relink_not_linked": "רכיב זה אינו מקושר לתכן מקור.",
"ui.relink_select_one": "יש לבחור תחילה רכיב מקושר אחד או אחד המופעים שלו.",
"ui.relink_source_all": "קישור מחדש למקור (כל המופעים)…",
"ui.removeFromQuickAccess": "הסרה מהגישה המהירה",
"ui.removeFromToolbar": "הסרה מסרגל הכלים",
"ui.removeSection": "הסרת מקטע",
"ui.render": "הדמיה",
@@ -5328,6 +5388,8 @@
"ui.repeat_nothing": "אין עדיין מה לחזור עליו: התחל קודם כלי או תיבת דו־שיח.",
"ui.replace_with_link_done": "העותקים הם עכשיו מיקומים של התכן המקושר.",
"ui.report_a_bug": "דיווח על באג",
"ui.report_characters_left_one": "תווים שנותרו: {count}",
"ui.report_characters_left_other": "תווים שנותרו: {count}",
"ui.reset": "איפוס",
"ui.reset_all": "איפוס הכול",
"ui.reset_the_settings_on_this_page": "איפוס ההגדרות בדף זה",
@@ -5612,6 +5674,14 @@
"values must be numbers or expressions": "הערכים חייבים להיות מספרים או ביטויים",
"view.auto_ortho_faces": "פרספקטיבה בזמן סיבוב, אורתוגרפי על פאות",
"view.render_inspector": "מפקח רינדור",
"view_gizmo.arrows": "חיצי סיבוב",
"view_gizmo.home": "לחצן תצוגת בית",
"view_gizmo.no_such_body": "אין בעיצוב הזה גוף בשם הזה.",
"view_gizmo.note": "בחרו גוף בתצוגה. הוא נשמר עם ההעדפות ופרופילי ההגדרות שלכם ומוצג בכל עיצוב. לחצו על אחת מפאותיו כדי להביט בה ישירות.",
"view_gizmo.texture": "מרקם",
"view_gizmo.themed": "לפי ערכת הנושא",
"view_gizmo.too_detailed": "מפורט מדי לניווט התצוגה (יותר מ-10,000 משולשים)",
"view_gizmo.too_thin": "ארוך ודק מדי לניווט התצוגה: הצד הדק ביותר חייב להיות לפחות חמישית מהצד הארוך ביותר",
"web.files.insert_drawings_hint": "קובצי CAD, רשתות משולשים, DXF או SVG אל התכן הפתוח",
"web.files.insert_stored_drawings_hint": "אל התכן הפתוח (קובצי CAD, רשתות משולשים, DXF, SVG)",
"web.open_full": "פתיחה ב־SolidCraft",
-2
View File
@@ -957,8 +957,6 @@
"cmd.inspect_accessibility": "Analiza dostupnosti",
"cmd.inspect_center_of_mass": "Središte mase",
"cmd.inspect_clearance": "Provjera zazora",
"cmd.inspect_colored_components": "Omogući boje komponenti",
"cmd.inspect_colored_components_disable": "Onemogući boje komponenti",
"cmd.inspect_compare": "Usporedi tijela",
"cmd.inspect_compare_picture": "Usporedi sliku",
"cmd.inspect_continuity": "Analiza kontinuiteta",
-2
View File
@@ -957,8 +957,6 @@
"cmd.inspect_accessibility": "Hozzáférhetőség-elemzés",
"cmd.inspect_center_of_mass": "Tömegközéppont",
"cmd.inspect_clearance": "Hézag",
"cmd.inspect_colored_components": "Komponensszínek bekapcsolása",
"cmd.inspect_colored_components_disable": "Komponensszínek kikapcsolása",
"cmd.inspect_compare": "Testek összehasonlítása",
"cmd.inspect_compare_picture": "Kép összehasonlítása",
"cmd.inspect_continuity": "Folytonosságelemzés",
-2
View File
@@ -957,8 +957,6 @@
"cmd.inspect_accessibility": "Analisis Aksesibilitas",
"cmd.inspect_center_of_mass": "Pusat Massa",
"cmd.inspect_clearance": "Kelonggaran",
"cmd.inspect_colored_components": "Aktifkan Warna Komponen",
"cmd.inspect_colored_components_disable": "Nonaktifkan Warna Komponen",
"cmd.inspect_compare": "Bandingkan Bodi",
"cmd.inspect_compare_picture": "Bandingkan dengan Gambar",
"cmd.inspect_continuity": "Analisis Kontinuitas",
+3 -2
View File
@@ -957,8 +957,6 @@
"cmd.inspect_accessibility": "Analisi di accessibilità",
"cmd.inspect_center_of_mass": "Centro di massa",
"cmd.inspect_clearance": "Distanza libera",
"cmd.inspect_colored_components": "Attiva colori dei componenti",
"cmd.inspect_colored_components_disable": "Disattiva colori dei componenti",
"cmd.inspect_compare": "Confronta corpi",
"cmd.inspect_compare_picture": "Confronta immagine",
"cmd.inspect_continuity": "Analisi di continuità",
@@ -1366,11 +1364,14 @@
"dialog.computing": "Calcolo in corso… {seconds} s",
"dialog.could_not_be_checked": "impossibile verificare",
"dialog.custom_axis": "↖ Asse personalizzato",
"dialog.cylinder_face_tip": "La faccia o il vertice del modello su cui poggia il cilindro: fai clic qui, poi su di esso nella vista. Se resta vuoto, lo posizionano X, Y e Z.",
"dialog.dimension_insert_hint": "Fai doppio clic su un’altra quota di controllo per inserirne il nome",
"dialog.dimension_name": "Quota: {name}",
"dialog.edit_title": "MODIFICA {title}",
"dialog.no_shown_body": "Nessun corpo visibile da modificare: mostrane uno o scegli Nuovo corpo",
"dialog.pattern_sketch_not_object": "Seleziona corpi o lavorazioni: uno schizzo non è nessuno dei due. Ripeti le sue curve con Ripetizione o Specchia dello schizzo stesso, oppure estrudilo prima.",
"dialog.primitive_point_tip": "Un punto che compila X, Y e Z: fai clic qui, poi su un vertice, uno spigolo o una faccia del modello, su un punto di uno schizzo oppure sull'origine.",
"dialog.reference_edge_tip": "Uno spigolo rettilineo della faccia scelta da cui si misura la distanza sotto: fai clic qui, poi sullo spigolo.",
"dialog.select": "↖ Seleziona",
"dialog.selected_count": "↖ Selezionati: {count}",
"dialog.selection_constraint": "Vincolo {id}",
+85 -52
View File
@@ -85,6 +85,7 @@
"Bottom Right": "右下",
"Boundary": "境界",
"Bounding box": "境界ボックス",
"Bridge": "ブリッジ",
"CHECK": "チェック",
"COMPONENTS": "コンポーネント",
"CONFIGURATIONS": "構成",
@@ -833,8 +834,6 @@
"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": "ボディの名前を変更",
@@ -934,36 +933,6 @@
"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": "チェックポイントを復元",
@@ -995,8 +964,7 @@
"cmd.inspect_analysis": "表示解析",
"cmd.inspect_center_of_mass": "重心",
"cmd.inspect_clearance": "クリアランス",
"cmd.inspect_colored_components": "コンポーネントの色分けを有効化",
"cmd.inspect_colored_components_disable": "コンポーネントの色分けを無効化",
"cmd.inspect_colored_components": "コンポーネントの色分け",
"cmd.inspect_compare": "ボディを比較",
"cmd.inspect_compare_picture": "参考画像と比較",
"cmd.inspect_continuity": "連続性解析",
@@ -1061,7 +1029,6 @@
"cmd.motion_trace_edit": "モーション トレースを編集",
"cmd.motion_trace_keep": "モーション トレースを保持",
"cmd.occurrence_align": "オカレンスを整列",
"cmd.occurrence_bom_type": "部品表の種類",
"cmd.occurrence_copy": "貼り付け(インスタンス)",
"cmd.occurrence_drag": "オカレンスをドラッグ",
"cmd.occurrence_ground": "空間固定",
@@ -1329,6 +1296,7 @@
"cmd.timeline_stop": "履歴を停止",
"cmd.timeline_suppress": "フィーチャーを抑制",
"cmd.ui.assistant": "アシスタントの表示切り替え",
"cmd.view.navigator": "カスタム ビューナビゲーター",
"cmd.view.render_browser": "Renderブラウザー",
"cmd.view.render_dock": "レンダードックを移動",
"cmd.view.render_focus": "Renderフォーカス表示",
@@ -1427,6 +1395,7 @@
"dialog.computing": "計算中… {seconds} 秒",
"dialog.could_not_be_checked": "チェックできません",
"dialog.custom_axis": "↖ カスタム軸",
"dialog.cylinder_face_tip": "円柱が立つモデルの面または頂点: ここをクリックしてから、ビューでそれをクリック。空のままなら X、Y、Z で配置します。",
"dialog.dimension_insert_hint": "別の駆動寸法をクリックして名前を挿入",
"dialog.dimension_name": "寸法: {name}",
"dialog.dimension_step_down": "{step} 減らす",
@@ -1435,6 +1404,8 @@
"dialog.edit_title": "{title}を編集",
"dialog.no_shown_body": "変更できる表示中のボディがありません。ボディを表示するか、新規ボディを選択してください",
"dialog.pattern_sketch_not_object": "ボディかフィーチャーを選んでください。スケッチはどちらでもありません。曲線はスケッチ内のパターンかミラーで繰り返すか、先に押し出してください。",
"dialog.primitive_point_tip": "X、Y、Z を埋める点: ここをクリックしてから、モデルの頂点、エッジ、面、スケッチの点、または原点をクリック。",
"dialog.reference_edge_tip": "下の距離を測る基準となる、選んだ面の直線エッジ: ここをクリックしてからエッジをクリック。",
"dialog.select": "↖ 選択",
"dialog.selected_count": "↖ {count} 個選択中",
"dialog.selection_constraint": "拘束 {id}",
@@ -1478,6 +1449,9 @@
"each rail's curves must be in one sketch": "各レールの曲線は1つのスケッチ内にある必要があります",
"feature computation ended without a result": "フィーチャーの計算が結果なしで終了しました",
"file.open.drawing_filter": "デザイン、CADファイル、メッシュ、図面",
"file.open.portal_fallback.no_portal_service": "開いたファイル選択はデスクトップのものではありません。デスクトップのファイル選択サービスが利用できません。",
"file.open.portal_fallback.no_session_bus": "開いたファイル選択はデスクトップのものではありません。D-Bus セッションがありません。",
"file.open.portal_fallback.xwayland": "開いたファイル選択はデスクトップのものではありません。XWayland ではポータルが親ウィンドウを拒否します。",
"file_load.built": "構築済み: 全{total}フィーチャー中{built}件。",
"file_load.converting": "ソリッドを変換中: {done}/{total}",
"file_load.inserting": "ジオメトリを挿入中",
@@ -2068,6 +2042,8 @@
"nav.blender.help": "中ドラッグで回転、Shift+中ドラッグでパン、Ctrl+中ドラッグでズーム。右ドラッグではカメラは動きません。",
"nav.creo.help": "マウスの中ボタンドラッグで回転、Shift+中ボタンで平行移動、Ctrl+中ボタンの上下ドラッグでズーム(下方向で拡大)。ホイールの回転でもズーム",
"nav.creo.label": "Creo",
"nav.drag_keeps_mode.help": "中ボタンまたは右ボタンのドラッグは、途中でキーを押したり離したりしても、ボタンを離すまで開始時の回転・パン・ズームを続けます。オフにすると、ドラッグ中の各時点で押しているキーによって決まります。",
"nav.drag_keeps_mode.label": "ドラッグは開始時のナビゲーションを維持",
"nav.free_orbit.help": "マウスと3Dマウスで、極点を越えて回転し、ロールを保ちます。\nShiftによる軸スナップは無効になります。",
"nav.free_orbit.label": "自由回転",
"nav.free_orbit.snap_help": "マウスと3Dマウスで両極を越えて回転でき、ロールも保持されます。オービットのドラッグ中にShiftを押すと、視線方向とロールがスナップします。",
@@ -2304,6 +2280,9 @@
"prefs.auto_operation_note": "材料が接触する対象に応じて結合、新規ボディ、切り取りを選びます。",
"prefs.axes": "軸",
"prefs.axis": "軸",
"prefs.background_choose": "画像を選択…",
"prefs.background_image": "画像",
"prefs.background_unreadable": "背景画像を読み込めませんでした",
"prefs.blur_cost": "相対的な描画負荷です。バーが多いほど、通常は処理量が増えます。",
"prefs.blur_device": "このグラフィックスデバイスまたは描画経路では利用できません",
"prefs.blur_guard": "画面の応答性を保つため、ぼかしの品質を下げました。半径は変更されていません。",
@@ -2325,6 +2304,8 @@
"prefs.browser_eye_left": "折りたたみ矢印の隣の目のアイコン",
"prefs.browser_eye_left_note": "ブラウザーの各行の表示切り替えを、行の右端の列ではなく、以前のように折りたたみ矢印の隣に表示します。",
"prefs.browser_reveal_on_hover": "ホバーで表示",
"prefs.browser_rows_start_closed": "ブラウザーの行を閉じた状態で始める",
"prefs.browser_rows_start_closed_note": "ブラウザーの配置とフォルダーを閉じた状態で表示するため、多数のコンポーネントを含むデザインでは最上位の行だけが表示されます。各行は矢印、「すべて展開」または「ブラウザで検索」で開きます。「ブラウザで検索」はボディまでの行だけを開き、前回の検索で開いた行を再び閉じます。3Dビューでボディを選んでもブラウザーはそのままです。オフのときはフォルダーが開いた状態で始まり、配置は「コンポーネントを最初から展開」の設定に従い、3Dビューで選ぶとブラウザーがそのボディまで開きます(以前と同じ)。",
"prefs.browser_show_note": "「表示」はブラウザーを常に画面に表示します。「ホバーで表示」はポインターがその位置か左端の細いタブに来るまで隠し、ポインターが中にある間やそのメニューが開いている間は表示したままにします。",
"prefs.browser_shown": "表示",
"prefs.built_in": "組み込み",
@@ -2378,7 +2359,6 @@
"prefs.dimension_values_view": "ビュー内",
"prefs.dimension_values_window": "ウィンドウ",
"prefs.display": "表示",
"prefs.drawings": "図面",
"prefs.edges": "エッジと陰影",
"prefs.extend": "拡張",
"prefs.extra_navigation": "何もない場所でCtrl+左ドラッグすると回転し、Spaceを押しながらドラッグすると移動します。",
@@ -2457,6 +2437,10 @@
"prefs.invert_curved_roll_arrows_note": "キューブの隅にある2つのカーブした矢印は、画面内でビューをロールします。オン: 時計回りの矢印が反時計回りに、反時計回りの矢印が時計回りにロールします。オフ: 各矢印は、そのカーブの向きにビューをロールします。",
"prefs.invert_navigator_arrows": "ビューキューブの回転矢印を反転",
"prefs.invert_navigator_arrows_note": "キューブの周りの矢印が、矢印の指す方向にカメラを回します(Fusion や Inventor と同じ動き):左の矢印は右を指し、カメラを右に振ります。オフ: 矢印は、そのある面の方へキューブを回すため、左の矢印はカメラを左に振ります。カーブしたロール矢印は影響を受けません。",
"prefs.keep_unsaved_also_asks": "タブを閉じるときにも確認する",
"prefs.keep_unsaved_also_asks_tip": "オフにすると、未保存の各デザインのコピーが最新であれば、ブラウザーの確認なしでタブが閉じます。",
"prefs.keep_unsaved_in_browser": "未保存の作業をこのブラウザーに保持する",
"prefs.keep_unsaved_in_browser_tip": "開いている各デザインの未保存の変更を、このブラウザーのプライベートストレージに常に最新の状態で保持します。次回のアクセス時に復元を提案します。",
"prefs.keep_work_on_tab_switch": "デザインを切り替えてもダイアログとツールを開いたままにする",
"prefs.keep_work_on_tab_switch_note": "各デザインのタブが、開いているダイアログまたはツール(選択や入力した内容を含む)とツールバーのタブを保持します。デザインに戻ると元のまま表示されます。オフ: デザインを切り替えるとそれらはキャンセルされ、モデル タブが表示されます。",
"prefs.labels": "ラベル",
@@ -2588,6 +2572,8 @@
"prefs.section_hatch_by_part": "部品ごと",
"prefs.section_hatch_one": "単一スタイル",
"prefs.select": "選択",
"prefs.select_hidden_edges": "隠線表示で隠れた辺を選択する",
"prefs.select_hidden_edges_help": "「Shaded with hidden edges」で面の向こうの辺を選択し、Filletにも使用できます。オフではv0.30.1相当の選択動作になります。非表示のボディは選択対象になりません。",
"prefs.select_one_item": "項目を 1 つ選択",
"prefs.select_other_hidden": "「他を選択」で非表示のジオメトリを選択",
"prefs.select_other_hidden_help": "オン: ポインター付近の非表示のボディやスケッチ、遮られたエッジを含めます。オフ: ポインター直下の表示中のジオメトリだけを元のリストに表示します。",
@@ -2612,6 +2598,8 @@
"prefs.sketch.click_selects": "クリックで選択",
"prefs.sketch.conflict_colour": "競合色",
"prefs.sketch.drawing": "作図",
"prefs.sketch.ellipse_axes": "楕円を軸と一緒に描く",
"prefs.sketch.ellipse_axes_note": "長軸と短軸をコンストラクション線として描き、半軸と軸に寸法を付けられるようにします。オフ: 楕円のみを描き、D で短半径を寸法にします。値をどこへ引いても、これまでどおり R 付きで描かれます。",
"prefs.sketch.exit_keep": "維持",
"prefs.sketch.exit_previous": "開始前のビューに戻る",
"prefs.sketch.exit_unless_rotated": "回転した場合を除く",
@@ -2781,6 +2769,12 @@
"recovery.just_now": "たった今",
"recovery.min_ago": "{count}分前",
"recovery.never_saved": "未保存",
"recovery.open_saved": "保存済みのバージョンを開く",
"recovery.restore": "復元",
"recovery.restore_intro": "このページを前回閉じたときに保存されていなかった変更は、このブラウザーに保持されています。",
"recovery.restore_row": "{name}、{when}に変更",
"recovery.restore_title": "未保存のデザインを復元しますか?",
"recovery.restore_title_one": "未保存のデザイン {name}({when}に変更)を復元しますか?",
"refused.change": "{body} を変更できませんでした。",
"refused.cut": "{body} をカットできませんでした。",
"refused.face_curved": "曲面の 1 つがモデリングに使えません。",
@@ -2795,9 +2789,9 @@
"render.adaptive_absolute": "RGB 絶対誤差の下限",
"render.adaptive_coverage": "被覆率の誤差目標",
"render.adaptive_enable": "独立した予備サンプルで配分",
"render.adaptive_help": "GPU 専用。独立した予備サンプルは破棄され、各ピクセルに最小16から設定上限までの本サンプルを配分します。RGB と被覆率の分散を目安にしますが、まれな経路を見逃したり上限でノイズが残ったりします。最終レポートは実際の処理量を示します。ノイズや時間を保証しません。",
"render.adaptive_help": "独立したパイロットサンプルは破棄され、ピクセルごとに16以上、「ピクセルあたりのサンプル数」以下の本番サンプル数を決めます。CPUのリソース上限には両段階が含まれます。RGBとカバレッジの分散から配分し、上限に達したピクセルにはノイズが残ることがあります。最終レポートには実際のサンプル数を記録します。",
"render.adaptive_minimum": "最小本サンプル数",
"render.adaptive_pending": "GPU の独立予備サンプルによる適応配分と条件付きフレネル層化が利用できます。収束の認証、まれな事象の保証、CPU 適応輸送は未対応です。",
"render.adaptive_pending": "独立したパイロットによる適応配分はCPUとGPUで利用できます。条件付きフレネル層にはGPUが必要です。統計的な配分は収束を保証しません。",
"render.adaptive_pilots": "ピクセルごとの予備サンプル",
"render.adaptive_relative": "RGB 相対誤差の目標",
"render.add_ground": "フィットした床をセットアップに追加",
@@ -2858,7 +2852,7 @@
"render.appearance_editor.replace": "このボディの保存済み光学設定を置換",
"render.appearance_editor.revalidate": "このボディを再検証",
"render.appearance_editor.save": "この光学設定を保存",
"render.appearance_editor.save_reason": "保存には現在のGPUボディの物理ガラスが必要です。コートとローカル画像マップは一時設定のままです。",
"render.appearance_editor.save_reason": "ガラスを保存するには、現在のCPUまたはGPUボディで有効にしてください。クリアコートは「コートをショットに保存」を使用します。ローカル画像ファイルは一時設定のままです。",
"render.appearance_editor.saved": "このショットに保存",
"render.appearance_editor.saved_state": "この光学設定はこのショットに保存されています。",
"render.appearance_editor.scatter_reason": "内部散乱には閉じたソリッド境界が必要です。",
@@ -2954,7 +2948,7 @@
"render.children.copy_request": "リクエストJSONをコピー",
"render.children.coverage_error": "カバレッジ誤差",
"render.children.cpu": "CPUレイトレーシング",
"render.children.denoise": "完成画像のノイズを低減",
"render.children.denoise": "画像のノイズを低減",
"render.children.depth": "パス深度",
"render.children.distortion": "歪み",
"render.children.edge_samples": "エッジサンプル",
@@ -3086,7 +3080,7 @@
"render.cpu_point_help": "選択した平行光に最大8個の点光源を追加できます。距離はmmで、最小距離は基準距離以下にしてください。",
"render.cpu_primary_samples": "輪郭サンプル数/ピクセル",
"render.cpu_progressive": "CPUプログレッシブ",
"render.cpu_progressive_help": "既存のCPU積分器を、一次サンプル1個ずつのパスで累積します。選択した反射は1サンプルが必要です。面光源、誘電体、異方性には固定画像を使用してください。",
"render.cpu_progressive_help": "新しいCPUカメラサンプルを蓄積し、完了したすべてのパスを表示します。選択した反射はカメラサンプルごとに1回評価します。認証済みガラスと平面異方性には従来の制限が適用されます。適応サンプリングのパイロットと本番は同じCPUプロファイル予算を使います。",
"render.cpu_progressive_limits": "最大1,048,576ピクセル、ピクセル×サンプルの合計は16,777,216以下です。サンプル数を変えても画像サイズは自動変更されません。",
"render.cpu_progressive_seed": "CPUサンプリングシード",
"render.cpu_progressive_spp": "サンプル/ピクセル",
@@ -3262,7 +3256,7 @@
"render.export_display": "書き出し:表示PNG",
"render.export_image_label": "画像を書き出す",
"render.export_linear": "書き出し:生の線形EXR",
"render.exr_pending": "GPUでは線形EXRを保持できます。圧縮、複数レイヤー、補助パスは未実装です。",
"render.exr_pending": "CPUレイトレーシングとGPUパストレーシングでは生の線形EXRを保持できます。圧縮、複数レイヤー、補助パスは未実装です。",
"render.film_grain": "写真粒子",
"render.fit": "製品を画面に合わせる",
"render.fixed_camera": "カメラ",
@@ -3302,9 +3296,11 @@
"render.frame_wide": "16:9 · ワイドスクリーン",
"render.gpu_depth": "最大パス深度",
"render.gpu_options_help": "サンプル数、深度、シード、露出をこの設定に保存します。保存するのは選択中の画像方式だけです。",
"render.gpu_path_crashed": "前回このグラフィックアダプターでの準備中に SolidCraft が終了しました。グラフィックドライバー内で終了した可能性が高いため、自動では再度準備しません。代わりに CPU レイトレーシングを使うか、もう一度試してください(ドライバーを更新した場合も再試行されます)。",
"render.gpu_path_failed": "GPUパストレーサーを利用できません",
"render.gpu_path_first_setup": "このGPUでの初回セットアップには数分かかることがあります。その間もSolidCraftは使用できます。以降の起動では、ドライバーが保持していればコンパイル済みのシェーダーを再利用します。",
"render.gpu_path_preparing": "GPUパストレーサー:シェーダーをコンパイル中…",
"render.gpu_path_retry": "もう一度試す",
"render.gpu_seed": "乱数シード",
"render.gpu_unavailable": "このデバイスでは GPU パストレーシングの準備ができていません。",
"render.ground_apply": "床をフィットして保存",
@@ -3623,7 +3619,7 @@
"render.method_cpu": "CPU レイトレーシング",
"render.method_gpu": "GPU パストレーシング",
"render.method_limit_raster": "ラスターは固定の不透明スタジオプレビューを使用します。対応する光学設定には明示的なレイトレーシング方式を選択してください。",
"render.method_raster": "CPU ラスター",
"render.method_raster": "CPU ラスター(模式図)",
"render.navigation_help": "カメラはデザインと同じように動きます(環境設定 › ナビゲーション)。「セットアップを保存」で現在のカメラを記録します。",
"render.nested_enable": "検証済み入れ子誘電体",
"render.nested_help": "最大16層。親は凸形状、子は厳密に内部である必要があります。交差・接触・薄膜・直線透過影は拒否します。内部カメラは検証した階層を使います。液面の生成や接触するCAD境界の結合は行いません。",
@@ -3651,7 +3647,7 @@
"render.output_exr_unavailable": "ここではEXR出力設定を利用できません。リニアEXR書き出しには、リニアマスターを保持した完成GPU画像が必要です。",
"render.output_fit": "全体表示",
"render.output_format": "形式",
"render.output_format_status": "PNGは完了した表示画像そのものです。GPUの任意EXRは表示フィルター前の生の線形FLOAT32放射輝度を保持します。プレビューキャッシュや別ジョブへの置換はありません。",
"render.output_format_status": "PNGは完了した表示画像そのものです。CPU/GPUの任意EXRは表示フィルター前の生の線形FLOAT32放射輝度を保持します。プレビューキャッシュや別ジョブへの置換はありません。",
"render.output_grading": "カラーグレーディング",
"render.output_image": "画像",
"render.output_largest": "{width} × {height} 最大",
@@ -3736,7 +3732,7 @@
"render.quality_custom": "カスタム",
"render.quality_draft": "ドラフト",
"render.quality_high": "高品質",
"render.quality_projection_help": "Qプリセットには将来の要件も含まれます。この操作は対応する固定サンプルPNG予算のみを適用し、適応的停止、生の線形出力、独立した透過制限は含みません。",
"render.quality_projection_help": "この確認で変更するのは、解像度、サンプル予算、パス深度、シード、ノイズ低減だけです。他の設定は維持され、収束は保証されません。",
"render.quality_review": "サンプリング予算を確認",
"render.queue_pending": "このUIには自動の本番キューやアニメーションレンダラーはありません。完了した静止画像は個別に書き出せます。",
"render.radial_assign": "外観を割り当て",
@@ -3767,7 +3763,7 @@
"render.result_navigation": "移動とズームはナビゲーション設定に従います。スペースキーを押しながらドラッグすると移動します。",
"render.result_open": "完成した画像をメインキャンバスに表示します。",
"render.result_preview": "ライブプレビュー",
"render.result_provisional": "CPU仮画像",
"render.result_provisional": "仮画像",
"render.result_provisional_help": "この中間画像はエクスポートできません。最後に完成した結果をエクスポートします。キャンセルするとその結果に戻ります。",
"render.result_reduced_display": "表示プレビューをビューポート機器またはキャッシュの上限に縮小しています。保持したPNGと書き出しは元の解像度です。この縮小表示ではネイティブ100%表示はできません。",
"render.result_retained": "最後に完成した画像を表示しています。新しいレンダリングにはまだ置き換わっていません。",
@@ -3931,7 +3927,7 @@
"render.stage_panels.prepare_plan": "作成計画を準備",
"render.stage_panels.projects": "投影",
"render.stage_panels.quality_ack": "固定予算と省略事項を確認しました",
"render.stage_panels.quality_omissions": "適応停止と誤差目標、個別の透過深度、ロシアンルーレット規則、線形RAW出力、EXRマスター、収束認証。",
"render.stage_panels.quality_omissions": "この予算は、適応サンプリングの誤差目標、独立した透過制限、ロシアンルーレット規則、リニアマスターの保持を設定しません。収束も保証しません。",
"render.stage_panels.reference": "参照",
"render.stage_panels.references": "参照",
"render.stage_panels.remove_optics": "光学設定を削除",
@@ -3975,7 +3971,7 @@
"render.stage_panels.subtitle_environment": "オリジナルHDR環境・シーン全体",
"render.stage_panels.subtitle_maps": "作成済み空間マップ・一時プレビュー",
"render.stage_panels.subtitle_physical": "読み取り専用計画・マテリアルの作成や割り当てなし",
"render.stage_panels.subtitle_quality": "固定GPU予算・将来の要件は省略",
"render.stage_panels.subtitle_quality": "GPUサンプリング予算 · 記載された設定のみ",
"render.stage_panels.subtitle_rig": "作成済みライトリグ・光源と受動カード",
"render.stage_panels.subtitle_saved_maps": "作成済みマップ・このショットに保存",
"render.stage_panels.subtitle_stage": "物理的な地面とステージ・製品にフィット",
@@ -4000,7 +3996,7 @@
"render.studio_area_help": "最大 4 個の片面矩形です。直交する半辺 U と V でサイズを定め、U × V が発光方向になります。座標はワールド空間の mm です。",
"render.studio_center": "中心 X, Y, Z (mm)",
"render.studio_denoise": "完成画像のノイズを低減",
"render.studio_denoise_help": "輪郭を保持する最終フィルターです。微細な描写が柔らかくなることがあります。ガラスと鋭い鏡面反射は対象外です。",
"render.studio_denoise_help": "プログレッシブ画像と完成画像に輪郭を保持するフィルターを適用します。微細な描写が柔らかくなることがあります。ガラスと鋭い鏡面反射は対象外です。",
"render.studio_direction": "太陽に向かう方向 (X, Y, Z)",
"render.studio_environment": "環境",
"render.studio_environment_bounds": "環境回転は±1,000,000ラジアン以内の有限値、強度は0~100でなければなりません。",
@@ -4202,6 +4198,32 @@
"sketch.dof_left": "残り自由度 {count}",
"sketch.dof_left_one": "残り自由度 {count}",
"sketch.draw_curves": "{linetype}として描く",
"sketch.error.closed_curve_two_crossings": "a closed curve needs two crossings to trim",
"sketch.error.nothing_to_break": "nothing to break here",
"sketch.error.offset_fillet_chamfer": "`{id}` is an offset, and its corners follow its source curves: round or cut the source corners before offsetting, or break the offset association first",
"sketch.error.offset_trim": "`{id}` is an offset, and its ends follow its source curves: edit the source curves instead, or break the offset association first",
"sketch.hint.arc.center_point": "Arc: click center and start, go round either way and click the end, or type a signed sweep",
"sketch.hint.arc.three_point": "Arc: click start, end, then a point on the arc",
"sketch.hint.arc3d": "3D Arc: click start, end, then a point on the arc (points may leave the sketch plane)",
"sketch.hint.break": "Break: click a curve where it should split",
"sketch.hint.circle.center": "Circle: click the center, then a point on the circle",
"sketch.hint.constraint.generic": "Constraint: pick the sketch entities",
"sketch.hint.constraint.horizontal_vertical": "Horizontal/Vertical: pick a line, two points, or a point and a line; hold Shift to pick a line midpoint",
"sketch.hint.constraint.midpoint": "MidPoint: pick a point and a line, or two lines to center the second on the first",
"sketch.hint.dimension": "Dimension: pick one or two entities and type the value (or click to place it first); click a dimension to edit it; Enter applies",
"sketch.hint.extend": "Extend: click a curve near the end to extend (Shift: click two curves to extend both to where they meet)",
"sketch.hint.fallback": "Click in the sketch",
"sketch.hint.line": "Line: click points; right-click or Esc to finish",
"sketch.hint.line3d": "3D Line: click world points; right-click or Esc to finish (points may leave the sketch plane)",
"sketch.hint.open_done": "Open done",
"sketch.hint.point3d": "3D Point: click where a 3D point goes",
"sketch.hint.rectangle.center": "Center rectangle: click the center, then a corner",
"sketch.hint.rectangle.rounded": "Rounded rectangle: click two opposite corners (Tab to the radius box to type the corner radius)",
"sketch.hint.rectangle.three_point": "3-point rectangle: click two points of one edge, then the width",
"sketch.hint.rectangle.two_point": "Rectangle: click two opposite corners",
"sketch.hint.slot": "Slot: click both ends, then the width",
"sketch.hint.spline3d": "3D Spline: click fit points in 3D; right-click or Esc to finish",
"sketch.hint.trim": "Trim: click the piece of a curve to remove, or drag across the pieces to remove",
"sketch.make_selection": "選択を{linetype}にする",
"sketch.open_ends": "開放 {count}",
"sketch.status_conflict": "拘束が競合しています",
@@ -4423,6 +4445,7 @@
"ui.3d_sketch": "3D スケッチ",
"ui.activate": "アクティブ化",
"ui.active_component": "アクティブなコンポーネント",
"ui.addToQuickAccess": "クイックアクセスに追加",
"ui.add_configuration": "構成を追加",
"ui.add_parameter": "パラメーターを追加",
"ui.agent_chat": "エージェントチャット",
@@ -5015,7 +5038,6 @@
"ui.fine": "細かい",
"ui.finest": "最も細かい",
"ui.finish_sketch": "スケッチを終了",
"ui.first_angle": "第一角法",
"ui.fit": "フィット",
"ui.fix_unfix": "固定/固定解除",
"ui.flat": "展開",
@@ -5052,6 +5074,7 @@
"ui.heading_construct": "構築",
"ui.heading_create": "作成",
"ui.heading_create_sketch": "スケッチを作成",
"ui.heading_custom_view_navigator": "カスタム ビューナビゲーター",
"ui.heading_cylinder": "円柱",
"ui.heading_design": "デザイン",
"ui.heading_draft": "勾配",
@@ -5320,7 +5343,6 @@
"ui.profiles": "プロファイル",
"ui.project": "投影",
"ui.project_face_edges_sketch": "新規スケッチに面のエッジを自動投影",
"ui.projection_of_new_drawings": "新規図面の投影法",
"ui.properties": "プロパティ",
"ui.properties.copy_matrix": "行列をコピー",
"ui.properties.first_moments": "一次モーメント",
@@ -5339,6 +5361,7 @@
"ui.quality": "品質",
"ui.quantity": "数量",
"ui.quantity_2": "数量 2",
"ui.quick_access_full": "クイックアクセスバーに置けるコマンドは {count} 個までです。追加するには 1 つ削除してください",
"ui.quit": "終了",
"ui.radial_context_menu": "パイ メニュー",
"ui.radius": "半径",
@@ -5356,6 +5379,7 @@
"ui.relink_not_linked": "このコンポーネントは参照元の設計にリンクされていません。",
"ui.relink_select_one": "リンクされたコンポーネントまたはそのインスタンスを1つ選択してください。",
"ui.relink_source_all": "参照元を再リンク(すべてのインスタンス)…",
"ui.removeFromQuickAccess": "クイックアクセスから削除",
"ui.removeFromToolbar": "ツールバーから削除",
"ui.removeSection": "セクションを解除",
"ui.render": "レンダリング",
@@ -5364,6 +5388,8 @@
"ui.repeat_nothing": "まだ繰り返すものがありません。先にツールかダイアログを開始してください。",
"ui.replace_with_link_done": "コピーはリンクされたデザインの配置になりました。",
"ui.report_a_bug": "不具合を報告",
"ui.report_characters_left_one": "残り {count} 文字",
"ui.report_characters_left_other": "残り {count} 文字",
"ui.reset": "リセット",
"ui.reset_all": "すべてリセット",
"ui.reset_the_settings_on_this_page": "このページの設定をリセット",
@@ -5506,7 +5532,6 @@
"ui.text": "テキスト",
"ui.theme": "テーマ",
"ui.thickness": "厚さ",
"ui.third_angle": "第三角法",
"ui.thread": "ねじ",
"ui.tie_ends": "拘束",
"ui.tie_ends_tip": "未拘束をすべて拘束",
@@ -5649,6 +5674,14 @@
"values must be numbers or expressions": "値は数値または式にしてください",
"view.auto_ortho_faces": "回転中は透視投影、面を正面に見ると平行投影",
"view.render_inspector": "レンダーインスペクター",
"view_gizmo.arrows": "回転矢印",
"view_gizmo.home": "ホーム ボタン",
"view_gizmo.no_such_body": "このデザインにその名前のボディはありません。",
"view_gizmo.note": "ビューでボディを選択してください。ボディは環境設定と設定プロファイルに保存され、すべてのデザインで表示されます。面をクリックすると、その面を正面から見ます。",
"view_gizmo.texture": "テクスチャ",
"view_gizmo.themed": "テーマ",
"view_gizmo.too_detailed": "ビューナビゲーターには細かすぎます (10,000 三角形を超えています)",
"view_gizmo.too_thin": "ビューナビゲーターには細長すぎます: 最も薄い辺が最も長い辺の 5 分の 1 以上である必要があります",
"web.files.insert_drawings_hint": "CADファイル、メッシュ、DXF、SVGを開いているデザインに挿入",
"web.files.insert_stored_drawings_hint": "開いているデザインに挿入(CADファイル、メッシュ、DXF、SVG)",
"web.open_full": "SolidCraft で開く",
+1 -2
View File
@@ -963,8 +963,7 @@
"cmd.inspect_analysis": "ანალიზის ნახვა",
"cmd.inspect_center_of_mass": "მასის ცენტრი",
"cmd.inspect_clearance": "ღრეჩო",
"cmd.inspect_colored_components": "კომპონენტების ფერების ჩართვა",
"cmd.inspect_colored_components_disable": "კომპონენტების ფერების გამორთვა",
"cmd.inspect_colored_components": "კომპონენტების ფერები",
"cmd.inspect_compare": "სხეულების შედარება",
"cmd.inspect_compare_picture": "სურათთან შედარება",
"cmd.inspect_continuity": "უწყვეტობის ანალიზი",
+32 -13
View File
@@ -724,8 +724,6 @@
"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": "바디 이름 변경",
@@ -896,7 +894,6 @@
"cmd.motion_study": "운동 연구",
"cmd.motion_trace": "모션 궤적",
"cmd.motion_trace_keep": "모션 궤적 유지",
"cmd.occurrence_bom_type": "자재 명세서 유형",
"cmd.occurrence_copy": "붙여넣기(인스턴스)",
"cmd.occurrence_ground": "고정",
"cmd.occurrence_move": "인스턴스 이동",
@@ -1136,6 +1133,7 @@
"cmd.timeline_stop": "기록 재생 중지",
"cmd.timeline_suppress": "특성 비활성화",
"cmd.ui.assistant": "도우미 표시 전환",
"cmd.view.navigator": "사용자 지정 뷰 탐색기",
"cmd.view.render_browser": "렌더 브라우저",
"cmd.view.render_dock": "렌더 도크 이동",
"cmd.view.render_focus": "렌더 집중 보기",
@@ -1208,6 +1206,7 @@
"dialog.computing": "계산 중… {seconds}초",
"dialog.could_not_be_checked": "확인할 수 없음",
"dialog.custom_axis": "↖ 사용자 정의 축",
"dialog.cylinder_face_tip": "원기둥이 놓일 모델의 면 또는 꼭짓점: 여기를 클릭한 다음 뷰에서 그것을 클릭. 비워 두면 X, Y, Z로 배치합니다.",
"dialog.dimension_insert_hint": "다른 구동 치수를 두 번 클릭하여 이름 삽입",
"dialog.dimension_name": "치수: {name}",
"dialog.dimension_step_down": "{step} 줄이기",
@@ -1215,6 +1214,8 @@
"dialog.dimension_step_up": "{step} 늘리기",
"dialog.edit_title": "{title} 편집",
"dialog.pattern_sketch_not_object": "바디나 피처를 선택하세요. 스케치는 둘 다 아닙니다. 곡선은 스케치 안의 패턴이나 대칭 복사로 반복하거나, 먼저 돌출하세요.",
"dialog.primitive_point_tip": "X, Y, Z를 채우는 점: 여기를 클릭한 다음 모델의 꼭짓점, 모서리, 면, 스케치의 점 또는 원점을 클릭.",
"dialog.reference_edge_tip": "아래 거리를 재는 기준이 되는, 선택한 면의 직선 모서리: 여기를 클릭한 다음 모서리를 클릭.",
"dialog.select": "↖ 선택",
"dialog.selected_count": "↖ {count}개 선택됨",
"dialog.selection_constraint": "구속 조건 {id}",
@@ -1243,6 +1244,9 @@
"dxf.tolerance_help": "표시된 바디와 스케치만 내보내며 곡선은 직선 구간으로 근사합니다. 허용 오차가 작을수록 곡선이 매끄러워지지만 처리 시간과 메모리 사용량이 늘어납니다.",
"each rail's curves must be in one sketch": "각 레일의 곡선은 하나의 스케치 안에 있어야 합니다",
"feature computation ended without a result": "특성 계산이 결과 없이 끝났습니다",
"file.open.portal_fallback.no_portal_service": "열린 파일 선택은 데스크톱의 것이 아닙니다: 데스크톱의 파일 선택 서비스를 사용할 수 없습니다.",
"file.open.portal_fallback.no_session_bus": "열린 파일 선택은 데스크톱의 것이 아닙니다: D-Bus 세션 버스가 없습니다.",
"file.open.portal_fallback.xwayland": "열린 파일 선택은 데스크톱의 것이 아닙니다: XWayland에서는 포털이 부모 창을 거부합니다.",
"file_load.built": "지금까지 빌드됨: 피처 {total}개 중 {built}개.",
"file_load.converting": "솔리드 변환 중 {done}/{total}",
"file_load.inserting": "기하 삽입 중",
@@ -1464,6 +1468,8 @@
"nav.blender.help": "가운데 드래그는 회전, Shift+가운데 드래그는 이동, Ctrl+가운데 드래그는 확대/축소입니다. 오른쪽 드래그는 카메라에 영향을 주지 않습니다",
"nav.creo.help": "가운데 버튼을 끌면 회전, Shift+가운데 버튼은 이동, Ctrl+가운데 버튼을 위아래로 끌면 확대/축소합니다(아래로 끌면 확대). 휠을 굴려도 확대/축소합니다",
"nav.creo.label": "Creo",
"nav.drag_keeps_mode.help": "가운데 또는 오른쪽 버튼 드래그는 도중에 키를 누르거나 떼어도 버튼을 놓을 때까지 시작할 때의 회전, 이동 또는 확대/축소를 계속합니다. 끄면 드래그하는 동안 누르고 있는 키가 매 순간 결정합니다.",
"nav.drag_keeps_mode.label": "드래그는 시작할 때의 탐색 방식을 유지",
"nav.free_orbit.help": "마우스와 3D 마우스로 롤을 유지하며 양 극점을 넘어 회전합니다. Shift 축 스냅은 비활성화됩니다.",
"nav.free_orbit.label": "자유 회전",
"nav.free_orbit.snap_help": "마우스와 3D 마우스로 양쪽 극점을 지나 회전할 수 있으며 롤 각도가 유지됩니다. 궤도 회전 드래그 중 Shift를 누르면 보기 방향과 롤 각도가 스냅됩니다.",
@@ -1684,6 +1690,8 @@
"prefs.browser_components_open_note": "브라우저의 모든 구성요소가 이전처럼 펼쳐진 상태로 시작합니다. 끄면 직접 펼칠 때까지 접혀 있습니다.",
"prefs.browser_eye_left": "접기 화살표 옆의 눈 아이콘",
"prefs.browser_eye_left_note": "브라우저 행의 표시 아이콘을 행 오른쪽 끝의 열이 아니라 이전처럼 접기 화살표 옆에 표시합니다.",
"prefs.browser_rows_start_closed": "브라우저 행을 접힌 상태로 시작",
"prefs.browser_rows_start_closed_note": "브라우저의 배치와 폴더가 접힌 상태로 시작하므로, 컴포넌트가 많은 디자인은 최상위 행만 보여 줍니다. 각 행은 화살표, '모두 펼치기' 또는 '브라우저에서 찾기'로 엽니다. '브라우저에서 찾기'는 바디까지의 행만 열고, 지난번 찾기로 연 행은 다시 접습니다. 3D 뷰에서 바디를 골라도 브라우저는 그대로입니다. 끄면 폴더가 펼친 상태로 시작하고, 배치는 '구성요소를 펼친 상태로 시작' 설정을 따르며, 3D 뷰에서 고르면 브라우저가 그 바디까지 열립니다(이전과 같음).",
"prefs.built_in": "기본 제공",
"prefs.button_left": "왼쪽 드래그",
"prefs.button_middle": "가운데 드래그",
@@ -1735,7 +1743,6 @@
"prefs.dimension_values_view": "뷰에서",
"prefs.dimension_values_window": "창에서",
"prefs.display": "표시",
"prefs.drawings": "도면",
"prefs.edges": "모서리 및 음영",
"prefs.extend": "확장",
"prefs.extra_navigation": "Ctrl+왼쪽 드래그로 빈 공간에서 회전하고, Space를 누른 채 이동합니다.",
@@ -1893,6 +1900,8 @@
"prefs.search": "설정 검색",
"prefs.search_empty": "일치하는 설정이 없습니다",
"prefs.select": "선택",
"prefs.select_hidden_edges": "숨은 모서리가 표시된 음영 뷰에서 숨은 모서리 선택",
"prefs.select_hidden_edges_help": "숨은 모서리가 표시된 음영 뷰에서 면 뒤의 모서리를 선택하여 필렛에 사용할 수 있습니다. 끄면 v0.30.1의 선택 동작을 유지합니다. 숨긴 바디는 선택되지 않습니다.",
"prefs.select_one_item": "항목 하나 선택",
"prefs.select_other_hidden": "다른 항목 선택으로 숨겨진 형상 선택",
"prefs.select_other_hidden_help": "켜짐: 포인터 근처의 숨겨진 바디, 스케치 및 가려진 모서리를 포함합니다. 꺼짐: 포인터 바로 아래에 보이는 형상만 포함하는 기존 목록을 사용합니다.",
@@ -1917,6 +1926,8 @@
"prefs.sketch.click_selects": "클릭으로 선택",
"prefs.sketch.conflict_colour": "충돌 색상",
"prefs.sketch.drawing": "그리기",
"prefs.sketch.ellipse_axes": "타원을 축과 함께 그리기",
"prefs.sketch.ellipse_axes_note": "장축과 단축을 구성선으로 그려 반축과 축에 치수를 지정할 수 있습니다. 끄면: 타원만 그리며, D는 값을 어디로 끌든 단반경을 주며, 이전처럼 R로 표시되어 그려집니다.",
"prefs.sketch.exit_keep": "유지",
"prefs.sketch.exit_previous": "이전 뷰로 돌아가기",
"prefs.sketch.exit_unless_rotated": "회전하지 않았다면",
@@ -2109,7 +2120,7 @@
"render.appearance_editor.replace": "이 바디의 저장된 광학 설정 교체",
"render.appearance_editor.revalidate": "이 바디 다시 검증",
"render.appearance_editor.save": "이 광학 설정 저장",
"render.appearance_editor.save_reason": "저장하려면 현재 GPU 바디에 물리 유리가 필요합니다. 코트와 로컬 이미지 맵은 임시로 유지됩니다.",
"render.appearance_editor.save_reason": "유리를 저장하려면 현재 CPU 또는 GPU 바디에서 활성화하세요. 클리어 코트는 샷에 코트 저장을 사용하세요. 로컬 이미지 파일은 임시 설정으로 유지됩니다.",
"render.appearance_editor.saved": "이 샷에 저장됨",
"render.appearance_editor.saved_state": "이 광학 설정은 이 샷과 함께 저장되어 있습니다.",
"render.appearance_editor.scatter_reason": "내부 산란에는 닫힌 솔리드 경계가 필요합니다.",
@@ -2157,7 +2168,7 @@
"render.children.copy_request": "요청 JSON 복사",
"render.children.coverage_error": "커버리지 오차",
"render.children.cpu": "CPU 레이 트레이싱",
"render.children.denoise": "완성 이미지의 노이즈 줄이기",
"render.children.denoise": "이미지 노이즈 줄이기",
"render.children.depth": "경로 깊이",
"render.children.distortion": "왜곡",
"render.children.edge_samples": "모서리 샘플",
@@ -2378,7 +2389,7 @@
"render.error_visible_solid": "여기서 미리 보려면 디자인에서 보이는 솔리드를 만드세요.",
"render.exit_focus": "집중 보기 종료",
"render.export_image_label": "이미지 내보내기",
"render.exr_pending": "GPU는 원본 선형 EXR을 보존할 수 있습니다. 압축, 다중 레이어 이미지 및 보조 렌더 패스는 아직 사용할 수 없습니다.",
"render.exr_pending": "CPU 레이 트레이싱과 GPU 패스 트레이싱은 원본 선형 EXR을 보존할 수 있습니다. 압축, 다중 레이어 이미지 및 보조 렌더 패스는 아직 사용할 수 없습니다.",
"render.fit": "제품에 맞추기",
"render.fixed_camera": "카메라",
"render.fixed_world": "월드",
@@ -2628,7 +2639,7 @@
"render.output_exr_unavailable": "여기서는 EXR 출력을 설정할 수 없습니다. 선형 EXR 내보내기에는 선형 원본을 보관한 완료된 GPU 이미지가 필요합니다.",
"render.output_fit": "맞춤",
"render.output_format": "형식",
"render.output_format_status": "PNG는 완료된 표시 이미지 그대로입니다. 선택적 GPU EXR은 표시 필터 전 선형 FLOAT32 방사 휘도를 보존합니다. 미리보기 캐시나 다른 작업으로 바꾸어 내보내지 않습니다.",
"render.output_format_status": "PNG는 완료된 표시 이미지 그대로입니다. 선택적 CPU/GPU EXR은 표시 필터 전 선형 FLOAT32 방사 휘도를 보존합니다. 미리보기 캐시나 다른 작업으로 바꾸어 내보내지 않습니다.",
"render.output_grading": "색 보정",
"render.output_image": "이미지",
"render.output_largest": "{width} × {height} 최대",
@@ -2678,6 +2689,7 @@
"render.quality_custom": "사용자 지정",
"render.quality_draft": "초안",
"render.quality_high": "높음",
"render.quality_projection_help": "이 검토에서는 해상도, 샘플 예산, 경로 깊이, 시드 및 노이즈 제거만 변경합니다. 다른 설정은 유지되며 수렴은 보장되지 않습니다.",
"render.radial_assign": "외관 지정",
"render.radial_compare": "비교",
"render.radial_fit": "제품 맞추기",
@@ -2820,7 +2832,7 @@
"render.stage_panels.prepare_plan": "생성 계획 준비",
"render.stage_panels.projects": "투영",
"render.stage_panels.quality_ack": "고정 예산과 생략 사항을 확인했습니다",
"render.stage_panels.quality_omissions": "적응형 중지 및 오차 목표, 별도 투과 깊이, 러시안 룰렛 규칙, 원시 선형 출력, EXR 마스터 및 수렴 인증.",
"render.stage_panels.quality_omissions": "이 예산은 적응형 오차 목표, 독립적인 투과 한도, 러시안 룰렛 규칙 또는 선형 마스터 보존을 설정하지 않으며 수렴을 보장하지 않습니다.",
"render.stage_panels.reference": "참조",
"render.stage_panels.references": "참조",
"render.stage_panels.remove_optics": "광학 설정 제거",
@@ -2864,7 +2876,7 @@
"render.stage_panels.subtitle_environment": "원본 HDR 환경 · 전체 장면",
"render.stage_panels.subtitle_maps": "제작된 공간 맵 · 임시 미리 보기",
"render.stage_panels.subtitle_physical": "읽기 전용 계획 · 재질 생성 또는 할당 없음",
"render.stage_panels.subtitle_quality": "고정 GPU 예산 · 향후 요구 사항 생략",
"render.stage_panels.subtitle_quality": "GPU 샘플링 예산 · 표시된 설정만 변경",
"render.stage_panels.subtitle_rig": "제작된 조명 리그 · 광원 및 수동 카드",
"render.stage_panels.subtitle_saved_maps": "제작된 맵 · 이 샷에 저장",
"render.stage_panels.subtitle_stage": "물리적 바닥 및 스테이지 · 제품에 맞춤",
@@ -2881,6 +2893,7 @@
"render.state_in_progress": "이미지 생성 중",
"render.state_model": "모델 미리보기",
"render.strip_label": "스트립",
"render.studio_denoise_help": "프로그레시브 이미지와 완성 이미지에 경계를 보존하는 필터를 적용합니다. 미세한 디테일이 부드러워질 수 있습니다. 유리와 날카로운 정반사 표면은 필터를 거치지 않습니다.",
"render.studio_tiles_require_gpu": "스튜디오에는 GPU 경로 추적이 필요합니다. 출력 편집기에서 GPU를 선택하세요.",
"render.studios_environments": "스튜디오 및 환경",
"render.tone_aces_fitted": "ACES 근사",
@@ -3754,7 +3767,6 @@
"ui.fine": "세밀",
"ui.finest": "가장 세밀",
"ui.finish_sketch": "스케치 종료",
"ui.first_angle": "제1각법",
"ui.fit": "맞춤",
"ui.fix_unfix": "고정/고정 해제",
"ui.flat": "플랫",
@@ -3789,6 +3801,7 @@
"ui.heading_construct": "구성",
"ui.heading_create": "만들기",
"ui.heading_create_sketch": "스케치 만들기",
"ui.heading_custom_view_navigator": "사용자 지정 뷰 탐색기",
"ui.heading_cylinder": "원통",
"ui.heading_design": "디자인",
"ui.heading_draft": "구배",
@@ -4028,7 +4041,6 @@
"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차 모멘트",
@@ -4188,7 +4200,6 @@
"ui.text": "텍스트",
"ui.theme": "테마",
"ui.thickness": "두께",
"ui.third_angle": "제3각법",
"ui.thread": "나사산",
"ui.tie_ends": "연결",
"ui.tie_ends_tip": "연결되지 않은 모든 끝점 구속",
@@ -4318,5 +4329,13 @@
"values must be finite numbers": "값은 유한한 숫자여야 합니다",
"values must be numbers or expressions": "값은 숫자 또는 식이어야 합니다",
"view.render_inspector": "렌더 검사기",
"view_gizmo.arrows": "회전 화살표",
"view_gizmo.home": "홈 버튼",
"view_gizmo.no_such_body": "이 디자인에 그런 이름의 바디가 없습니다.",
"view_gizmo.note": "뷰에서 바디를 선택하십시오. 바디는 환경설정과 설정 프로필에 저장되어 모든 디자인에 표시됩니다. 면을 클릭하면 그 면을 정면으로 봅니다.",
"view_gizmo.texture": "텍스처",
"view_gizmo.themed": "테마",
"view_gizmo.too_detailed": "뷰 탐색기에 너무 세밀합니다 (삼각형 10,000개 초과)",
"view_gizmo.too_thin": "뷰 탐색기에 너무 가늘고 깁니다: 가장 얇은 변이 가장 긴 변의 5분의 1 이상이어야 합니다",
"↑ ↓ choose · Enter / Tab complete · Esc dismiss": "↑ ↓ 선택 · Enter / Tab 완성 · Esc 닫기"
}
-2
View File
@@ -957,8 +957,6 @@
"cmd.inspect_accessibility": "Analisis Kebolehcapaian",
"cmd.inspect_center_of_mass": "Pusat Jisim",
"cmd.inspect_clearance": "Kelegaan",
"cmd.inspect_colored_components": "Aktifkan Warna Komponen",
"cmd.inspect_colored_components_disable": "Nyahaktifkan Warna Komponen",
"cmd.inspect_compare": "Bandingkan Jasad",
"cmd.inspect_compare_picture": "Banding dengan Imej",
"cmd.inspect_continuity": "Analisis Keselanjaran",
-2
View File
@@ -957,8 +957,6 @@
"cmd.inspect_accessibility": "Tilgjengelighetsanalyse",
"cmd.inspect_center_of_mass": "Massesenter",
"cmd.inspect_clearance": "Klaring",
"cmd.inspect_colored_components": "Aktiver komponentfarger",
"cmd.inspect_colored_components_disable": "Deaktiver komponentfarger",
"cmd.inspect_compare": "Sammenlign kropper",
"cmd.inspect_compare_picture": "Sammenlign bilde",
"cmd.inspect_continuity": "Kontinuitetsanalyse",
-2
View File
@@ -957,8 +957,6 @@
"cmd.inspect_accessibility": "Toegankelijkheidsanalyse",
"cmd.inspect_center_of_mass": "Zwaartepunt",
"cmd.inspect_clearance": "Speling",
"cmd.inspect_colored_components": "Componentkleuren inschakelen",
"cmd.inspect_colored_components_disable": "Componentkleuren uitschakelen",
"cmd.inspect_compare": "Lichamen vergelijken",
"cmd.inspect_compare_picture": "Afbeelding vergelijken",
"cmd.inspect_continuity": "Continuïteitsanalyse",
@@ -0,0 +1,26 @@
{
"en": {
"ai.prefs.instructions": "Custom instructions",
"ai.prefs.instructions_note": "Sent to the agent at the start of every chat, after SolidCraft's own instructions: how you like to work, rules to keep to. A change applies from the next chat (Connect or New chat)."
},
"ar": {
"ai.prefs.instructions": "تعليمات مخصصة",
"ai.prefs.instructions_note": "تُرسَل إلى الوكيل في بداية كل محادثة، بعد تعليمات SolidCraft الخاصة: طريقتك المفضلة في العمل والقواعد التي يجب الالتزام بها. يسري أي تغيير من المحادثة التالية (اتصال أو محادثة جديدة)."
},
"he": {
"ai.prefs.instructions": "הוראות מותאמות אישית",
"ai.prefs.instructions_note": "נשלחות לסוכן בתחילת כל צ'אט, אחרי ההוראות של SolidCraft עצמה: דרך העבודה המועדפת עליך וכללים שיש להקפיד עליהם. שינוי חל מהצ'אט הבא (התחברות או צ'אט חדש)."
},
"ja": {
"ai.prefs.instructions": "カスタム指示",
"ai.prefs.instructions_note": "各チャットの開始時に、SolidCraft 自身の指示の後に続けてエージェントへ送られます。作業の進め方や守ってほしいルールを書いてください。変更は次のチャット(接続または新しいチャット)から適用されます。"
},
"vi": {
"ai.prefs.instructions": "Hướng dẫn tùy chỉnh",
"ai.prefs.instructions_note": "Được gửi cho agent khi bắt đầu mỗi cuộc trò chuyện, sau hướng dẫn riêng của SolidCraft: cách bạn muốn làm việc, các quy tắc cần tuân theo. Thay đổi có hiệu lực từ cuộc trò chuyện tiếp theo (Kết nối hoặc Cuộc trò chuyện mới)."
},
"zh": {
"ai.prefs.instructions": "自定义指令",
"ai.prefs.instructions_note": "在每次对话开始时发送给代理,位于 SolidCraft 自身的指令之后:你喜欢的工作方式、需要遵守的规则。更改从下一次对话(连接或新对话)起生效。"
}
}
+29
View File
@@ -0,0 +1,29 @@
{
"ar": {
"render.aging_raytrace_help": "يُطبّق التعتيق في تتبع الأشعة على المعالج وتتبع المسارات على المعالج الرسومي. يبقى التصيير النقطي تخطيطياً."
},
"de": {
"render.aging_raytrace_help": "Alterung wird beim CPU-Raytracing und GPU-Pathtracing angewendet. Raster bleibt eine schematische Darstellung."
},
"en": {
"render.aging_raytrace_help": "Aging is applied by CPU ray tracing and GPU path tracing. Raster keeps schematic rendering."
},
"en-GB": {
"render.aging_raytrace_help": "Ageing is applied by CPU ray tracing and GPU path tracing. Raster keeps schematic rendering."
},
"he": {
"render.aging_raytrace_help": "התיישנות מוחלת במעקב קרניים במעבד ובמעקב מסלולים במעבד הגרפי. רינדור רסטר נשאר סכמטי."
},
"ja": {
"render.aging_raytrace_help": "エイジングはCPUレイトレーシングとGPUパストレーシングで適用されます。ラスタは模式的な描画を維持します。"
},
"ko": {
"render.aging_raytrace_help": "에이징은 CPU 광선 추적과 GPU 경로 추적에 적용됩니다. 래스터는 도식적인 렌더링을 유지합니다."
},
"vi": {
"render.aging_raytrace_help": "Hiệu ứng lão hoá được áp dụng khi dò tia bằng CPU và dò đường đi bằng GPU. Kết xuất raster giữ kiểu sơ đồ."
},
"zh": {
"render.aging_raytrace_help": "老化效果适用于CPU光线追踪和GPU路径追踪。光栅保持示意渲染。"
}
}
@@ -0,0 +1,98 @@
{
"ar": {
"render.cpu_volume_enable_path": "تفعيل الإضاءة غير المباشرة",
"render.cpu_volume_enable_path_plan": "تتبع أشعة CPU · عمق المسار {depth} · {spp} عينة/بكسل · بذرة {seed} · ظلال متتبعة. يحتفظ بالغلاف الجوي وميزانية الصورة المحددين.",
"render.cpu_volume_help": "يستخدم الغلاف الجوي على CPU الإضاءة غير المباشرة. يحافظ الانطفاء الصفري على الصندوق المحفوظ دون ضباب.",
"render.cpu_volume_method_adopt": "تتبع أشعة CPU مع إضاءة غير مباشرة",
"render.cpu_volume_method_incompatible": "لا يمكن نقل إعدادات الإضاءة أو أخذ العينات أو الغلاف الجوي هذه إلى هذه الطريقة. تبقى إعداداتك محفوظة.",
"render.cpu_volume_path_required": "فعّل الإضاءة غير المباشرة لاستخدام الغلاف الجوي على CPU.",
"render.volume_denoise_required": "أوقف تقليل الضوضاء لاستخدام الغلاف الجوي.",
"render.volume_glass_required": "لا يمكن الجمع بين الغلاف الجوي والزجاج في هذا الإعداد.",
"render.volume_lighting_required": "اختر بيئة أو مجموعة إضاءة محددة لتصيير الغلاف الجوي.",
"render.volume_single_stratum_required": "استخدم عينة نفاذ واحدة لتصيير الغلاف الجوي."
},
"de": {
"render.cpu_volume_enable_path": "Indirekte Beleuchtung einschalten",
"render.cpu_volume_enable_path_plan": "CPU-Raytracing · Pfadtiefe {depth} · {spp} Samples/Pixel · Seed {seed} · Raytracing-Schatten. Atmosphäre und Bildbudget bleiben erhalten.",
"render.cpu_volume_help": "CPU-Atmosphäre nutzt indirekte Beleuchtung. Bei Extinktion null bleibt die gespeicherte Box ohne Nebel erhalten.",
"render.cpu_volume_method_adopt": "CPU-Raytracing mit indirekter Beleuchtung",
"render.cpu_volume_method_incompatible": "Diese Licht-, Sampling- oder Atmosphäreinstellungen lassen sich nicht auf diese Methode übertragen. Die Einstellungen bleiben erhalten.",
"render.cpu_volume_path_required": "Indirekte Beleuchtung einschalten, um CPU-Atmosphäre zu nutzen.",
"render.volume_denoise_required": "Für Atmosphäre die Rauschreduzierung ausschalten.",
"render.volume_glass_required": "Atmosphäre und Glas können in dieser Einstellung nicht kombiniert werden.",
"render.volume_lighting_required": "Für Atmosphäre eine Umgebung oder ein eigenes Lichtrig wählen.",
"render.volume_single_stratum_required": "Für Atmosphäre ein Transmissionssample verwenden."
},
"en": {
"render.cpu_volume_enable_path": "Enable Indirect lighting",
"render.cpu_volume_enable_path_plan": "CPU ray tracing · path depth {depth} · {spp} samples/pixel · seed {seed} · traced shadows. Keeps the authored atmosphere and image budget.",
"render.cpu_volume_help": "CPU Atmosphere uses Indirect lighting. Zero extinction keeps the saved box without fog.",
"render.cpu_volume_method_adopt": "CPU ray tracing with indirect lighting",
"render.cpu_volume_method_incompatible": "These light, sampling or atmosphere settings cannot be transferred to this method. Your settings are kept.",
"render.cpu_volume_path_required": "Enable Indirect lighting to use CPU Atmosphere.",
"render.volume_denoise_required": "Turn off noise reduction to use Atmosphere.",
"render.volume_glass_required": "Atmosphere cannot be combined with Glass in this setup.",
"render.volume_lighting_required": "Choose an environment or an explicit light rig to render Atmosphere.",
"render.volume_single_stratum_required": "Use one transmission sample to render Atmosphere."
},
"he": {
"render.cpu_volume_enable_path": "הפעלת תאורה עקיפה",
"render.cpu_volume_enable_path_plan": "מעקב קרניים ב־CPU · עומק מסלול {depth} · {spp} דגימות לפיקסל · זרע {seed} · צללים במעקב קרניים. האטמוספרה ותקציב התמונה שנקבעו נשמרים.",
"render.cpu_volume_help": "אטמוספרה ב־CPU משתמשת בתאורה עקיפה. הכחדה אפסית שומרת את התיבה ללא ערפל.",
"render.cpu_volume_method_adopt": "מעקב קרניים ב־CPU עם תאורה עקיפה",
"render.cpu_volume_method_incompatible": "לא ניתן להעביר הגדרות תאורה, דגימה או אטמוספרה אלה לשיטה זו. ההגדרות שלך נשמרות.",
"render.cpu_volume_path_required": "יש להפעיל תאורה עקיפה כדי להשתמש באטמוספרה ב־CPU.",
"render.volume_denoise_required": "יש לכבות הפחתת רעש כדי להשתמש באטמוספרה.",
"render.volume_glass_required": "לא ניתן לשלב אטמוספרה וזכוכית בהגדרה זו.",
"render.volume_lighting_required": "יש לבחור סביבה או מערך תאורה מפורש כדי לרנדר אטמוספרה.",
"render.volume_single_stratum_required": "יש להשתמש בדגימת מעבר אחת כדי לרנדר אטמוספרה."
},
"ja": {
"render.cpu_volume_enable_path": "間接照明を有効にする",
"render.cpu_volume_enable_path_plan": "CPUレイトレーシング · パス深度{depth} · {spp}サンプル/ピクセル · シード{seed} · レイトレースシャドウ。大気と画像予算の設定を保持します。",
"render.cpu_volume_help": "CPUの大気は間接照明を使用します。消散係数がゼロでも、霧のないボックスとして設定は保存されます。",
"render.cpu_volume_method_adopt": "間接照明を使うCPUレイトレーシング",
"render.cpu_volume_method_incompatible": "この照明、サンプリング、大気の設定はこの方式に引き継げません。設定は保持されます。",
"render.cpu_volume_path_required": "CPUの大気を使うには間接照明を有効にしてください。",
"render.volume_denoise_required": "大気を使うにはノイズ低減をオフにしてください。",
"render.volume_glass_required": "この設定では大気とガラスを併用できません。",
"render.volume_lighting_required": "大気を描画するには環境または明示的なライト構成を選んでください。",
"render.volume_single_stratum_required": "大気を描画するには透過サンプル数を1にしてください。"
},
"ko": {
"render.cpu_volume_enable_path": "간접 조명 켜기",
"render.cpu_volume_enable_path_plan": "CPU 레이 트레이싱 · 경로 깊이 {depth} · 픽셀당 {spp}샘플 · 시드 {seed} · 레이 트레이싱 그림자. 작성한 대기와 이미지 예산을 유지합니다.",
"render.cpu_volume_help": "CPU 대기는 간접 조명을 사용합니다. 소광 계수가 0이면 저장된 상자는 안개 없이 유지됩니다.",
"render.cpu_volume_method_adopt": "간접 조명을 사용하는 CPU 레이 트레이싱",
"render.cpu_volume_method_incompatible": "이 조명, 샘플링 또는 대기 설정을 이 방식으로 옮길 수 없습니다. 설정은 유지됩니다.",
"render.cpu_volume_path_required": "CPU 대기를 사용하려면 간접 조명을 켜세요.",
"render.volume_denoise_required": "대기를 사용하려면 노이즈 감소를 끄세요.",
"render.volume_glass_required": "이 설정에서는 대기와 유리를 함께 사용할 수 없습니다.",
"render.volume_lighting_required": "대기를 렌더링하려면 환경 또는 명시적인 조명 구성을 선택하세요.",
"render.volume_single_stratum_required": "대기를 렌더링하려면 투과 샘플을 1로 설정하세요."
},
"vi": {
"render.cpu_volume_enable_path": "Bật chiếu sáng gián tiếp",
"render.cpu_volume_enable_path_plan": "Dò tia CPU · độ sâu đường đi {depth} · {spp} mẫu/pixel · hạt giống {seed} · bóng dò tia. Giữ nguyên khí quyển và ngân sách ảnh đã đặt.",
"render.cpu_volume_help": "Khí quyển trên CPU dùng chiếu sáng gián tiếp. Hệ số suy giảm bằng không vẫn giữ hộp đã lưu mà không tạo sương.",
"render.cpu_volume_method_adopt": "Dò tia CPU với chiếu sáng gián tiếp",
"render.cpu_volume_method_incompatible": "Không thể chuyển các thiết lập đèn, lấy mẫu hoặc khí quyển này sang phương pháp này. Thiết lập của bạn được giữ nguyên.",
"render.cpu_volume_path_required": "Bật chiếu sáng gián tiếp để dùng Khí quyển trên CPU.",
"render.volume_denoise_required": "Tắt giảm nhiễu để dùng Khí quyển.",
"render.volume_glass_required": "Không thể kết hợp Khí quyển và Kính trong thiết lập này.",
"render.volume_lighting_required": "Chọn môi trường hoặc bộ đèn tường minh để kết xuất Khí quyển.",
"render.volume_single_stratum_required": "Dùng một mẫu truyền sáng để kết xuất Khí quyển."
},
"zh": {
"render.cpu_volume_enable_path": "启用间接照明",
"render.cpu_volume_enable_path_plan": "CPU 光线追踪 · 路径深度 {depth} · 每像素 {spp} 个采样 · 种子 {seed} · 追踪阴影。保留已编写的大气和图像预算。",
"render.cpu_volume_help": "CPU 大气使用间接照明。消光系数为零时,已保存的包围盒仍会保留,但不会产生雾。",
"render.cpu_volume_method_adopt": "使用间接照明的 CPU 光线追踪",
"render.cpu_volume_method_incompatible": "这些灯光、采样或大气设置无法转移到此方式。您的设置会保留。",
"render.cpu_volume_path_required": "启用间接照明以使用 CPU 大气。",
"render.volume_denoise_required": "关闭降噪以使用大气。",
"render.volume_glass_required": "此设置无法同时使用大气和玻璃。",
"render.volume_lighting_required": "选择环境或显式灯光配置以渲染大气。",
"render.volume_single_stratum_required": "将透射采样数设为 1 以渲染大气。"
}
}
@@ -0,0 +1,42 @@
{
"ar": {
"render.cpu_constant_direct_help": "بدون الإضاءة غير المباشرة، يمكن أن يظهر هذا المصدر الثابت في الانعكاسات وعبر الزجاج. لكنه لا يضيء الأسطح المعتمة. فعّل الإضاءة غير المباشرة لاستخدامه في إضاءة المشهد.",
"render.cpu_constant_enable_indirect_plan": "عمق المسار {depth}؛ أخذ عينات تدريجي بمعدل {spp} عينة/بكسل (البذرة {seed})؛ شعاع أولي واحد لكل عينة؛ ظلال متتبعة. يحتفظ بحجم الصورة وميزانية CPU والإعدادات الأخرى.",
"render.cpu_indirect_method_required": "اختر تتبع أشعة CPU لتفعيل وضع الإضاءة غير المباشرة هذا."
},
"de": {
"render.cpu_constant_direct_help": "Ohne indirekte Beleuchtung kann diese konstante Quelle in Reflexionen und durch Glas sichtbar sein. Sie beleuchtet keine undurchsichtigen Flächen. Schalte indirekte Beleuchtung ein, um sie als Szenenlicht zu nutzen.",
"render.cpu_constant_enable_indirect_plan": "Pfadtiefe {depth}; progressives Sampling mit {spp} Samples/Pixel (Seed {seed}); ein Primärstrahl pro Sample; Raytracing-Schatten. Bildgröße, CPU-Budget und andere Einstellungen bleiben erhalten.",
"render.cpu_indirect_method_required": "Wähle CPU-Raytracing, um diesen Modus für indirekte Beleuchtung einzuschalten."
},
"en": {
"render.cpu_constant_direct_help": "Without Indirect lighting, this constant source can appear in reflections and through Glass. It does not light opaque surfaces. Enable Indirect lighting to use it for scene lighting.",
"render.cpu_constant_enable_indirect_plan": "Path depth {depth}; progressive sampling at {spp} samples/pixel (seed {seed}); one primary ray per sample; traced shadows. Keeps your image size, CPU budget and other settings.",
"render.cpu_indirect_method_required": "Choose CPU ray tracing to enable this Indirect lighting mode."
},
"he": {
"render.cpu_constant_direct_help": "ללא תאורה עקיפה, מקור קבוע זה עשוי להופיע בהשתקפויות ודרך זכוכית. הוא אינו מאיר משטחים אטומים. יש להפעיל תאורה עקיפה כדי להשתמש בו לתאורת הסצנה.",
"render.cpu_constant_enable_indirect_plan": "עומק מסלול {depth}; דגימה הדרגתית עם {spp} דגימות לפיקסל (זרע {seed}); קרן ראשית אחת לכל דגימה; צללים במעקב קרניים. גודל התמונה, תקציב ה־CPU וההגדרות האחרות נשמרים.",
"render.cpu_indirect_method_required": "יש לבחור מעקב קרניים ב־CPU כדי להפעיל מצב זה של תאורה עקיפה."
},
"ja": {
"render.cpu_constant_direct_help": "間接照明を使わない場合、この一定の光源は反射やガラス越しに見えることがありますが、不透明な面は照らしません。シーンの照明に使うには、間接照明を有効にしてください。",
"render.cpu_constant_enable_indirect_plan": "パスの深さ {depth}、ピクセルあたり {spp} サンプルのプログレッシブサンプリング(シード {seed})、各サンプルに一次レイ1本、レイトレーシングの影を使用します。画像サイズ、CPU予算、その他の設定は保持します。",
"render.cpu_indirect_method_required": "この間接照明モードを有効にするには、CPUレイトレーシングを選択してください。"
},
"ko": {
"render.cpu_constant_direct_help": "간접 조명이 꺼져 있으면 이 일정한 광원은 반사나 유리를 통해 보일 수 있지만 불투명한 표면을 밝히지는 않습니다. 장면 조명으로 사용하려면 간접 조명을 켜세요.",
"render.cpu_constant_enable_indirect_plan": "경로 깊이 {depth}, 픽셀당 {spp}개 샘플의 점진적 샘플링(시드 {seed}), 샘플당 기본 광선 1개, 추적된 그림자를 사용합니다. 이미지 크기, CPU 예산 및 다른 설정을 유지합니다.",
"render.cpu_indirect_method_required": "이 간접 조명 모드를 켜려면 CPU 레이 트레이싱을 선택하세요."
},
"vi": {
"render.cpu_constant_direct_help": "Khi tắt Chiếu sáng gián tiếp, nguồn sáng đồng nhất này có thể xuất hiện trong phản xạ và qua Kính nhưng không chiếu sáng các bề mặt đục. Bật Chiếu sáng gián tiếp để dùng nguồn này chiếu sáng cảnh.",
"render.cpu_constant_enable_indirect_plan": "Độ sâu đường đi {depth}; lấy mẫu tăng dần với {spp} mẫu/pixel (hạt giống {seed}); một tia sơ cấp cho mỗi mẫu; bóng đổ dò tia. Giữ kích thước ảnh, ngân sách CPU và các cài đặt khác.",
"render.cpu_indirect_method_required": "Chọn dò tia CPU để bật chế độ Chiếu sáng gián tiếp này."
},
"zh": {
"render.cpu_constant_direct_help": "未启用间接照明时,此恒定光源可能出现在反射中或透过玻璃可见,但不会照亮不透明表面。要用它照亮场景,请启用间接照明。",
"render.cpu_constant_enable_indirect_plan": "路径深度 {depth};每像素 {spp} 个样本的渐进采样(种子 {seed});每个样本一条主光线;光线追踪阴影。保留图像尺寸、CPU预算和其他设置。",
"render.cpu_indirect_method_required": "请选择CPU光线追踪以启用此间接照明模式。"
}
}
@@ -0,0 +1,58 @@
{
"ar": {
"render.cpu_glass_enable_path": "تشغيل الإضاءة غير المباشرة",
"render.cpu_path_depth": "عمق المسار",
"render.cpu_path_help": "تحسب الضوء المرتد بين الأسطح المعتمة والمار عبر أشكال الزجاج المدعومة. تتطلب التصيير التدريجي وظلال تتبع الأشعة. يحسب عمق المسار الأسطح المضاءة وحدود الزجاج وأحداث التشتت بدءاً من أول تقاطع (1–16). يقتصر العمق 1 على الإضاءة المباشرة للأسطح المعتمة؛ وتسمح القيم الأعلى بمزيد من الارتدادات أو عبور الزجاج وقد تستغرق وقتاً أطول. أوقف الإضاءة غير المباشرة لاستخدام الانعكاسات المخصصة أو التباين الاتجاهي.",
"render.cpu_path_lighting": "إضاءة غير مباشرة",
"render.cpu_path_optics_reason": "تبقى إعداداتك كما هي. أوقف الإضاءة غير المباشرة لاستخدام الانعكاسات المخصصة أو التباين الاتجاهي."
},
"de": {
"render.cpu_glass_enable_path": "Indirekte Beleuchtung einschalten",
"render.cpu_path_depth": "Pfadtiefe",
"render.cpu_path_help": "Berechnet Licht, das zwischen undurchsichtigen Flächen und durch unterstützte Glasformen läuft. Erfordert progressives Rendern und Raytracing-Schatten. Die Pfadtiefe zählt beleuchtete Flächen, Glasgrenzen und Streuereignisse ab dem ersten Treffer (1–16). Tiefe 1 liefert direkte Beleuchtung auf undurchsichtigen Flächen; höhere Werte erlauben mehr Reflexionen oder Glasdurchgänge und können länger dauern. Für eigene Reflexionen oder Anisotropie die indirekte Beleuchtung ausschalten.",
"render.cpu_path_lighting": "Indirekte Beleuchtung",
"render.cpu_path_optics_reason": "Die Einstellungen bleiben erhalten. Für eigene Reflexionen oder Anisotropie die indirekte Beleuchtung ausschalten."
},
"en": {
"render.cpu_glass_enable_path": "Enable Indirect lighting",
"render.cpu_path_depth": "Path depth",
"render.cpu_path_help": "Adds light bouncing between opaque surfaces and through supported Glass shapes. Requires progressive rendering and traced shadows. Path depth counts shaded surfaces, Glass boundaries and scattering events, starting with the first hit (1–16). Depth 1 gives direct lighting on opaque surfaces; higher values allow more bounces or Glass crossings and may take longer. Turn off Indirect lighting to use custom reflections or anisotropy.",
"render.cpu_path_lighting": "Indirect lighting",
"render.cpu_path_optics_reason": "Your settings are kept. Turn off Indirect lighting to use custom reflections or anisotropy."
},
"he": {
"render.cpu_glass_enable_path": "הפעלת תאורה עקיפה",
"render.cpu_path_depth": "עומק מסלול",
"render.cpu_path_help": "מחשבת אור שמוחזר בין משטחים אטומים ועובר דרך צורות זכוכית נתמכות. נדרשים רינדור הדרגתי וצללים במעקב קרניים. עומק המסלול סופר משטחים מוצללים, גבולות זכוכית ואירועי פיזור החל מהמפגש הראשון (1–16). עומק 1 נותן תאורה ישירה למשטחים אטומים; ערכים גבוהים מאפשרים יותר החזרות או מעברים בזכוכית ועשויים לדרוש יותר זמן. יש לכבות תאורה עקיפה כדי להשתמש בהשתקפויות מותאמות או באנאיזוטרופיה.",
"render.cpu_path_lighting": "תאורה עקיפה",
"render.cpu_path_optics_reason": "ההגדרות שלך נשמרות. יש לכבות תאורה עקיפה כדי להשתמש בהשתקפויות מותאמות או באנאיזוטרופיה."
},
"ja": {
"render.cpu_glass_enable_path": "間接照明を有効にする",
"render.cpu_path_depth": "パス深度",
"render.cpu_path_help": "不透明な面の間で反射し、対応するガラス形状を通る光を計算します。プログレッシブ描画とレイトレーシングの影が必要です。パス深度は最初の交点から、照明を計算する面、ガラス境界、散乱イベントを数えます(1~16)。深度1は不透明面の直接照明のみです。値を上げると反射やガラスの通過を増やせますが、時間がかかる場合があります。独自の反射設定や異方性を使うには間接照明をオフにしてください。",
"render.cpu_path_lighting": "間接照明",
"render.cpu_path_optics_reason": "設定は保持されます。独自の反射設定や異方性を使うには間接照明をオフにしてください。"
},
"ko": {
"render.cpu_glass_enable_path": "간접 조명 켜기",
"render.cpu_path_depth": "경로 깊이",
"render.cpu_path_help": "불투명 표면 사이에서 반사되고 지원되는 유리 형상을 통과하는 빛을 계산합니다. 점진적 렌더링과 레이 추적 그림자가 필요합니다. 경로 깊이는 첫 교점부터 조명이 계산되는 표면, 유리 경계 및 산란 이벤트를 셉니다(1–16). 깊이 1은 불투명 표면의 직접 조명만 계산합니다. 값을 높이면 반사나 유리 통과가 늘어나며 시간이 더 걸릴 수 있습니다. 사용자 반사 설정이나 이방성을 사용하려면 간접 조명을 끄세요.",
"render.cpu_path_lighting": "간접 조명",
"render.cpu_path_optics_reason": "설정은 유지됩니다. 사용자 반사 설정이나 이방성을 사용하려면 간접 조명을 끄세요."
},
"vi": {
"render.cpu_glass_enable_path": "Bật Chiếu sáng gián tiếp",
"render.cpu_path_depth": "Độ sâu đường truyền",
"render.cpu_path_help": "Tính ánh sáng phản xạ giữa các bề mặt đục và truyền qua các hình kính được hỗ trợ. Cần kết xuất lũy tiến và bóng dò tia. Độ sâu đường đi đếm các bề mặt được chiếu sáng, ranh giới kính và các sự kiện tán xạ từ giao điểm đầu tiên (1–16). Độ sâu 1 chỉ chiếu sáng trực tiếp bề mặt đục; giá trị cao hơn cho phép thêm lần phản xạ hoặc xuyên kính và có thể lâu hơn. Tắt Chiếu sáng gián tiếp để dùng phản xạ tùy chỉnh hoặc dị hướng.",
"render.cpu_path_lighting": "Chiếu sáng gián tiếp",
"render.cpu_path_optics_reason": "Các thiết lập được giữ nguyên. Tắt Chiếu sáng gián tiếp để dùng phản xạ tùy chỉnh hoặc dị hướng."
},
"zh": {
"render.cpu_glass_enable_path": "启用间接照明",
"render.cpu_path_depth": "路径深度",
"render.cpu_path_help": "计算在不透明表面之间反射并穿过受支持玻璃形状的光。需要渐进渲染和光线追踪阴影。路径深度从首次相交开始计算着色表面、玻璃边界和散射事件(1–16)。深度 1 只计算不透明表面的直接照明;更高的值允许更多反射或玻璃穿越,也可能耗时更长。使用自定义反射或各向异性时,请关闭间接照明。",
"render.cpu_path_lighting": "间接照明",
"render.cpu_path_optics_reason": "设置会保留。使用自定义反射或各向异性时,请关闭间接照明。"
}
}
@@ -0,0 +1,42 @@
{
"en": {
"render.cpu_quality_budget_help": "Changes size, production sample cap, seed and CPU resource profile. Camera, lens, lighting, GI depth and effects are kept.",
"render.cpu_quality_progressive_required": "Enable progressive sampling below before choosing a CPU budget.",
"render.cpu_quality_summary": "{width} × {height} · {samples} sample cap · seed {seed} · {profile}"
},
"ar": {
"render.cpu_quality_budget_help": "يغير الحجم وحد عينات الإنتاج والبذرة وملف موارد CPU. تُحفظ الكاميرا والعدسة والإضاءة وعمق GI والتأثيرات.",
"render.cpu_quality_progressive_required": "فعّل أخذ العينات التدريجي أدناه قبل اختيار ميزانية CPU.",
"render.cpu_quality_summary": "{width} × {height} · حد العينات {samples} · البذرة {seed} · {profile}"
},
"de": {
"render.cpu_quality_budget_help": "Ändert Größe, Obergrenze der Produktionssamples, Startwert und CPU-Ressourcenprofil. Kamera, Objektiv, Beleuchtung, GI-Tiefe und Effekte bleiben erhalten.",
"render.cpu_quality_progressive_required": "Aktiviere unten progressive Samples, bevor du ein CPU-Budget auswählst.",
"render.cpu_quality_summary": "{width} × {height} · Sample-Grenze {samples} · Startwert {seed} · {profile}"
},
"he": {
"render.cpu_quality_budget_help": "משנה גודל, תקרת דגימות הפקה, זרע ופרופיל משאבי CPU. המצלמה, העדשה, התאורה, עומק GI והאפקטים נשמרים.",
"render.cpu_quality_progressive_required": "יש להפעיל דגימה מתקדמת למטה לפני בחירת תקציב CPU.",
"render.cpu_quality_summary": "{width} × {height} · תקרת דגימות {samples} · זרע {seed} · {profile}"
},
"ja": {
"render.cpu_quality_budget_help": "サイズ、本番サンプル数の上限、シード、CPUリソースプロファイルを変更します。カメラ、レンズ、照明、GI深度、エフェクトは維持されます。",
"render.cpu_quality_progressive_required": "CPU予算を選ぶ前に、下でプログレッシブサンプリングを有効にしてください。",
"render.cpu_quality_summary": "{width} × {height} · サンプル上限 {samples} · シード {seed} · {profile}"
},
"ko": {
"render.cpu_quality_budget_help": "크기, 제작 샘플 상한, 시드와 CPU 리소스 프로필을 변경합니다. 카메라, 렌즈, 조명, GI 깊이와 효과는 유지됩니다.",
"render.cpu_quality_progressive_required": "CPU 예산을 선택하기 전에 아래에서 점진적 샘플링을 활성화하세요.",
"render.cpu_quality_summary": "{width} × {height} · 샘플 상한 {samples} · 시드 {seed} · {profile}"
},
"vi": {
"render.cpu_quality_budget_help": "Thay đổi kích thước, trần mẫu sản xuất, hạt giống và cấu hình tài nguyên CPU. Giữ nguyên máy ảnh, ống kính, ánh sáng, độ sâu GI và hiệu ứng.",
"render.cpu_quality_progressive_required": "Bật lấy mẫu tiến dần bên dưới trước khi chọn ngân sách CPU.",
"render.cpu_quality_summary": "{width} × {height} · trần mẫu {samples} · hạt giống {seed} · {profile}"
},
"zh": {
"render.cpu_quality_budget_help": "更改尺寸、正式样本上限、种子和CPU资源配置。保留相机、镜头、照明、GI深度和效果。",
"render.cpu_quality_progressive_required": "选择CPU预算前,请先在下方启用渐进采样。",
"render.cpu_quality_summary": "{width} × {height} · 样本上限 {samples} · 种子 {seed} · {profile}"
}
}
@@ -0,0 +1,82 @@
{
"ar": {
"render.appearance_editor.discard_saved_edit": "تجاهل التعديلات البصرية غير المحفوظة",
"render.cpu_subsurface_denoise_reason": "يتطلب التشتت داخل الزجاج على CPU إيقاف إزالة الضجيج. تبقى إعداداتك محفوظة.",
"render.cpu_subsurface_help": "يغيّر التشتت اتجاه الضوء داخل الزجاج المغلق. تتطلب المعدلات الموجبة إضاءة غير مباشرة مع إيقاف إزالة الضجيج. المعدل هو عدد التصادمات لكل مم؛ لا يضيف الصفر أي تشتت. يفضّل انحياز الاتجاه الموجب الأمام، والسالب الخلف.",
"render.material_cpu_glass_only": "تتطلب خرائط ملفات الصور المؤقتة GPU. يمكن لـCPU استخدام خرائط الأسطح المضمنة المحفوظة.",
"render.material_cpu_optics_limits": "شغّل الإضاءة غير المباشرة لأشكال الزجاج المغلقة المدعومة والتداخل المتحقق منه والتشتت داخل الزجاج. يمكن للصفائح الرقيقة تمرير الضوء للظلال. وإلا فاستخدم صفيحة مستوية أو صندوقًا محاذيًا للمحاور. تظل الأشكال المتلامسة والظلال النافذة عبر الزجاج المغلق غير مدعومة. تبقى قيمك محفوظة.",
"render.material_cpu_saved_runtime_conflict": "لا يمكن الجمع بين البصريات المحفوظة على CPU والارتباطات البصرية المؤقتة السابقة. احفظ البصريات التي راجعتها أو أزل الارتباط المؤقت المتعارض.",
"render.material_raytrace_required": "يتطلب الزجاج الفيزيائي تتبع الأشعة على CPU أو تتبع المسارات على GPU.",
"render.material_saved_edit_pending": "هذه التعديلات البصرية غير محفوظة. احفظ القيم التي راجعتها، أو أزل التعيين المحفوظ، أو تجاهل التعديلات. لا يمكن للارتباطات المؤقتة السابقة والمؤثرات غير المدعومة تجاوز الهدف المحفوظ."
},
"de": {
"render.appearance_editor.discard_saved_edit": "Ungespeicherte Optikänderungen verwerfen",
"render.cpu_subsurface_denoise_reason": "Streuung im Glas auf der CPU benötigt ausgeschaltete Rauschreduzierung. Die Einstellungen bleiben erhalten.",
"render.cpu_subsurface_help": "Streuung lenkt Licht im Inneren geschlossenen Glases um. Positive Raten benötigen indirekte Beleuchtung ohne Rauschreduzierung. Die Rate zählt Kollisionen pro mm; 0 fügt keine Streuung hinzu. Positive Richtungswerte bevorzugen vorwärts, negative rückwärts.",
"render.material_cpu_glass_only": "Temporäre Bilddatei-Karten benötigen die GPU. Die CPU kann gespeicherte integrierte Oberflächenkarten verwenden.",
"render.material_cpu_optics_limits": "Indirekte Beleuchtung für unterstützte geschlossene Glasformen, geprüfte Verschachtelung und Streuung im Inneren einschalten. Dünne Scheiben können Licht für Schatten durchlassen. Sonst eine ebene Scheibe oder achsenparallele Box verwenden. Berührende Formen und durch geschlossenes Glas übertragene Schatten werden weiterhin nicht unterstützt. Die Werte bleiben erhalten.",
"render.material_cpu_saved_runtime_conflict": "Gespeicherte CPU-Optik kann nicht mit temporären optischen Bindungen kombiniert werden. Geprüfte Optik speichern oder die temporäre Bindung entfernen.",
"render.material_raytrace_required": "Physikalisches Glas benötigt CPU-Raytracing oder GPU-Pathtracing.",
"render.material_saved_edit_pending": "Diese Optikänderungen sind ungespeichert. Geprüfte Werte speichern, die gespeicherte Zuordnung entfernen oder Änderungen verwerfen. Temporäre Bindungen und nicht unterstützte Effekte können das gespeicherte Ziel nicht überschreiben."
},
"en": {
"render.appearance_editor.discard_saved_edit": "Discard unsaved optical edits",
"render.cpu_subsurface_denoise_reason": "CPU subsurface scattering needs Denoise off. Your settings are kept.",
"render.cpu_subsurface_help": "Scattering redirects light inside closed Glass. Positive rates need Indirect lighting with Denoise off. The rate is collisions per mm; 0 adds no scattering. Positive phase bias favors forward directions; negative favors backward.",
"render.material_cpu_glass_only": "Temporary image-file maps require GPU. CPU can use saved built-in surface maps.",
"render.material_cpu_optics_limits": "Turn on Indirect lighting for supported closed Glass shapes, certified nesting and subsurface scattering. Thin sheets can pass shadows. Otherwise use a planar sheet or axis-aligned box. Touching shapes and transmitted shadows through closed Glass remain unsupported. Your values are kept.",
"render.material_cpu_saved_runtime_conflict": "Saved CPU optics cannot combine with legacy temporary optical bindings. Save the reviewed optics or remove the conflicting temporary binding.",
"render.material_raytrace_required": "Physical Glass requires CPU ray tracing or GPU path tracing.",
"render.material_saved_edit_pending": "These optical edits are unsaved. Save reviewed values, remove the saved assignment, or discard the edits. Legacy temporary bindings and unsupported effects cannot override the saved target."
},
"he": {
"render.appearance_editor.discard_saved_edit": "ביטול שינויים אופטיים שלא נשמרו",
"render.cpu_subsurface_denoise_reason": "פיזור בתוך זכוכית ב־CPU דורש הסרת רעש כבויה. ההגדרות נשמרות.",
"render.cpu_subsurface_help": "פיזור משנה את כיוון האור בתוך זכוכית סגורה. קצב חיובי דורש תאורה עקיפה והסרת רעש כבויה. הקצב הוא מספר התנגשויות למ״מ; 0 אינו מוסיף פיזור. הטיית כיוון חיובית מעדיפה קדימה, ושלילית אחורה.",
"render.material_cpu_glass_only": "מפות זמניות מקובצי תמונה דורשות GPU. ב־CPU אפשר להשתמש במפות משטח מובנות שנשמרו.",
"render.material_cpu_optics_limits": "יש להפעיל תאורה עקיפה לצורות זכוכית סגורות נתמכות, לקינון שנבדק ולפיזור בתוך הזכוכית. יריעות דקות יכולות להעביר אור לצללים. אחרת יש להשתמש ביריעה מישורית או בתיבה מקבילה לצירים. צורות נוגעות וצללים העוברים בזכוכית סגורה עדיין אינם נתמכים. הערכים נשמרים.",
"render.material_cpu_saved_runtime_conflict": "אי אפשר לשלב הגדרות אופטיות שמורות ב־CPU עם הקישורים האופטיים הזמניים הישנים. יש לשמור את ההגדרות שנבדקו או להסיר את הקישור הזמני המתנגש.",
"render.material_raytrace_required": "זכוכית פיזיקלית דורשת עקיבת קרניים ב־CPU או עקיבת נתיבים ב־GPU.",
"render.material_saved_edit_pending": "השינויים האופטיים האלה לא נשמרו. יש לשמור את הערכים שנבדקו, להסיר את ההקצאה השמורה או לבטל את השינויים. קישורים זמניים ישנים ואפקטים שאינם נתמכים אינם יכולים לעקוף את היעד השמור."
},
"ja": {
"render.appearance_editor.discard_saved_edit": "未保存の光学設定の変更を破棄",
"render.cpu_subsurface_denoise_reason": "CPUの内部散乱ではノイズ除去をオフにしてください。設定値は保持されます。",
"render.cpu_subsurface_help": "散乱は閉じたガラスの内部で光の方向を変えます。正の値には間接照明を使い、ノイズ除去をオフにします。率は1mmあたりの衝突回数です。0では散乱を加えません。方向の偏りが正なら前方、負なら後方を優先します。",
"render.material_cpu_glass_only": "一時画像ファイルのマップにはGPUが必要です。CPUでは保存済みの組み込み表面マップを使用できます。",
"render.material_cpu_optics_limits": "対応する閉じたガラス形状、検証済みの入れ子形状、内部散乱には間接照明をオンにしてください。薄いシートは影に光を透過できます。それ以外は平面シートか軸平行の箱を使います。接触する形状と閉じたガラスを通る透過影には未対応です。値は保持されます。",
"render.material_cpu_saved_runtime_conflict": "保存済みCPU光学設定と一時的な光学割り当ては併用できません。確認した設定を保存するか、競合する一時割り当てを削除してください。",
"render.material_raytrace_required": "物理ガラスにはCPUレイトレーシングまたはGPUパストレーシングが必要です。",
"render.material_saved_edit_pending": "光学設定の変更は未保存です。確認した値を保存するか、保存済み割り当てを削除するか、変更を破棄してください。一時割り当てや未対応の効果で保存済み対象を上書きできません。"
},
"ko": {
"render.appearance_editor.discard_saved_edit": "저장하지 않은 광학 편집 버리기",
"render.cpu_subsurface_denoise_reason": "CPU 내부 산란을 사용하려면 노이즈 제거를 끄세요. 설정은 유지됩니다.",
"render.cpu_subsurface_help": "산란은 닫힌 유리 내부에서 빛의 방향을 바꿉니다. 양수 비율에는 간접광을 사용하고 노이즈 제거를 끄세요. 비율은 mm당 충돌 횟수이며, 0은 산란을 추가하지 않습니다. 방향 편향이 양수이면 전방, 음수이면 후방을 선호합니다.",
"render.material_cpu_glass_only": "임시 이미지 파일 맵에는 GPU가 필요합니다. CPU는 저장된 내장 표면 맵을 사용할 수 있습니다.",
"render.material_cpu_optics_limits": "지원되는 닫힌 유리 형상, 검증된 중첩 및 내부 산란에는 간접 조명을 켜세요. 얇은 시트는 그림자에 빛을 통과시킬 수 있습니다. 그 외에는 평면 시트나 축 정렬 상자를 사용하세요. 서로 닿는 형상과 닫힌 유리를 통과하는 투과 그림자는 아직 지원하지 않습니다. 값은 유지됩니다.",
"render.material_cpu_saved_runtime_conflict": "저장된 CPU 광학 설정은 임시 광학 바인딩과 함께 사용할 수 없습니다. 검토한 설정을 저장하거나 충돌하는 임시 바인딩을 제거하세요.",
"render.material_raytrace_required": "물리 유리에는 CPU 레이 트레이싱이나 GPU 패스 트레이싱이 필요합니다.",
"render.material_saved_edit_pending": "광학 편집을 아직 저장하지 않았습니다. 검토한 값을 저장하거나 저장된 할당을 제거하거나 편집을 버리세요. 임시 바인딩과 지원하지 않는 효과는 저장된 대상을 덮어쓸 수 없습니다."
},
"vi": {
"render.appearance_editor.discard_saved_edit": "Hủy chỉnh sửa quang học chưa lưu",
"render.cpu_subsurface_denoise_reason": "Tán xạ bên trong kính trên CPU cần tắt Khử nhiễu. Các cài đặt được giữ nguyên.",
"render.cpu_subsurface_help": "Tán xạ đổi hướng ánh sáng bên trong kính kín. Tốc độ dương cần Chiếu sáng gián tiếp và tắt Khử nhiễu. Tốc độ là số va chạm mỗi mm; 0 không thêm tán xạ. Độ lệch hướng dương ưu tiên phía trước, âm ưu tiên phía sau.",
"render.material_cpu_glass_only": "Bản đồ từ tệp ảnh tạm thời yêu cầu GPU. CPU có thể dùng bản đồ bề mặt tích hợp đã lưu.",
"render.material_cpu_optics_limits": "Bật Chiếu sáng gián tiếp cho hình kính kín được hỗ trợ, cấu trúc lồng nhau đã kiểm chứng và tán xạ bên trong kính. Tấm mỏng có thể truyền ánh sáng cho bóng. Nếu không, dùng tấm phẳng hoặc hộp thẳng theo trục. Chưa hỗ trợ hình chạm nhau và bóng truyền qua kính kín. Các giá trị được giữ nguyên.",
"render.material_cpu_saved_runtime_conflict": "Không thể kết hợp quang học CPU đã lưu với các liên kết quang học tạm thời kiểu cũ. Hãy lưu quang học đã xem xét hoặc xóa liên kết tạm thời xung đột.",
"render.material_raytrace_required": "Kính vật lý yêu cầu dò tia trên CPU hoặc dò đường đi trên GPU.",
"render.material_saved_edit_pending": "Các chỉnh sửa quang học này chưa được lưu. Hãy lưu các giá trị đã xem xét, xóa phép gán đã lưu hoặc hủy chỉnh sửa. Liên kết tạm thời kiểu cũ và hiệu ứng chưa được hỗ trợ không thể ghi đè đích đã lưu."
},
"zh": {
"render.appearance_editor.discard_saved_edit": "放弃未保存的光学编辑",
"render.cpu_subsurface_denoise_reason": "CPU 内部散射需要关闭降噪。设置会保留。",
"render.cpu_subsurface_help": "散射会改变闭合玻璃内部的光线方向。正散射率需要开启间接光照并关闭降噪。散射率表示每毫米的碰撞次数;0 不增加散射。方向偏置为正时偏向前方,为负时偏向后方。",
"render.material_cpu_glass_only": "临时图像文件贴图需要GPU。CPU可以使用已保存的内置表面贴图。",
"render.material_cpu_optics_limits": "启用间接光照可使用受支持的闭合玻璃形状、经验证的嵌套结构及内部散射。薄片可以透射阴影。否则请使用平面薄片或轴对齐盒体。仍不支持相互接触的形状及穿过闭合玻璃的透射阴影。设置值会保留。",
"render.material_cpu_saved_runtime_conflict": "已保存的CPU光学设置不能与临时光学绑定组合。请保存已审阅的设置或移除冲突的临时绑定。",
"render.material_raytrace_required": "物理玻璃需要CPU光线追踪或GPU路径追踪。",
"render.material_saved_edit_pending": "这些光学编辑尚未保存。请保存已审阅的值、移除已保存的分配或放弃编辑。临时绑定和不支持的效果不能覆盖已保存的目标。"
}
}
@@ -0,0 +1,32 @@
{
"ar": {
"render.cpu_maps_saved_help": "يمكن لتصيير CPU استخدام خرائط الأسطح المعتمة المحفوظة: اختر سطحًا هنا ثم احفظ خرائطه في اللقطة. مع الإضاءة غير المباشرة، يمكن استخدام الزجاج في جسم آخر؛ لا يمكن استخدام الزجاج في الجسم ذي الخرائط. يجب إيقاف الانعكاسات المختارة والتباين الاتجاهي والتأثيرات الإجرائية. تتطلب مسارات ملفات الخرائط المؤقتة GPU."
},
"de": {
"render.cpu_maps_saved_help": "CPU-Renderings können gespeicherte Karten undurchsichtiger Flächen verwenden: Oberfläche wählen und die Karten in der Aufnahme speichern. Mit indirekter Beleuchtung darf ein anderer Körper Glas verwenden; der Körper mit Karten darf kein Glas sein. Ausgewählte Reflexionen, Anisotropie und prozedurale Effekte ausschalten. Temporäre Kartendateipfade verwenden die GPU."
},
"en": {
"render.cpu_maps_saved_help": "CPU renders can use saved opaque maps: choose a surface here, then Save maps to shot. With Indirect lighting, Glass can be on another body; a mapped body cannot use Glass. Selected reflections, anisotropy and procedural surface effects must be off. Temporary map file paths use GPU."
},
"es": {
"render.cpu_maps_saved_help": "Los renders CPU pueden usar mapas opacos guardados: elige una superficie y guarda sus mapas en la toma. Con Iluminación indirecta, otro cuerpo puede usar Vidrio; el cuerpo con mapas no puede usar Vidrio. Desactiva los reflejos seleccionados, la anisotropía y los efectos procedurales. Las rutas temporales de archivos de mapas usan GPU."
},
"fr": {
"render.cpu_maps_saved_help": "Le rendu CPU accepte les cartes opaques enregistrées : choisissez une surface, puis enregistrez ses cartes dans la vue. Avec l’éclairage indirect, un autre corps peut utiliser Verre ; le corps avec les cartes ne le peut pas. Désactivez les réflexions sélectionnées, l’anisotropie et les effets procéduraux. Les fichiers temporaires de cartes utilisent le GPU."
},
"he": {
"render.cpu_maps_saved_help": "רינדור CPU יכול להשתמש במפות אטומות שמורות: בחרו כאן משטח ושמרו את המפות בתצלום. עם תאורה עקיפה, גוף אחר יכול להשתמש בזכוכית; הגוף בעל המפות אינו יכול להשתמש בזכוכית. יש לכבות החזרות שנבחרו, אנאיזוטרופיה ואפקטים פרוצדורליים. נתיבים זמניים לקובצי מפות משתמשים ב־GPU."
},
"ja": {
"render.cpu_maps_saved_help": "CPUレンダリングは保存された不透明サーフェスマップを使えます。サーフェスを選び、ショットにマップを保存してください。間接光を使う場合、別のボディにはガラスを使えますが、マップを持つボディには使えません。選択した反射、異方性、プロシージャル効果はオフにします。一時的なマップファイルにはGPUを使います。"
},
"ko": {
"render.cpu_maps_saved_help": "CPU 렌더링은 저장한 불투명 표면 맵을 사용할 수 있습니다. 표면을 선택하고 샷에 맵을 저장하세요. 간접광을 사용하면 다른 바디에 유리를 사용할 수 있지만, 맵이 있는 바디에는 유리를 사용할 수 없습니다. 선택 반사, 비등방성 및 절차적 효과는 끄세요. 임시 맵 파일 경로는 GPU를 사용합니다."
},
"vi": {
"render.cpu_maps_saved_help": "Kết xuất CPU hỗ trợ bản đồ bề mặt đục đã lưu: chọn bề mặt rồi lưu bản đồ vào ảnh chụp. Với chiếu sáng gián tiếp, thân khác có thể dùng kính; thân có bản đồ không thể dùng kính. Tắt phản xạ được chọn, dị hướng và hiệu ứng thủ tục. Đường dẫn tạm tới tệp bản đồ dùng GPU."
},
"zh": {
"render.cpu_maps_saved_help": "CPU 渲染可使用已保存的不透明表面贴图:在此选择表面,再将贴图保存到镜头。使用间接光照时,其他实体可以使用玻璃;带有贴图的实体不能使用玻璃。请关闭选定的反射、各向异性和程序化表面效果。临时贴图文件路径使用 GPU。"
}
}
@@ -0,0 +1,26 @@
{
"ar": {
"render.cpu_denoise_help": "يقلل التشويش في كل تحديث للصورة. قد ينعّم التفاصيل الدقيقة وحواف عمق المجال. يبقى EXR الخام دون تغيير. تستخدم تعديلات الصورة المكتملة صورتها الخام ولا تطبّق تقليل التشويش مرة أخرى."
},
"de": {
"render.cpu_denoise_help": "Verringert das Rauschen bei jeder Bildaktualisierung. Feine Details und Übergänge der Tiefenschärfe können weicher werden. Die EXR-Rohdaten bleiben unverändert. Nachträgliche Anpassungen eines fertigen Bildes verwenden dessen Rohbild und wenden die Rauschreduzierung nicht erneut an."
},
"en": {
"render.cpu_denoise_help": "Reduces noise in every image update. It may soften fine detail and depth-of-field edges. Raw EXR is unchanged. Adjusting a completed image uses its raw image and does not apply noise reduction again."
},
"he": {
"render.cpu_denoise_help": "מפחיתה רעש בכל עדכון תמונה. היא עלולה לרכך פרטים עדינים וקצוות של עומק שדה. קובץ EXR הגולמי אינו משתנה. התאמות של תמונה שהושלמה משתמשות בתמונה הגולמית שלה ואינן מחילות שוב הפחתת רעש."
},
"ja": {
"render.cpu_denoise_help": "画像が更新されるたびにノイズを減らします。細部や被写界深度によるぼけの境界が柔らかくなる場合があります。RAW EXRは変更されません。完成画像の調整にはそのRAW画像を使用し、ノイズ低減は再適用しません。"
},
"ko": {
"render.cpu_denoise_help": "이미지가 갱신될 때마다 노이즈를 줄입니다. 미세한 디테일과 피사계 심도 경계가 부드러워질 수 있습니다. 원본 EXR은 바뀌지 않습니다. 완성된 이미지를 조정할 때는 해당 원본 이미지를 사용하며 노이즈 감소를 다시 적용하지 않습니다."
},
"vi": {
"render.cpu_denoise_help": "Giảm nhiễu mỗi khi ảnh được cập nhật. Có thể làm mềm chi tiết nhỏ và biên vùng mờ do độ sâu trường ảnh. EXR thô không thay đổi. Khi điều chỉnh ảnh đã hoàn tất, hệ thống dùng ảnh thô của ảnh đó và không áp dụng giảm nhiễu lần nữa."
},
"zh": {
"render.cpu_denoise_help": "每次图像更新时减少噪点,可能使细节和景深模糊边缘变柔。原始EXR保持不变。调整已完成的图像时使用其原始图像,不会再次降噪。"
}
}
@@ -0,0 +1,50 @@
{
"ar": {
"render.cpu_studio_enable": "تفعيل الإضاءة غير المباشرة لهذا التوزيع",
"render.cpu_studio_enable_apply": "تفعيل الإضاءة غير المباشرة وتطبيقها",
"render.cpu_studio_indirect_help": "يحتاج توزيع الشمس هذا إلى إضاءة غير مباشرة بعمق 8 وظلال متتبعة وعينة كاميرا واحدة لكل مرور. إذا كان التصيير التدريجي معطلاً، يبدأ بـ 16 عينة لكل بكسل وبذرة 0. تُحفظ الإعدادات التدريجية الحالية وملف الموارد. تمنع الإعدادات غير المتوافقة التطبيق دون تغييرها.",
"render.cpu_studio_radiance_limit": "سطوع المصدر المساحي في هذا التوزيع هو {radiance}، ويتجاوز حد CPU البالغ 64. تُحفظ الإضاءة المؤلَّفة؛ اختر توزيعاً آخر أو GPU."
},
"de": {
"render.cpu_studio_enable": "Indirekte Beleuchtung für dieses Lichtset aktivieren",
"render.cpu_studio_enable_apply": "Indirekte Beleuchtung aktivieren und anwenden",
"render.cpu_studio_indirect_help": "Dieses Sonnen-Lichtset benötigt indirekte Beleuchtung mit Tiefe 8, verfolgte Schatten und eine Kamerastichprobe pro Durchlauf. Ist progressives Rendern ausgeschaltet, beginnt es mit 16 Stichproben pro Pixel und Startwert 0. Vorhandene progressive Einstellungen und das Ressourcenprofil bleiben erhalten. Unvereinbare Einstellungen verhindern die Anwendung, ohne sie zu ändern.",
"render.cpu_studio_radiance_limit": "Dieses Lichtset hat eine Flächenstrahldichte von {radiance}, über der CPU-Grenze von 64. Die vorgegebene Beleuchtung bleibt erhalten; wählen Sie ein anderes Lichtset oder GPU."
},
"en": {
"render.cpu_studio_enable": "Enable indirect lighting for this rig",
"render.cpu_studio_enable_apply": "Enable indirect lighting and apply",
"render.cpu_studio_indirect_help": "This Sun rig needs indirect lighting at depth 8, traced shadows and one camera sample per pass. If progressive rendering is off, it starts at 16 samples per pixel with seed 0. Existing progressive settings and the resource profile are kept. Incompatible settings stop the apply without changing them.",
"render.cpu_studio_radiance_limit": "This rig has area radiance {radiance}, above the CPU limit of 64. Its authored lighting is kept; choose another rig or GPU."
},
"he": {
"render.cpu_studio_enable": "הפעלת תאורה עקיפה עבור מערך זה",
"render.cpu_studio_enable_apply": "הפעלת תאורה עקיפה והחלה",
"render.cpu_studio_indirect_help": "מערך השמש הזה דורש תאורה עקיפה בעומק 8, צללים מחושבים ודגימת מצלמה אחת בכל מעבר. אם רינדור הדרגתי כבוי, הוא מתחיל ב־16 דגימות לפיקסל עם זרע 0. הגדרות הדרגתיות קיימות ופרופיל המשאבים נשמרים. הגדרות שאינן תואמות עוצרות את ההחלה בלי לשנותן.",
"render.cpu_studio_radiance_limit": "עוצמת מקור השטח במערך זה היא {radiance}, מעל מגבלת CPU של 64. התאורה המקורית נשמרת; בחרו מערך אחר או GPU."
},
"ja": {
"render.cpu_studio_enable": "このリグで間接照明を有効にする",
"render.cpu_studio_enable_apply": "間接照明を有効にして適用",
"render.cpu_studio_indirect_help": "この太陽リグには深度8の間接照明、追跡した影、パスごとに1つのカメラサンプルが必要です。プログレッシブ描画がオフなら、ピクセルごと16サンプル、シード0で開始します。既存のプログレッシブ設定とリソースプロファイルは維持します。非対応の設定がある場合は変更せず適用を中止します。",
"render.cpu_studio_radiance_limit": "このリグの面光源の放射輝度は{radiance}で、CPU上限64を超えます。作成された照明は維持します。別のリグかGPUを選んでください。"
},
"ko": {
"render.cpu_studio_enable": "이 리그의 간접 조명 켜기",
"render.cpu_studio_enable_apply": "간접 조명을 켜고 적용",
"render.cpu_studio_indirect_help": "이 태양 리그에는 깊이 8의 간접 조명, 추적된 그림자, 패스당 카메라 샘플 1개가 필요합니다. 점진적 렌더링이 꺼져 있으면 픽셀당 샘플 16개와 시드 0으로 시작합니다. 기존 점진적 설정과 리소스 프로필은 유지됩니다. 호환되지 않는 설정은 변경하지 않고 적용을 중단합니다.",
"render.cpu_studio_radiance_limit": "이 리그의 면 광원 방사 휘도 {radiance}은(는) CPU 한도 64를 넘습니다. 작성된 조명은 유지됩니다. 다른 리그나 GPU를 선택하세요."
},
"vi": {
"render.cpu_studio_enable": "Bật chiếu sáng gián tiếp cho bộ đèn này",
"render.cpu_studio_enable_apply": "Bật chiếu sáng gián tiếp và áp dụng",
"render.cpu_studio_indirect_help": "Bộ đèn Mặt Trời này cần chiếu sáng gián tiếp với độ sâu 8, bóng được truy vết và một mẫu máy ảnh mỗi lượt. Nếu kết xuất tăng dần đang tắt, bắt đầu với 16 mẫu mỗi điểm ảnh và hạt giống 0. Giữ nguyên thiết lập tăng dần và hồ sơ tài nguyên hiện có. Thiết lập không tương thích sẽ ngăn áp dụng mà không thay đổi chúng.",
"render.cpu_studio_radiance_limit": "Độ chói bức xạ đèn diện tích của bộ này là {radiance}, vượt giới hạn CPU 64. Giữ nguyên ánh sáng đã tạo; hãy chọn bộ đèn khác hoặc GPU."
},
"zh": {
"render.cpu_studio_enable": "为此灯组启用间接光照",
"render.cpu_studio_enable_apply": "启用间接光照并应用",
"render.cpu_studio_indirect_help": "此太阳灯组需要深度为8的间接光照、追踪阴影,以及每轮一个相机采样。如果渐进渲染关闭,则以每像素16个采样和种子0开始。保留现有渐进设置和资源配置。不兼容的设置会阻止应用,且不会被更改。",
"render.cpu_studio_radiance_limit": "此灯组的面光源辐射亮度为{radiance},超过CPU上限64。保留原有灯光,请选择其他灯组或GPU。"
}
}
+66
View File
@@ -0,0 +1,66 @@
{
"ar": {
"render.cpu_celestial_adopt": "تتبع الأشعة على المعالج مع إضاءة غير مباشرة · عمق المسار {depth} · {spp} عينة لكل بكسل · ظلال متتبعة · بذرة {seed} · عينة أولية واحدة",
"render.cpu_celestial_adopt_label": "تتبع الأشعة على المعالج مع إضاءة غير مباشرة",
"render.cpu_celestial_path_required": "تستخدم الشمس المحدودة والسماء المتدرجة الإضاءة غير المباشرة على المعالج.",
"render.cpu_celestial_switch": "اختر تتبع الأشعة على المعالج مع الإضاءة غير المباشرة للاحتفاظ بهذه الشمس أو السماء.",
"render.cpu_celestial_unportable": "أبقِ تتبع المسارات على المعالج الرسومي لإعدادات عينات النفاذ الحالية.",
"render.cpu_sky_bounds": "يجب أن يكون دوران السماء محدودًا ضمن ±1,000,000 راديان، وأن تكون الشدة بين 0 و100."
},
"de": {
"render.cpu_celestial_adopt": "CPU-Raytracing mit indirekter Beleuchtung · Pfadtiefe {depth} · {spp} SPP · verfolgte Schatten · Seed {seed} · 1 Primärsample",
"render.cpu_celestial_adopt_label": "CPU-Raytracing mit indirekter Beleuchtung",
"render.cpu_celestial_path_required": "Endliche Sonne und Verlaufshimmel verwenden indirekte CPU-Beleuchtung.",
"render.cpu_celestial_switch": "Wähle CPU-Raytracing mit indirekter Beleuchtung, um diese Sonne oder diesen Himmel beizubehalten.",
"render.cpu_celestial_unportable": "Behalte GPU-Pathtracing für die aktuellen Transmissions-Sample-Einstellungen bei.",
"render.cpu_sky_bounds": "Die Himmelsrotation muss endlich innerhalb ±1.000.000 Radiant liegen; die Intensität muss 0–100 sein."
},
"en": {
"render.cpu_celestial_adopt": "CPU ray tracing with indirect lighting · path depth {depth} · {spp} SPP · traced shadows · seed {seed} · 1 primary sample",
"render.cpu_celestial_adopt_label": "CPU ray tracing with indirect lighting",
"render.cpu_celestial_path_required": "Finite Sun and gradient Sky use CPU indirect lighting.",
"render.cpu_celestial_switch": "Choose CPU ray tracing with indirect lighting to keep this Sun or Sky.",
"render.cpu_celestial_unportable": "Keep GPU path tracing for the current transmission sample settings.",
"render.cpu_sky_bounds": "Sky rotation must be finite within ±1,000,000 radians; intensity must be 0–100."
},
"he": {
"render.cpu_celestial_adopt": "מעקב קרניים במעבד עם תאורה עקיפה · עומק מסלול {depth} · {spp} דגימות לפיקסל · צללים במעקב · זרע {seed} · דגימה ראשית אחת",
"render.cpu_celestial_adopt_label": "מעקב קרניים במעבד עם תאורה עקיפה",
"render.cpu_celestial_path_required": "שמש בעלת גודל סופי ושמיים מדורגים משתמשים בתאורה עקיפה במעבד.",
"render.cpu_celestial_switch": "כדי לשמור על השמש או השמיים האלה, יש לבחור מעקב קרניים במעבד עם תאורה עקיפה.",
"render.cpu_celestial_unportable": "יש לשמור על מעקב מסלולים במעבד הגרפי עבור הגדרות דגימות ההעברה הנוכחיות.",
"render.cpu_sky_bounds": "סיבוב השמיים חייב להיות סופי בטווח ±1,000,000 רדיאנים, והעוצמה חייבת להיות בטווח 0–100."
},
"ja": {
"render.cpu_celestial_adopt": "間接照明付き CPU レイトレーシング · パス深度 {depth} · {spp} SPP · 追跡シャドウ · シード {seed} · プライマリサンプル1個",
"render.cpu_celestial_adopt_label": "間接照明付き CPU レイトレーシング",
"render.cpu_celestial_path_required": "有限の太陽とグラデーション空は CPU の間接照明を使用します。",
"render.cpu_celestial_switch": "この太陽や空を維持するには、間接照明を使用する CPU レイトレーシングを選択してください。",
"render.cpu_celestial_unportable": "現在の透過サンプル設定には GPU パストレーシングを使用してください。",
"render.cpu_sky_bounds": "空の回転は ±1,000,000 ラジアン以内の有限値、強度は 0~100 にしてください。"
},
"ko": {
"render.cpu_celestial_adopt": "간접 조명을 사용하는 CPU 레이 트레이싱 · 경로 깊이 {depth} · {spp} SPP · 추적 그림자 · 시드 {seed} · 기본 샘플1개",
"render.cpu_celestial_adopt_label": "간접 조명을 사용하는 CPU 레이 트레이싱",
"render.cpu_celestial_path_required": "유한 태양과 그라데이션 하늘은 CPU 간접 조명을 사용합니다.",
"render.cpu_celestial_switch": "이 태양 또는 하늘을 유지하려면 간접 조명을 사용하는 CPU 레이 트레이싱을 선택하세요.",
"render.cpu_celestial_unportable": "현재 투과 샘플 설정에는 GPU 경로 추적을 유지하세요.",
"render.cpu_sky_bounds": "하늘 회전은 ±1,000,000 라디안 이내의 유한 값이어야 하며 강도는 0~100이어야 합니다."
},
"vi": {
"render.cpu_celestial_adopt": "Dò tia CPU với chiếu sáng gián tiếp · độ sâu đường đi {depth} · {spp} SPP · bóng đổ dò tia · seed {seed} · 1 mẫu chính",
"render.cpu_celestial_adopt_label": "Dò tia CPU với chiếu sáng gián tiếp",
"render.cpu_celestial_path_required": "Mặt Trời hữu hạn và bầu trời chuyển sắc dùng chiếu sáng gián tiếp CPU.",
"render.cpu_celestial_switch": "Chọn dò tia CPU với chiếu sáng gián tiếp để giữ Mặt Trời hoặc bầu trời này.",
"render.cpu_celestial_unportable": "Giữ dò đường đi GPU cho cài đặt mẫu truyền sáng hiện tại.",
"render.cpu_sky_bounds": "Góc xoay bầu trời phải hữu hạn trong ±1.000.000 radian; cường độ phải từ 0–100."
},
"zh": {
"render.cpu_celestial_adopt": "CPU 光线追踪与间接照明 · 路径深度 {depth} · {spp} SPP · 追踪阴影 · 种子 {seed} · 1 个主样本",
"render.cpu_celestial_adopt_label": "CPU 光线追踪与间接照明",
"render.cpu_celestial_path_required": "有限太阳和渐变天空使用 CPU 间接照明。",
"render.cpu_celestial_switch": "选择带间接照明的 CPU 光线追踪,以保留此太阳或天空。",
"render.cpu_celestial_unportable": "当前透射采样设置请继续使用 GPU 路径追踪。",
"render.cpu_sky_bounds": "天空旋转必须为 ±1,000,000 弧度内的有限值;强度必须为 0–100。"
}
}
+34
View File
@@ -0,0 +1,34 @@
{
"ar": {
"render.shot_lens_cpu_progressive_reason": "فعّل أخذ العينات التدريجي في قسم «أخذ العينات» قبل تمكين عمق المجال على CPU.",
"render.shot_lens_raytrace_reason": "يتوفر عمق المجال مع تتبع الأشعة على CPU وتتبع المسارات على GPU."
},
"de": {
"render.shot_lens_cpu_progressive_reason": "Vor der CPU-Schärfentiefe unter Sampling die progressive Abtastung übernehmen.",
"render.shot_lens_raytrace_reason": "Schärfentiefe ist für CPU-Raytracing und GPU-Pathtracing verfügbar."
},
"en": {
"render.shot_lens_cpu_progressive_reason": "Adopt progressive sampling in Sampling before enabling CPU depth of field.",
"render.shot_lens_raytrace_reason": "Depth of field is available for CPU ray tracing and GPU path tracing."
},
"he": {
"render.shot_lens_cpu_progressive_reason": "יש להפעיל דגימה הדרגתית בחלונית הדגימה לפני הפעלת עומק שדה ב־CPU.",
"render.shot_lens_raytrace_reason": "עומק שדה זמין בעקיבת קרניים ב־CPU ובעקיבת נתיבים ב־GPU."
},
"ja": {
"render.shot_lens_cpu_progressive_reason": "CPUの被写界深度を有効にするには、サンプリングでプログレッシブ方式を適用してください。",
"render.shot_lens_raytrace_reason": "被写界深度はCPUレイトレーシングとGPUパストレーシングで利用できます。"
},
"ko": {
"render.shot_lens_cpu_progressive_reason": "CPU 피사계 심도를 켜기 전에 샘플링에서 점진적 샘플링을 적용하세요.",
"render.shot_lens_raytrace_reason": "피사계 심도는 CPU 레이 트레이싱과 GPU 패스 트레이싱에서 사용할 수 있습니다."
},
"vi": {
"render.shot_lens_cpu_progressive_reason": "Bật lấy mẫu lũy tiến trong mục Lấy mẫu trước khi bật độ sâu trường ảnh trên CPU.",
"render.shot_lens_raytrace_reason": "Độ sâu trường ảnh khả dụng với dò tia trên CPU và dò đường đi trên GPU."
},
"zh": {
"render.shot_lens_cpu_progressive_reason": "启用CPU景深前,请在采样中应用渐进式采样。",
"render.shot_lens_raytrace_reason": "CPU光线追踪和GPU路径追踪均支持景深。"
}
}
@@ -0,0 +1,26 @@
{
"ar": {
"render.cpu_transparent_help": "يزيل PNG الشفاف الخلفية ويحافظ على تغطية الحواف. يجب أن تبقى عتامة الأجسام والأوجه عند 100%."
},
"de": {
"render.cpu_transparent_help": "Transparentes PNG entfernt den Hintergrund und erhält die Kantenabdeckung. Körper- und Flächendeckkraft müssen 100 % bleiben."
},
"en": {
"render.cpu_transparent_help": "Transparent PNG removes the background and keeps edge coverage. Body and face opacity must stay at 100%."
},
"he": {
"render.cpu_transparent_help": "PNG שקוף מסיר את הרקע ושומר על כיסוי הקצוות. אטימות הגופים והפאות חייבת להישאר 100%."
},
"ja": {
"render.cpu_transparent_help": "透明PNGは背景を除き、輪郭の被覆率を保持します。ボディと面の不透明度は100%にしてください。"
},
"ko": {
"render.cpu_transparent_help": "투명 PNG는 배경을 제거하고 가장자리 커버리지를 유지합니다. 바디와 면의 불투명도는 100%여야 합니다."
},
"vi": {
"render.cpu_transparent_help": "PNG trong suốt loại bỏ nền và giữ độ phủ ở biên. Độ đục của thân và mặt phải giữ ở 100%."
},
"zh": {
"render.cpu_transparent_help": "透明PNG移除背景并保留边缘覆盖率。实体和面的不透明度必须保持100%。"
}
}
@@ -1,34 +0,0 @@
{
"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
@@ -1,26 +0,0 @@
{
"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
@@ -1,42 +0,0 @@
{
"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": "检查注释"
}
}
@@ -1,34 +0,0 @@
{
"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": "修复绘图尺寸"
}
}

Some files were not shown because too many files have changed in this diff Show More