Compare commits

...

71 Commits

Author SHA1 Message Date
bherbruck 3d3315e0e4 CI: run the full suite on pushes to kernel-smlib
PRs into kernel-smlib only run their changed packages and the branch has no
merge queue, so nothing tested the branch as a whole. A push run after each
merge does. Caches stay main-only (save-if and the sticky-disk commit key on
refs/heads/main).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 21:58:43 -04:00
bherbruck 4098ff9dc8 Ribbon: New Sketch on the Sheet Metal tab again; tests for entering and leaving sketches (#3946)
* Ribbon: New Sketch on the Sheet Metal tab again; tests pin sketch enter/exit

Before the ribbon redesign the Sheet Metal tab started with New Sketch. It
gets its own Sketch panel again, like the Model tab's, and a sketch started
there keeps the tab until it opens.

Workspace tests pin the ways into and out of a sketch: New Sketch then a
plane, a double-click in the timeline or the browser, and the browser's Edit
Sketch all open the SKETCH tab looking at the sketch. Finish Sketch from the
palette or the ribbon lands on MODEL with the earlier view back. Escape and
Enter keep the sketch open (#1804), and Undo after Finish reopens it.

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

* docs: ribbon layout lists Sheet Metal's Sketch panel

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

* Workspace test: press the keys well apart

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 21:32:48 -04:00
bherbruck 97383454c6 Browser rows truncate long names with an ellipsis and a tooltip; Solve Sketch is no longer offered in the UI (#3940)
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-20 21:32:42 -04:00
bherbruck 10d7bd045e CI: build and run the tests in one job, sccache on a Blacksmith disk (#3941)
* CI: build and run the tests in one job, sccache on a Blacksmith sticky disk

The tests job compiles and runs the tests itself on the 32 vCPU runner, so a pull request or merge
group uploads and downloads no 3 GB archive (the download took 45 to 385 s per shard, and once
14 minutes) and starts beside the repository checks instead of after them. The archive is built
only for the nightly and manual jobs that need it. sccache moves from GitHub's 10 GB cache, where
about 1100 per-ref entries were evicting each other, to a sticky disk that only main commits.

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

* TEMP: let this branch save caches so the warm run can be measured

* Revert "TEMP: let this branch save caches so the warm run can be measured"

This reverts commit 1bbbfcf5ff.

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-20 21:25:35 -04:00
bherbruck 6e87fb1a25 Ribbon: Parameters and Configurations back on Model, Sheet Metal and Sketch; Sheet Metal tab always reachable; Finish Sketch button back in the sketch palette (#3937)
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-20 21:08:04 -04:00
denelson1-dot 30e0db1e05 Pattern of a join that merged its targets keeps working (#3378) (#3387)
* claim: #3378

* Pattern and mirror copies of a join that merged its targets join the merged body (#3378)

Since #2755 a pattern or mirror copy of a primitive reuses the seed's targets. When the
seed was a join that merged two targets, the second one no longer exists, so the copies
failed with `unknown body`. The copies now keep the seed targets that are still there;
when none is, the names stay and the error says which body is missing.

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

* Rerun CI: the previous run was cancelled before any check ran

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 00:15:39 +00:00
Dahak Builds 2b1cb2c4e1 MCP batch: rollback restores the original document (#3242) (#3288)
* claim: #3242

* MCP batch: rollback restores the design the batch began in (#3242)

A failed batch with rollback: true reported rolled_back: true even when the
design never went back. Two defects, both fixed:

- file.new (and any document-replacing command) replaced the session with it,
  taking its checkpoints down too, so a batch that failed after file.new had
  nothing to restore. Session::replace_design now carries the checkpoints over
  to the fresh session: the checkpoint belongs to the batch, not to the
  session that happened to be holding the document.
- rolled_back echoed the requested flag. It now reports whether the design is
  actually back: the app answers engine.batch with a new restored field, and
  the MCP tool reports restored && rollback. An app that predates the field
  falls back to undone/completed.

* Batch: answer with main's rolled_back field after the merge (#3242)

Upstream #2900 put the real restore check on engine.batch as
`rolled_back` while this branch named it `restored`; the merge kept
main's control test and this branch's field, and CI caught it.
Standardize on main's wire name: the app answers `rolled_back`, the
MCP tool reads it (fallback for older apps unchanged), docs and tests
follow. The checkpoint carry-over across file.new is unchanged.

---------

Co-authored-by: DahakBuilds <dahakbuilds@users.noreply.github.com>
2026-10-21 00:15:36 +00:00
phanoob f815491936 2-tangent circle: start from the touching circle, not the click (#3141) (#3210)
* claim: #3141

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

* 2-tangent circle: start from the touching circle, not the click (#3141)

The tool passed its second click as the place to start solving, and a
click lies on a curve: the circle began centred on that curve, where its
tangency has no side yet. The solve then moved the user's curves to fit
(a rectangle came out skewed), or failed outright when they were fixed.
The command now starts from the centre, nearest the requested point, of
a circle of that radius touching both curves (where the curves moved by
the radius meet), and the tool asks for the circle between its two clicks.

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

---------

Co-authored-by: Phanoob <phanoob@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 00:15:34 +00:00
rlartot a595805fc8 Sketch: symmetry about a line pairs line ends by mirror image (#2961, #2940) (#3029)
* claim: #2961

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

* Tests: symmetry about a line keeps rectangle sides' height (#2961, #2940)

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

* Sketch: symmetry about a line pairs line ends by mirror image (#2961, #2940)

c_symmetry paired the ends of two lines by index. Opposite sides of a
rectangle run in opposite directions, so that forced both heights to 0
(or was refused when a side had a length). Pair each end with the end
of the other line nearest its mirror image, as symmetry about a point does.

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

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-21 00:15:32 +00:00
bherbruck 0379ee76c6 CI: spread the slowest tests across the test shards (#3646)
* CI: give the slowest tests a shard each

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

* CI: spread the slow tests on pull requests too, and keep both junit files

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

* CI: pull requests skip the goldens and the action matrix sample unless the kernel changes

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

* Docs: which pull requests run the goldens

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

* CI: run the tests in one job on a big runner, slowest first

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

* CI: allow the 32 vCPU runner label in actionlint, read junit.xml in the golden hint

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 20:41:58 -04:00
plexi32 4f7ff8c4d8 Move: Point to Point takes the origin point (#3696) (#3718)
The origin point was drawn in the view and listed in the browser, but
nothing could pick it: the viewport never offered it as a hit and the
browser's O row had no selection. A new ORIGIN accept bit lets an input
take it as a vertex of no body; Move's point inputs (From, To, Point,
Pivot) accept it, from the view or the browser's Origin folder.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 00:13:59 +00:00
bherbruck d2188fb7ab IGES import: a B-spline curve over a tiny knot span keeps its faces (#3885) (#3886)
A curve written over a knot span of 1e-7 or less was refused on the STEP side
("knot vector consists single value"), and both faces it bounds were lost. Its
knots are moved and scaled onto (0, 1), which leaves its shape as it is.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 00:13:28 +00:00
bherbruck 438abbcf82 STEP conformance sweep: the cones to a point read right now (#258) (#3901)
cone_point and cone_point_vertex join the sweep now that #3871 reads a cone
bounded only by its base circle up to its point.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 00:03:46 +00:00
Christian Svensson 079a600481 Recovery: autosave every open design tab (#2884) (#2952)
* Fix recovery for unsaved background tabs (#2884)

* Recovery: scheduled autosaves of every tab write on the worker thread (#2884)

After #3139, Autosaver::save waits for the write and Autosaver::tick runs
it on a worker. The scheduled path now goes through tick (which skips a
tab whose previous write is still running), so several dirty tabs no
longer write on the window thread each interval (#3044). save stays for
Save, close and shutdown. Each tab's autosaver has no clock of its own:
the shared cadence decides when it is due. The tests wait for the
writes a tick started.

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

* Recovery: closing a tab waits for its running autosave before removing the entry (#2884)

With the autosave on a worker thread, discard could remove the files and
the lock while a write was still running; the write then recreated the
entry without a lock, and the next launch offered a design closed with
Don't Save. discard now ends the running write first, as Drop does.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 23:58:05 +00:00
bherbruck 94bd8719aa STEP import: a void reads as a cavity whichever way its shell flag points (#3730) (#3872)
A BREP_WITH_VOIDS void's faces face into the cavity, so it encloses volume of
the opposite sign to the outer shell. Its ORIENTED_CLOSED_SHELL flag alone
doesn't tell: SOLIDWORKS writes .F. over a shell already facing into the
cavity, SolidCraft .T., and flipping by the flag added the cavity's volume. A
void enclosing volume of the outer shell's sign is now turned inside out.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 23:54:54 +00:00
bherbruck fc3dedfd5c STEP import: regression sweep over the step-conformance parts of known shape (#3880)
* STEP import: regression sweep over the step-conformance parts of known shape

Fifteen generated parts from step-conformance (CC0): plain, NURBS, seamed,
placed, inch and far-away cylinders, domes, a grooved shaft, a plate with 100
holes, a 3.6 m disc, a 0.5 mm pin, a B-spline half-torus and a washer. Each
reads back with its exact volume, its box and its face count. The half-torus
on a TOROIDAL_SURFACE and the cones to a point are left out until they read
right.

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

* STEP conformance sweep: the pin's volume as FRAC_PI_8 (clippy)

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 23:07:47 +00:00
bherbruck 41a62d210c File menu: Export STL… and Export 3MF… open the mesh dialog with that format; command search finds mesh export by format (#3873) 2026-10-20 22:43:30 +00:00
bherbruck 5638259737 STEP import: a cone bounded only by its base circle reaches its point (#258) (#3871)
A cone face written with no vertex at its point has the base circle as its only
loop (or that circle and a VERTEX_LOOP at the point, which has no edges). Its
surface was spanned over the circle alone, a sliver, so a solid cone came in as
a flat disc. Such a face is now spanned to the apex, and its circle is joined to
the apex by a seam line out and back, as a cone with a seam is written.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 22:35:12 +00:00
NOWN-DEV 090119a5c5 Boolean: a small hole in a large body's face stays a hole when the face is divided (#2579) (#2580)
* Boolean: a small hole in a large body's face stays a hole when the face is divided

A boolean divides faces at a tolerance that follows the larger operand (a few
millimetres on a part a few metres long). A small hole's rim, two half circles
when read from STEP, then became two chords: its polygon in the face's
parameters had no area, and the sign that tells a hole from a face piece came
out at random. The hole became a face of its own, inside out, and the face it
was cut in stayed whole. A box cut round such a hole gave a piece without it,
and the shared part of that piece and a pin in the hole came out negative
("malformed") or the boolean failed.

divide_face now divides a loop whose polygon is flat again, finer (down to a
hundredth of the loop's own size), before reading its sign. Only the polygon
that places the loop changes; loops that have an area are untouched.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Kernel: the long-plate hole test in tests.rs; a flat loop's size from its box

Review: the test moves into the kernel's test file (no file of its own),
and finer_if_flat takes the loop's size from the diagonal of the box round
its sample points instead of the largest distance between any two.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Aac7mP9rthK3WsPpPRvCsF

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 21:56:33 +00:00
bherbruck 522ae77556 Release 0.18.1 (#3851)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 17:33:29 -04:00
raykholo 5ba8c815a6 IGES import: trim curves with reversed pieces no longer drop their faces (#3358) (#3359)
* claim: #3358

* IGES import: trim curves with reversed pieces no longer drop their faces

A curve on a surface (142) gives each trim loop twice: B in the surface's
parameter space and C in model space, running the same way. Some exporters
write C as a composite curve (102) whose pieces are in order but some of
them end to start. The reader passed those pieces on as they were, so the
loop did not close and the STEP reader left the face out ("face boundary is
not a closed loop").

The pieces of C are now turned to run head to tail, starting at S(B(start)),
the point where the parameter-space curve begins on the surface. That keeps
the loop's sense from the file, so holes still run against the outer loop.
A piece whose ends are one vertex at the sewing tolerance keeps its
direction.

On a 9,276-face trimmed-surface car model, 7,887 faces now import instead
of 3,882.
2026-10-20 17:32:48 -04:00
bherbruck cb1b230732 Add Dutch, Swedish, Traditional Chinese, Indonesian, Romanian and Hungarian (#3826)
The new catalogs are machine translated; native speakers, corrections are very welcome.
2026-10-20 17:32:44 -04:00
bherbruck 6e22e3709d Web: reopen designs saved on the computer from the recent list after a reload; forget stale remembered files (#104) (#3834) 2026-10-20 17:32:36 -04:00
Krzysztof Heim 1b84ce6330 fix: skip impossible drags of determined sketch geometry (#2006) (#2199)
Co-authored-by: Krzysztof Heim <krzysztof.heim@b4bridge.com>
2026-10-20 20:56:50 +00:00
bherbruck 687caad24c IGES import: a revolved arc is a cap, not a whole sphere (#3806) (#3813)
A surface of revolution (120) wrote its arc generatrix as a whole STEP circle, which
turns into a whole sphere twice over when the arc is centred on the axis; the trim could
not pick the cap out of it and the face meshed as the full ball. An arc in a plane through
the axis now comes in as a sphere or torus, any other arc as an exact rational B-spline of
just the arc.
2026-10-20 20:56:35 +00:00
bherbruck 7e05c8b23c Hovering a long helical edge no longer freezes (#3745) (#3818)
Hovering an edge lights its circular rim, which starts from the edge's
tangent chain. That looked at every edge of the mesh for each edge in the
chain (and searched the chain itself each time), so a long helical edge in
many pieces, a thread's crest, took seconds: 7.6 s inside the frame on the
first hover of the stem's thread in a real watch-movement STEP file. The
edges are now found by the grid cells of their ends, two join tolerances
across, so each step looks only at the edges that end near it: the same chain
in the same order, in 29 ms for that thread.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 17:08:50 -04:00
DuBearte 7dfbaf2a2c Parameter autocomplete matches anywhere in the name, prefix matches first (#2757) (#2849)
* claim: #2757

* fix(params): autocomplete offers a parameter for any part of its name (#2757)

Typing `side` now offers `cab_side_thickness`: parameters match the word
anywhere in their name, after those that start with it, both in the expression
boxes and in parameters.complete. Functions, constants and units still match
from the start.

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-10-20 20:39:14 +00:00
phanoob 1e0e8f78dc Shift+R repeats the last tool or dialog (#3045) (#3160)
* claim: #3045

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

* Shift+R repeats the last tool or dialog (#3045)

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

---------

Co-authored-by: Phanoob <phanoob@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 20:29:22 +00:00
bherbruck c3151fb7fe Release 0.18.0 (#3792)
Version 0.18.0; no pending design format changes, so it writes format 28 like 0.17.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 16:28:46 -04:00
bherbruck a9aab13d92 Translations: complete Ukrainian, French, Danish (#3802)
The added strings are machine translated; native speakers, corrections are very welcome.
2026-10-20 16:28:31 -04:00
bherbruck 6235295ba6 Translations: complete Czech, Italian, Vietnamese, Spanish (#3801)
The added strings are machine translated; native speakers, corrections are very welcome.
2026-10-20 16:28:27 -04:00
bherbruck 51e2f0a34b Translations: complete Malay, Brazilian Portuguese, Polish, Galician (#3800)
The added strings are machine translated; native speakers, corrections are very welcome.
2026-10-20 16:28:22 -04:00
bherbruck 0191e3e157 Translations: complete Russian, Korean, German, Turkish (#3799)
The added strings are machine translated; native speakers, corrections are very welcome.
2026-10-20 16:28:17 -04:00
bherbruck 754eb56e5b perf: hovering a large body never waits for its pick index or plan mesh (#3745) (#3766)
The first hover over a large body made two things in the frame: the body's
pick index (its triangles and edges in hierarchies; about 0.3 s for a long
modelled thread, a quarter of a million triangles) and, with the GPU viewport,
the plan mesh a highlighted face is drawn from (about 0.7 s for the same
thread). The window froze for the sum.

A large mesh's pick index is now made on a worker, and picks scan the mesh
until it is there (a few milliseconds); a body's plan mesh is made on a worker
too (`ModelBody::plan_mesh_soon`), and until it is there the hovered face is
lit on the mesh the viewport already has. Small meshes, and the browser build,
make both at once as before.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 16:28:11 -04:00
github-actions[bot] 9d04e28b51 Regenerate THIRD-PARTY-LICENSES.txt and docs/features.md [skip ci] 2026-10-20 20:21:42 +00:00
bherbruck 8e77d9fd85 Merge pull request #3627 from Nathan-Dane/ui/icon-accents
UI redesign stack: floating workspace, shared controls, search and icon accents
2026-10-20 16:16:24 -04:00
Nathan Dane c78095ccde Fix UI stack integration after rebasing on main 2026-10-20 21:50:22 +02:00
Nathan Dane 6decbf407f Command search: concentric corner radii, a wider closed field with tighter spacing 2026-10-20 21:01:32 +02:00
Nathan Dane 6f521991c7 Command search: the closed ribbon field keeps its original size; the I-beam cursor over it 2026-10-20 21:01:32 +02:00
Nathan Dane 6c9ddc2528 Command search: the ribbon field animates into the search field; the footer fills the sheet edge to edge 2026-10-20 21:01:32 +02:00
Nathan Dane 41f5957d60 Icons keep their multi-tone artwork: tone accents for confirm, menu and radial glyphs 2026-10-20 21:01:32 +02:00
Nathan Dane d45fcce72c Command search popup on the UI kit; the bottom command line is gone 2026-10-20 21:01:32 +02:00
Nathan Dane a3e2fe25bb Context menus, radial menu and rename popover on the UI kit 2026-10-20 21:01:25 +02:00
Nathan Dane 57aad71bbe Window frame with page list and the confirm dialog on the UI kit 2026-10-20 21:01:25 +02:00
Nathan Dane f2eaead594 Per-tab accent tokens: fill, ink and tint for every tab, with a contrast guard 2026-10-20 21:01:25 +02:00
Nathan Dane a5e0fe3ec4 Parameters, Configuration Table, Crash Reports and Appearance redesign (step 8 of #2016, after the cleanup PR) 2026-10-20 21:01:25 +02:00
Nathan Dane dad56187df Cleanup after the popup move (step 7 of #2016, after the panels PR) 2026-10-20 21:01:18 +02:00
Nathan Dane 7026248e25 Every popup and panel on the UI library (step 6 of #2016, after the library PR) 2026-10-20 21:01:11 +02:00
Nathan Dane 1122cd44b8 UI library, tokens, fonts and icons (step 5 of #2016, after #2287) 2026-10-20 21:00:56 +02:00
Nathan Dane 837dbe66df Compass and viewport overlays (step 4 of #2016, rebased onto step 3) 2026-10-20 21:00:50 +02:00
Nathan Dane 845edeba5f Floating browser card and history strip (step 3 of #2016, rebased onto step 2) 2026-10-20 21:00:50 +02:00
Nathan Dane c2e3bdfe1c Floating shell frame: workspace and ribbon cards over a full-bleed viewport (step 2 of #2016, rebased onto #2176) 2026-10-20 21:00:50 +02:00
Nathan Dane a0c1a67220 Application bar and ribbon redesign (step 1 of #2016) (#2176)
* ui: spacing, radius and type tokens, per-tab accents, agent and on-accent tokens

* ui: per-tab accent behaviour and a larger application bar with a Start page button

* icons: silhouette master and flat assistant and agent link icons (open for review)

* icons: apply review notes (sparkle assistant, refined plug, silhouette halo)

* icons: wire the silhouette master to inspect.silhouette; record the accepted icons

* ui: application bar to the design: separators, Save, house, assistant icon, readable hovers

* ui: ribbon structure: Model to View tabs, contextual Sketch and Sheet Metal, migrated pins

* ui: ribbon painting to the compact design

* ui: smaller icon button highlight and assistant mark on the bubble corner

* ui: short ribbon labels that wrap at spaces

* ui: ribbon buttons fit their labels and Finish Sketch shows its full name

* ui: constraint gallery, workspace menu, section list headings and richer tooltips

* ui: lighter selected tab label, ribbon surface and dimmer bar separators match the dark design

* ui: document tabs as rounded chips with a dimmer inactive label

* ui: document tabs fit their names and show close only when active or hovered

* icons: close and plus masters for the document tabs (open for review)

* ui: centre the new-tab plus and overflow button on the bar

* ui: a quiet rule under the ribbon tabs on non-contextual tabs

* ui: the application bar mark separates its faces with the bar colour, not white

* ui: open ribbon section label uses the selected tint and a chevron

* ui: dim the tab close and plus icons until hovered

* ui: every ribbon dropdown uses the chevron, the workspace card included

* ui: the active document tab uses the design's surface colour

* ui: the bug button's right padding mirrors its icon side

* ui: square constraint cells and a rule before the grid

* ui: constraint cells back to their earlier size, keeping the rule

* ui: smaller constraint cells and icons, centred in the section

* ui: constraint cells fit the height of the large buttons' icon and label

* ui: sketch Create titles share the other labels' line

* ui: format the application bar and ribbon changes

* ui: active tab surface token and the colour and hover tests that follow the redesign

* tests: the merged parameter and narrow toolbar tests follow the compact ribbon

* i18n: the Malay Gap label has an English source and its Japanese and Chinese translations

* apply_off_thread tests: the evaluation lingers in tests, so still computing never depends on machine speed

---------

Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
2026-10-20 17:23:49 +00:00
bherbruck 3606d44ad9 README: Trendshift Rust badge next to the overall one (#3714) 2026-10-20 14:20:13 -04:00
bherbruck 20a0d3b2e4 README: Trendshift badge (#3706) 2026-10-20 14:13:53 -04:00
bherbruck 5d9ebe9911 Renaming a feature no longer rebuilds the design (#3044) (#3097)
* perf: renaming a feature evaluates nothing (#3044)

A feature's name labels it and builds nothing, but a timeline rename evaluated
the whole design again (about 70 s on the Prusa Core One assembly, and again
for undo). When the only change is one feature's name, the model renames its
result, sketch and construction geometry in place and takes the renamed
design's fingerprints. A rename other features follow (a plane a sketch is on)
or of a failed feature still evaluates the design.

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

* Guard cheap commands on designs with thousands of bodies, plus a scaling bench (#3044) (#3121)

Cheap commands stay cheap on designs with thousands of bodies, with a scaling bench.

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 14:08:59 -04:00
bherbruck 76af4ee855 Web: Save to computer… for designs kept in the browser; OPFS writes route as before (#104) (#3687) 2026-10-20 13:51:01 -04:00
bherbruck b2168435ba Web: save to and open from files on your computer (#104) (#3681)
Save As and Open use the browser's file dialogs where the File System Access API exists; Save writes back to the same file, its handle kept in IndexedDB across reloads. File > Download design added. Other browsers keep storing designs in OPFS.
2026-10-20 13:39:55 -04:00
SS-EN 34afcf75c1 Joints: a loop that meets only to within nanometres still moves (#3475)
A closed loop whose joints meet a few nanometres apart along their parallel
axes (faces modelled that far apart) passes the conflict check, but the loop
solver wanted 1e-7 mm before it moved anything. No value of a planar loop
takes such a gap out, so every drive of it stopped at once (stopped_by_loop,
fraction 0) and joint.reach called the loop open.

The solver now closes such a loop as far as it comes and, when what is left
is within the conflict check, keeps it that closed on the way. joint.drive,
joint.reach and the nearby pose of the dof count share it.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 16:41:37 +00:00
Irakli Kourtis 932d8d6b4f Tessellation: trimmed faces of display meshes stay on their surfaces (#1653)
* Tessellation: trimmed faces of display meshes stay on their surfaces

Display meshes triangulate a trimmed face in parameters scaled by the
surface's speed along each, so a strip stretched along one parameter is
not fanned across, and every boundary segment stays a constraint: one that
crosses another is split where they meet. Boolean meshes keep truck's way.

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

* Tessellation: one boundary rule for every mesh; drop the #1648 retry

A boundary segment that crosses one already added is split where they
meet in Boolean meshes too, which keeps a pinched face (#1648) whole
without the snap-and-retry. Faces whose segments all go in mesh as
before.

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

* Robustness goldens: seeds 1, 23, 55, 64 and 86 re-recorded on current main for #1653

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

* Thread preview test: wait for the stale fit error to clear before the next preview

After switching back from M6x1.3 to M8x1.3, wait_feature_preview stopped at the M6 error still on
screen when the new preview started a few frames later (seen on Linux CI with #1653's meshing).

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

* Robustness goldens: the slow seeds 14, 20 and 40 re-recorded for #1653 from the merge queue

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

---------

Co-authored-by: jyviko <2501648+jyviko@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 16:49:45 +00:00
fkirill fb87cde6a1 Sketch: persist Shift-picked midpoints with ordinary constraints (#2087) (#2378) 2026-10-20 16:26:19 +00:00
Victor Santos Oliveira 5305d7c82d Clarify delayed detection in unclean-exit reports (#3640) 2026-10-20 16:05:55 +00:00
Colin Faulkingham 59cd55f531 Sketch: keep three-point arcs attached to the through point (#2166) (#3610)
* claim: #2166

* Fix three-point arc through-point attachments (#2166)
2026-10-20 15:55:58 +00:00
kaltenbrunnermarkus 2668518079 Section Analysis: dragging the cut stays smooth on large parts (#3616)
On an imported part with 2.5 million triangles, every frame of a Section
Analysis drag looked up the picked face's plane several times (each lookup
tessellates the whole body, about 0.1 s) and cut every triangle again for
the outline (38 ms).

- planar_face keeps its last answers for the model state they were found in.
- The outline cuts through a SectionIndex: the mesh's triangles sorted into
  slabs along the cut's normal, kept per mesh while the normal stays, so a
  cut looks at one slab (0.06 ms on that part) and finds the same segments.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 15:54:04 +00:00
rlartot e29a57dc3a inspect.interference/clearance: list mesh bodies as unchecked (#3249) (#3258)
* claim: #3249

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

* inspect.interference/clearance: list mesh bodies as unchecked

Mesh bodies (STL, 3MF and OBJ imports) were filtered out before any pair
was formed, so two overlapping STL imports reported count 0 and an empty
unchecked list, which reads as "no collision". Each mesh body is now
listed in `unchecked` as {a: name, b: null, reason: "mesh body: not
checked"} by both commands. inspect.clearance also stops refusing a mesh
body named in `bodies` or `against` as "no solid body"; it is listed
instead. The parameter docs and the assemblies guide say so.

Fixes #3249

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

* docs: separate the mesh-body note from the paragraph above

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

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-20 15:42:39 +00:00
Jakobonly 23b0120759 Sketch: a radius dimension past an arc's end extends the arc to its arrow (#3393) (#3408)
* claim: #3393

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

* Sketch: a radius dimension past an arc's end extends the arc to its arrow (#3393)

A radius or diameter dimension on an arc whose leader is dragged past the arc's
ends had its arrow pointing at empty space. The radial frame now keeps the arc's
span, and an arrow outside it gets the arc continued from the nearer end round
to the arrow tip and a little past it, as dimension drawings do. Full circles,
ellipses and arrows on the arc are unchanged.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 15:40:26 +00:00
SS-EN 76b17f2663 sketch(feat): a click on another dimension inserts its name into the value being edited (#3186)
While editing a dimension's value, a single click on another driving
dimension now inserts its parameter name (a double-click no longer needed,
and its second click does not insert it twice). A lone value such as `10`
or `10 mm` is replaced by the name; an expression in progress keeps what
was typed and gets the name at the cursor (replacing selected text), so
`10 + ` becomes `10 + d10`. The cursor ends after the name and the field
keeps the keyboard.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 15:32:07 +00:00
phanoob 83790139b1 Editing a sketch past the history marker rolls the marker to it (#2716) (#3339)
* claim: #2716

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

* Editing a sketch past the history marker rolls the marker to it (#2716)

Edit Sketch rolled the timeline back to just after the sketch only when
the marker was later. With the marker before the sketch it stayed there:
the sketch wasn't built, so it opened with no palette and couldn't be
edited. The marker now moves to just after the sketch either way, and
Finish Sketch puts it back as before.

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

---------

Co-authored-by: Phanoob <phanoob@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 15:28:52 +00:00
Dahak Builds 7bda28e786 Contact: a check finds bodies crossing face to face, not only a corner inside (#3323) (#3388)
* claim: #3323

* Contact: a check finds bodies crossing face to face, not only a corner inside (#3323)

The collision probe sampled each tessellation's points (vertices, edge
midpoints, triangle centres) against the other body, so plates crossing
face to face - where the crossing strip is smaller than a face triangle -
put no sample point inside and went unreported while inspect.interference
found the overlap. Where an edge of one body passes through the other's
surface, the middle of the chord it cuts lies inside; its distance from
that surface is the depth. Touching and grazing are unchanged. The sweep's
collision work model pays for the added edge pass.

---------

Co-authored-by: DahakBuilds <dahakbuilds@users.noreply.github.com>
2026-10-20 15:21:17 +00:00
Jakobonly 2f5f65d6e3 Sketch: an angle dimension between two lines from one point measures the angle between them (#2724) (#3439)
* claim: #2724

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

* Sketch: an angle between two lines from one point measures the angle between them (#2724)

An angle dimension on two lines that share an end (two radii of a circle, the
two sides of a corner) took the acute reading of the lines' directions, so
radii 120° apart measured 60° and 150° apart 30°. With no text position given,
the angle is now measured from the shared end towards the lines' other ends,
the reading anyone would mean; the supplement stays the alternative reading.
Lines that share no end, and dimensions placed with a text position, are
unchanged.

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

* ci: re-run the checks; the run on the last push was cancelled

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 14:51:06 +00:00
bherbruck 9e78e86b92 perf: renaming a body evaluates nothing (#3044) (#3095)
A body rename changes names only, but committing it went through the general
"design changed" path: the whole timeline was evaluated again. On an imported
assembly that means reading the STEP file again and meshing every body (about
100 s on the Prusa Core One, and the same again for undo).

When the only change from the previous design is one body rename (the
features' stored body names and the body-keyed maps the rename updates), the
model now renames the body in its results instead: every body keeps its
geometry and meshes, and the fingerprints are set to the renamed design's so
the next evaluation reuses everything. Undo and redo of the rename take the
same path. A rename the model can't follow that way (the body is changed or
named by a later feature, sheet metal, threads) still evaluates the design as
before.

`bench-open --ops script.json` times commands and their undo on an opened
design, with the view work each leaves.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 12:18:40 -04:00
434 changed files with 78336 additions and 12273 deletions
+7 -6
View File
@@ -1,6 +1,5 @@
# cargo-nextest settings for CI (`cargo xtask test` does not use nextest).
# CI builds the tests once and runs them in shards: 2 for a pull request, 4 for main and the nightly
# (.github/workflows/ci.yml).
# CI builds the tests once and runs them in one job on a 32 vCPU runner (.github/workflows/ci.yml).
[profile.ci]
# Heavy tests (one to twelve minutes each on a CI runner) that pull requests skip. They run on
@@ -34,12 +33,14 @@ slow-timeout = { period = "60s" }
[profile.ci.junit]
path = "junit.xml"
# The action matrix sample runs its designs in parallel worker processes, one per CPU. Run with
# other tests beside it (the heavy ones on main), a pi-case hole that takes 8 s alone (#952 makes
# it a real hole) went past the matrix's 90 s action limit. It runs on its own.
# The action matrix sample runs its designs in parallel workers (ACTION_MATRIX_JOBS, set to match in
# ci.yml). Its workers reserve that many of nextest's threads, so the cores are not oversubscribed
# (a pi-case hole that takes 8 s alone went past the matrix's 90 s action limit when they were),
# and it starts first, beside the other slow tests.
[[profile.ci.overrides]]
filter = "binary(action_matrix) & test(=action_matrix_sample)"
threads-required = "num-cpus"
threads-required = 12
priority = 100
# The slowest tests start first, so a shard does not end with one of them running alone on a
+1
View File
@@ -7,4 +7,5 @@ self-hosted-runner:
- blacksmith-4vcpu-ubuntu-2404
- blacksmith-8vcpu-ubuntu-2404
- blacksmith-16vcpu-ubuntu-2404
- blacksmith-32vcpu-ubuntu-2404
- blacksmith-8vcpu-windows-2025
+116 -47
View File
@@ -8,12 +8,13 @@
# whole workspace, as do pushes to main that change code, the nightly run and Run workflow.
# Documentation, packaging, the other workflows and the GitHub bots' scripts select nothing:
# no native job runs for them, on pull requests or on main.
# Tests are built once and run in shards. A few heavy tests (see .config/nextest.toml) are
# Tests are built and run in one job on a big runner, slowest first, starting beside the repository
# checks (no archive to upload and download). A few heavy tests (see .config/nextest.toml) are
# skipped on pull requests and run on pushes to main and nightly. The Windows jobs (D3D12 shaders
# and app startup), the reference tests and the doc tests run on main and nightly only, not on
# pull requests (the fast lane, in Select checks); a break there is fixed forward on main. Only
# main saves the build caches; pull requests restore them. On pull requests the test build and the
# shards start only after the format and repository checks pass.
# main saves the build caches; pull requests restore them. sccache is on a Blacksmith sticky disk that
# only main commits.
# A push to main runs only the repository checks when a passing merge-queue run tested that very
# commit, or when nothing a build reads changed since the last push to main whose run passed.
# Otherwise it runs everything.
@@ -29,14 +30,13 @@
# reads `ci` from the newest run of a commit, so a run without it would block a PR that passed.
# Cross-compilation is opt-in through Run workflow; release builds remain in release.yml.
# Every Linux job runs on Blacksmith's Ubuntu 24.04 runners (blacksmith-<N>vcpu-ubuntu-2404), sized
# per job: 16 vCPU for Build tests, Clippy and the merge queue's test shards, 8 for compiles, the
# other test shards, doc and reference tests, 4 for the light checks, 2 for Select checks and ci.
# per job: 32 vCPU for the tests, 16 for Clippy, the archive build (nightly) and the merge queue's, 8 for compiles, doc and reference tests, 4 for the light checks, 2 for Select checks and ci.
# The Windows shader job runs on Blacksmith's Windows Server 2025 runners (blacksmith-8vcpu-windows-2025);
# the Windows app startup smoke stays on windows-latest, which has a desktop session that presents frames.
# Pull requests, forks included, the merge queue, pushes to main and the nightly run all use them:
# the runners are ephemeral, fork runs get no secrets, and main saves the build caches that every
# other run restores read-only. The tests are built once (Build tests) into an archive the shards
# run; Blacksmith's image matches ubuntu-24.04, so the archive and the recorded goldens behave the
# other run restores read-only. Nightly and by hand, Build tests also makes an archive the matrix
# jobs run; Blacksmith's image matches ubuntu-24.04, so the archive and the recorded goldens behave the
# same.
name: CI
@@ -49,7 +49,7 @@ on:
# The merge queue: each queued group runs the full suite before it lands on main.
merge_group:
push:
branches: [main]
branches: [main, kernel-smlib] # kernel-smlib: full suite after each merge into the kernel branch
schedule:
- cron: "17 6 * * *"
workflow_dispatch:
@@ -178,9 +178,12 @@ jobs:
git diff --no-renames --name-only -z "$BASE_SHA...$HEAD_SHA" > "$RUNNER_TEMP/ci-paths"
while IFS= read -r -d '' path; do
case "$path" in crates/kernel/*|vendor/truck-*) kernel=true ;; esac
# What can change a robustness golden: the kernel and the crates a design is built with.
# What a pull request re-checks the goldens for: the kernel, truck and the goldens
# themselves (a re-record is checked before it is queued). The goldens are frozen, so
# on any other change they would only re-check unchanged results; the merge queue,
# main and the nightly run every seed before anything lands.
case "$path" in
crates/kernel/*|crates/engine/*|crates/doc/*|crates/sketch/*|crates/geom/*|vendor/truck-*|tests/robustness/*) goldens=true ;;
crates/kernel/*|vendor/truck-*|tests/robustness/*) goldens=true ;;
esac
# What the Windows D3D12 jobs exercise: the GPU crate and its shaders, the app and
# its startup, the viewport's wgpu glue, the vendored egui-wgpu, their scripts.
@@ -337,25 +340,33 @@ jobs:
build-tests:
name: Build tests
# Fail fast on pull requests: the test archive (and so the shards) waits for the repository
# checks, the most common cheap failure; heavy jobs used to run on to the end beside a failed
# format check. Main, nightly and Run workflow build regardless, so the action matrix and the
# property tests that hang off this job keep their signal.
needs: [changes, checks]
if: ${{ !cancelled() && needs.changes.result == 'success' && needs.changes.outputs.native == 'true' && (github.event_name != 'pull_request' || needs.checks.result == 'success') }}
# The archive is for the action matrix and the property tests, which run only nightly and by
# hand. Pull requests, the merge queue and main build and run their tests in the `tests` job.
needs: changes
if: ${{ !cancelled() && needs.changes.result == 'success' && needs.changes.outputs.native == 'true' && (github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') }}
runs-on: blacksmith-16vcpu-ubuntu-2404
# Fork builds start from main's cache, read-only; give them room to finish and upload.
timeout-minutes: ${{ (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository) && 60 || 45 }}
env:
CARGO_INCREMENTAL: "0"
RUSTC_WRAPPER: sccache
SCCACHE_GHA_ENABLED: "true"
SCCACHE_IGNORE_SERVER_IO_ERROR: "1"
SCCACHE_DIR: /home/runner/.cache/sccache
SCCACHE_CACHE_SIZE: 20G
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Size parallel compiles to the job's memory
run: scripts/ci/build-jobs.sh
- uses: dtolnay/rust-toolchain@stable
# sccache lives on a Blacksmith sticky disk (an ext4 volume cloned from the last snapshot,
# no 10 GB limit and no download). Only main commits it; pull requests, the merge queue and
# forks mount a clone and never write back, so nothing they build can reach main's cache.
- name: Mount the compiler cache
uses: useblacksmith/stickydisk@3f6be1451e3cad893b778bc93495c80452a5da26 # v1.7.2
continue-on-error: true
with:
key: ${{ github.repository }}-sccache-archive
path: /home/runner/.cache/sccache
commit: ${{ github.ref == 'refs/heads/main' && 'on-change' || 'false' }}
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
with:
key: native-tests
@@ -387,63 +398,94 @@ jobs:
run: sccache --show-stats
tests:
name: Native Linux tests (${{ matrix.shard }}/4)
needs: [changes, checks, build-tests]
if: ${{ !cancelled() && needs.changes.outputs.native == 'true' && (github.event_name != 'pull_request' || needs.checks.result == 'success') && needs.build-tests.result == 'success' }}
runs-on: ${{ github.event_name == 'merge_group' && 'blacksmith-16vcpu-ubuntu-2404' || 'blacksmith-8vcpu-ubuntu-2404' }}
name: Native Linux tests
needs: changes
if: ${{ !cancelled() && needs.changes.result == 'success' && needs.changes.outputs.native == 'true' }}
# Builds the tests and runs them in one job on a big runner: there is no archive to upload
# and download (3 GB on a full run), and the job starts beside the repository checks, not
# after them. nextest hands tests to its threads as they free up, the slowest first (priority
# in .config/nextest.toml), so they start at once and no core idles at the end.
runs-on: blacksmith-32vcpu-ubuntu-2404
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
# Pull requests test their affected crates in 2 shards; main and the nightly use 4.
shard: ${{ fromJSON(github.event_name == 'pull_request' && '[1, 2]' || '[1, 2, 3, 4]') }}
env:
# Pictures (snapshots, screenshots, ui.render, the viewport's offscreen tests) are drawn on
# the GPU. The runner has none: Mesa's lavapipe (software Vulkan) draws them, and a test that
# would skip for want of an adapter fails instead.
SOLIDCRAFT_GPU: software
SOLIDCRAFT_REQUIRE_GPU: "1"
ACTION_MATRIX_JOBS: "12"
CARGO_INCREMENTAL: "0"
RUSTC_WRAPPER: sccache
SCCACHE_DIR: /home/runner/.cache/sccache
SCCACHE_CACHE_SIZE: 20G
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install a software Vulkan driver (lavapipe)
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends mesa-vulkan-drivers libvulkan1
- name: Size parallel compiles to the job's memory
run: scripts/ci/build-jobs.sh
- uses: dtolnay/rust-toolchain@stable
# sccache lives on a Blacksmith sticky disk (an ext4 volume cloned from the last snapshot,
# no 10 GB limit and no download). Only main commits it; pull requests, the merge queue and
# forks mount a clone and never write back, so nothing they build can reach main's cache.
- name: Mount the compiler cache
uses: useblacksmith/stickydisk@3f6be1451e3cad893b778bc93495c80452a5da26 # v1.7.2
continue-on-error: true
with:
key: ${{ github.repository }}-sccache-tests
path: /home/runner/.cache/sccache
commit: ${{ github.ref == 'refs/heads/main' && 'on-change' || 'false' }}
# Main saves the cache; every other run restores it, read-only.
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
with:
key: native-tests
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Set up compiler cache
uses: mozilla-actions/sccache-action@fc920bf0ec8de6ee65d409111f7ec508035751ba # v0.0.11
with:
version: v0.16.0
- uses: taiki-e/install-action@1168f6810a3cb30a3514bf5756a3b7f0422c6df8 # v2.87.28
with:
tool: cargo-nextest
# Each shard runs the archive the Build tests job made.
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: nextest-archive
- name: Run tests
env:
EVENT_NAME: ${{ github.event_name }}
GOLDENS: ${{ needs.changes.outputs.goldens }}
SHARD: ${{ matrix.shard }}
SHARDS: ${{ github.event_name == 'pull_request' && 2 || 4 }}
FULL: ${{ needs.changes.outputs.full }}
PACKAGES: ${{ needs.changes.outputs.packages }}
# The robustness golden seeds a pull request skips: each takes one to twenty minutes under
# CI load, together about four fifths of the goldens' time. Pushes to main, the merge
# queue and the nightly run every seed.
SLOW_SEEDS: 81|45|20|24|95|02|57|40|18|48|32|46|85|61|77|34|14
run: |
# Pull requests skip the heavy tests (default-filter in .config/nextest.toml).
filter=()
# Pull requests skip the heavy tests (default-filter in .config/nextest.toml); `default()`
# below is that filter, so a pull request still runs only what it ran before.
base='default()'
if [ "$EVENT_NAME" = pull_request ]; then
# The action matrix sample (about 6 minutes on all cores) runs in the merge queue, on
# main and nightly, not on every pull request push.
base='default() & not (binary(action_matrix) & test(=action_matrix_sample))'
fi
if [ "$EVENT_NAME" != pull_request ]; then
# The boolean property tests draw new random inputs every run; they run only in the
# nightly property-tests job, so a known kernel bug cannot turn main red at random.
filter=(--ignore-default-filter -E 'not (package(solidcraft-kernel) & binary(boolean_props))')
base='not (package(solidcraft-kernel) & binary(boolean_props))'
elif [ "$GOLDENS" = true ]; then
# A pull request that can change a robustness golden (tests/robustness/goldens.json)
# also checks every seed but the slow ones, in the same run so they share its cores.
filter=(--ignore-default-filter -E "default() | (package(solidcraft-engine) & test(/^tests::robustness_goldens::/) & not test(/^tests::robustness_goldens::seed_($SLOW_SEEDS)\$/))")
base="(default() & not (binary(action_matrix) & test(=action_matrix_sample))) | (package(solidcraft-engine) & test(/^tests::robustness_goldens::/) & not test(/^tests::robustness_goldens::seed_($SLOW_SEEDS)\$/))"
fi
junit=${CARGO_TARGET_DIR:-target}/nextest/ci/junit.xml
rm -f "$junit"
# Extract into ./target: tests that run a built binary (env!("CARGO_BIN_EXE_*")) have
# the build job's path, which is the same checkout path here.
run=(cargo nextest run --archive-file tests.tar.zst --workspace-remap . --extract-to . --extract-overwrite)
if ! "${run[@]}" --profile ci --partition "slice:$SHARD/$SHARDS" --no-tests=pass "${filter[@]}"; then
# Pull requests test the affected packages (scripts/ci/affected.py).
scope=(--workspace)
if [ "$FULL" != true ]; then scope=(); for p in $PACKAGES; do scope+=(-p "$p"); done; fi
status=0
cargo nextest run "${scope[@]}" --cargo-profile ci --locked --profile ci --no-tests=pass \
--ignore-default-filter -E "$base" || status=1
if [ "$status" != 0 ]; then
# A failed golden gets the hint to re-record it (junit.xml lists the failures).
if [ -f "$junit" ] && tr '\n' ' ' < "$junit" | grep -q '<testcase name="tests::robustness_goldens::[^"]*"[^>]*>[[:space:]]*<failure'; then
echo "::error::A robustness golden changed. If the new result is intended, re-record it in this pull request and say why:"
@@ -451,11 +493,14 @@ jobs:
fi
exit 1
fi
- name: Compiler cache statistics
if: always()
run: sccache --show-stats
- name: Upload test timings
if: always()
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: junit-${{ matrix.shard }}
name: junit
path: ${{ env.CARGO_TARGET_DIR || 'target' }}/nextest/ci/junit.xml
retention-days: 7
@@ -560,13 +605,23 @@ jobs:
env:
CARGO_INCREMENTAL: "0"
RUSTC_WRAPPER: sccache
SCCACHE_GHA_ENABLED: "true"
SCCACHE_IGNORE_SERVER_IO_ERROR: "1"
SCCACHE_DIR: /home/runner/.cache/sccache
SCCACHE_CACHE_SIZE: 20G
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Size parallel compiles to the job's memory
run: scripts/ci/build-jobs.sh
- uses: dtolnay/rust-toolchain@stable
# sccache lives on a Blacksmith sticky disk (an ext4 volume cloned from the last snapshot,
# no 10 GB limit and no download). Only main commits it; pull requests, the merge queue and
# forks mount a clone and never write back, so nothing they build can reach main's cache.
- name: Mount the compiler cache
uses: useblacksmith/stickydisk@3f6be1451e3cad893b778bc93495c80452a5da26 # v1.7.2
continue-on-error: true
with:
key: ${{ github.repository }}-sccache-doc
path: /home/runner/.cache/sccache
commit: ${{ github.ref == 'refs/heads/main' && 'on-change' || 'false' }}
# Main saves the cache; every other run restores it, read-only.
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
with:
@@ -608,13 +663,23 @@ jobs:
env:
CARGO_INCREMENTAL: "0"
RUSTC_WRAPPER: sccache
SCCACHE_GHA_ENABLED: "true"
SCCACHE_IGNORE_SERVER_IO_ERROR: "1"
SCCACHE_DIR: /home/runner/.cache/sccache
SCCACHE_CACHE_SIZE: 20G
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Size parallel compiles to the job's memory
run: scripts/ci/build-jobs.sh
- uses: dtolnay/rust-toolchain@stable
# sccache lives on a Blacksmith sticky disk (an ext4 volume cloned from the last snapshot,
# no 10 GB limit and no download). Only main commits it; pull requests, the merge queue and
# forks mount a clone and never write back, so nothing they build can reach main's cache.
- name: Mount the compiler cache
uses: useblacksmith/stickydisk@3f6be1451e3cad893b778bc93495c80452a5da26 # v1.7.2
continue-on-error: true
with:
key: ${{ github.repository }}-sccache-oracle
path: /home/runner/.cache/sccache
commit: ${{ github.ref == 'refs/heads/main' && 'on-change' || 'false' }}
# Main saves the cache; every other run restores it, read-only.
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
with:
@@ -746,6 +811,7 @@ jobs:
DOCTESTS: ${{ needs.changes.outputs.doctests }}
CLIPPY: ${{ needs.clippy.result }}
BUILD: ${{ needs.build-tests.result }}
EVENT_NAME: ${{ github.event_name }}
TESTS: ${{ needs.tests.result }}
DOCS: ${{ needs.doc-tests.result }}
ORACLE: ${{ needs.oracle.result }}
@@ -760,7 +826,10 @@ jobs:
fi
if [ "$NATIVE" = true ]; then
test "$CLIPPY" = success
test "$BUILD" = success
# The archive for the action matrix and property tests exists nightly and by hand only.
if [ "$EVENT_NAME" = schedule ] || [ "$EVENT_NAME" = workflow_dispatch ]; then
test "$BUILD" = success
fi
test "$TESTS" = success
fi
if [ "$DOCTESTS" = true ]; then
+41 -2
View File
@@ -52,9 +52,28 @@ Editable drawing code, native projects and shipped exports are collected under `
| docs/book/src/generated/enclosure.png | SolidCraft contributors | rendered by `cargo xtask book` (`solidcraft-cli snapshot docs/book/scripts/enclosure.json`; gitignored) | MIT OR Apache-2.0 |
| docs/book/src/generated/revolve.png | SolidCraft contributors | rendered by `cargo xtask book` (`solidcraft-cli snapshot docs/book/scripts/revolve.json`; gitignored) | MIT OR Apache-2.0 |
| docs/book/src/generated/sheet-metal.png | SolidCraft contributors | rendered by `cargo xtask book` (`solidcraft-cli snapshot docs/book/scripts/sheet-metal.json`; gitignored) | MIT OR Apache-2.0 |
| assets/fonts/ibm-plex-mono/IBMPlexMono-Regular.ttf | IBM Corp. (IBM Plex Mono) | https://github.com/IBM/plex (via google/fonts), unmodified; licence text in assets/fonts/ibm-plex-mono/OFL.txt; the interface monospace font | SIL OFL 1.1 |
| assets/fonts/ibm-plex-sans/IBMPlexSans-Variable.ttf | IBM Corp. (IBM Plex Sans, variable wdth and wght) | https://github.com/IBM/plex (via google/fonts), unmodified; licence text in assets/fonts/ibm-plex-sans/OFL.txt; the default interface font | SIL OFL 1.1 |
| docs/book/src/generated/tool-preview.png | SolidCraft contributors | rendered by `cargo xtask book` (`solidcraft-cli snapshot docs/book/scripts/tool-preview.json`; gitignored) | MIT OR Apache-2.0 |
| assets/fonts/liberation-sans/LiberationSans-Regular.ttf | Red Hat, Inc. (Liberation Fonts 2.x; digitized data Google) | https://github.com/liberationfonts (Debian fonts-liberation), unmodified; licence text in assets/fonts/liberation-sans/OFL.txt; used for sketch Text outlines | SIL OFL 1.1 |
| assets/fonts/biz-udp-gothic/BIZUDPGothic-Regular.ttf | The BIZ UDGothic Project Authors | https://github.com/googlefonts/morisawa-biz-ud-gothic (Google Fonts), unmodified; licence text in assets/fonts/biz-udp-gothic/OFL.txt; used for web UI Japanese font | SIL OFL 1.1 |
| tests/step-conformance/cone_point.step | step-conformance contributors | generated from formulas by https://github.com/dan335/step-conformance (corpus/generated, 96f30bb); import regression fixture | CC0-1.0 |
| tests/step-conformance/cone_point_vertex.step | step-conformance contributors | generated from formulas by https://github.com/dan335/step-conformance (corpus/generated, 96f30bb); import regression fixture | CC0-1.0 |
| tests/step-conformance/cylinder.step | step-conformance contributors | generated from formulas by https://github.com/dan335/step-conformance (corpus/generated, 96f30bb); import regression fixture | CC0-1.0 |
| tests/step-conformance/cylinder_inch.step | step-conformance contributors | generated from formulas by https://github.com/dan335/step-conformance (corpus/generated, 96f30bb); import regression fixture | CC0-1.0 |
| tests/step-conformance/cylinder_nurbs.step | step-conformance contributors | generated from formulas by https://github.com/dan335/step-conformance (corpus/generated, 96f30bb); import regression fixture | CC0-1.0 |
| tests/step-conformance/cylinder_placed.step | step-conformance contributors | generated from formulas by https://github.com/dan335/step-conformance (corpus/generated, 96f30bb); import regression fixture | CC0-1.0 |
| tests/step-conformance/cylinder_seam.step | step-conformance contributors | generated from formulas by https://github.com/dan335/step-conformance (corpus/generated, 96f30bb); import regression fixture | CC0-1.0 |
| tests/step-conformance/cylinder_twice.step | step-conformance contributors | generated from formulas by https://github.com/dan335/step-conformance (corpus/generated, 96f30bb); import regression fixture | CC0-1.0 |
| tests/step-conformance/dome.step | step-conformance contributors | generated from formulas by https://github.com/dan335/step-conformance (corpus/generated, 96f30bb); import regression fixture | CC0-1.0 |
| tests/step-conformance/dome_nurbs.step | step-conformance contributors | generated from formulas by https://github.com/dan335/step-conformance (corpus/generated, 96f30bb); import regression fixture | CC0-1.0 |
| tests/step-conformance/far_cylinder.step | step-conformance contributors | generated from formulas by https://github.com/dan335/step-conformance (corpus/generated, 96f30bb); import regression fixture | CC0-1.0 |
| tests/step-conformance/grooved_shaft.step | step-conformance contributors | generated from formulas by https://github.com/dan335/step-conformance (corpus/generated, 96f30bb); import regression fixture | CC0-1.0 |
| tests/step-conformance/plate_100_holes.step | step-conformance contributors | generated from formulas by https://github.com/dan335/step-conformance (corpus/generated, 96f30bb); import regression fixture | CC0-1.0 |
| tests/step-conformance/sheet_disc.step | step-conformance contributors | generated from formulas by https://github.com/dan335/step-conformance (corpus/generated, 96f30bb); import regression fixture | CC0-1.0 |
| tests/step-conformance/tiny_pin.step | step-conformance contributors | generated from formulas by https://github.com/dan335/step-conformance (corpus/generated, 96f30bb); import regression fixture | CC0-1.0 |
| tests/step-conformance/torus_ring_nurbs.step | step-conformance contributors | generated from formulas by https://github.com/dan335/step-conformance (corpus/generated, 96f30bb); import regression fixture | CC0-1.0 |
| tests/step-conformance/washer.step | step-conformance contributors | generated from formulas by https://github.com/dan335/step-conformance (corpus/generated, 96f30bb); import regression fixture | CC0-1.0 |
| tests/oracle/17-loft/reference.stl | SolidCraft contributors | triangulation of our own recipe (tests/oracle/17-loft/recipe.json) built with FreeCAD as an external tool by tools/freecad-oracle | MIT OR Apache-2.0 |
| tests/oracle/23-bracket-multifeature/reference.stl | SolidCraft contributors | triangulation of our own recipe (tests/oracle/23-bracket-multifeature/recipe.json) built with FreeCAD as an external tool by tools/freecad-oracle | MIT OR Apache-2.0 |
| tests/oracle/28-blind-pocket-fillets/reference.stl | SolidCraft contributors | triangulation of our own recipe (tests/oracle/28-blind-pocket-fillets/recipe.json) built with FreeCAD as an external tool by tools/freecad-oracle | MIT OR Apache-2.0 |
@@ -78,6 +97,8 @@ Editable drawing code, native projects and shipped exports are collected under `
| assets/appearances/library.rs | SolidCraft contributors | Original SolidCraft resource data or drawing code, collected without redesign | MIT OR Apache-2.0 |
| assets/fonts/biz-udp-gothic/OFL.txt | The BIZ UDGothic Project Authors | Unmodified accompanying font licence | SIL OFL 1.1 |
| assets/fonts/ibm-plex-mono/OFL.txt | IBM Corp. | Unmodified accompanying font licence | SIL OFL 1.1 |
| assets/fonts/ibm-plex-sans/OFL.txt | IBM Corp. | Unmodified accompanying font licence | SIL OFL 1.1 |
| assets/fonts/liberation-sans/OFL.txt | Red Hat, Inc.; Liberation Fonts contributors | Unmodified accompanying font licence | SIL OFL 1.1 |
| assets/icons/app/linux/exports/hicolor/128x128/apps/ai.storyteller.solidcraft.png | SolidCraft contributors | Generated from assets/icons/app/shared/source/app_icon.rs with the original CLI/ICO encoders | MIT OR Apache-2.0 |
| assets/icons/app/linux/exports/hicolor/16x16/apps/ai.storyteller.solidcraft.png | SolidCraft contributors | Generated from assets/icons/app/shared/source/app_icon.rs with the original CLI/ICO encoders | MIT OR Apache-2.0 |
@@ -102,17 +123,23 @@ Editable drawing code, native projects and shipped exports are collected under `
| assets/locales/es.json | jawler | Spanish (Spain) translation of the English catalog, contributed upstream | MIT OR Apache-2.0 |
| assets/locales/fr.json | rlartot | French translation of the English catalog, contributed upstream | MIT OR Apache-2.0 |
| assets/locales/gl.json | Pablo López (pablopeza) | Galician translation of the English catalog, contributed upstream | MIT OR Apache-2.0 |
| assets/locales/hu.json | SolidCraft contributors | Original Hungarian translation of SolidCraft's English catalog; no commercial CAD text used | MIT OR Apache-2.0 |
| assets/locales/id.json | SolidCraft contributors | Original Indonesian translation of SolidCraft's English catalog; no commercial CAD text used | MIT OR Apache-2.0 |
| assets/locales/it.json | SolidCraft contributors | Original Italian translation of SolidCraft's English catalog; no commercial CAD text used | MIT OR Apache-2.0 |
| assets/locales/ja.json | SolidCraft contributors | Original SolidCraft resource data or drawing code, collected without redesign | MIT OR Apache-2.0 |
| assets/locales/ko.json | SolidCraft contributors | Original SolidCraft resource data or drawing code, collected without redesign | MIT OR Apache-2.0 |
| assets/locales/ms.json | A.Hakim (a-hakim) | Malay translation of the English message catalog | MIT OR Apache-2.0 |
| assets/locales/nl.json | SolidCraft contributors | Original Dutch translation of SolidCraft's English catalog; no commercial CAD text used | MIT OR Apache-2.0 |
| assets/locales/pl.json | cyryllo | Polish translation of the English catalog, contributed upstream | MIT OR Apache-2.0 |
| assets/locales/ru.json | Alex Pro (del000) | Russian translation of the English catalog, contributed upstream | MIT OR Apache-2.0 |
| assets/locales/sv.json | SolidCraft contributors | Original Swedish translation of SolidCraft's English catalog; no commercial CAD text used | MIT OR Apache-2.0 |
| assets/locales/pt-BR.json | DuBearte | Brazilian Portuguese translation of the English catalog, contributed upstream | MIT OR Apache-2.0 |
| assets/locales/ro.json | SolidCraft contributors | Original Romanian translation of SolidCraft's English catalog; no commercial CAD text used | MIT OR Apache-2.0 |
| assets/locales/tr.json | macrulz54 | Turkish translation of the English catalog, contributed upstream | MIT OR Apache-2.0 |
| assets/locales/uk.json | Igor Bronovskyi (BrunIF) | Ukrainian translation of the English catalog, contributed upstream | MIT OR Apache-2.0 |
| assets/locales/vi.json | vandang890615 | Vietnamese translation of the English catalog, contributed upstream | MIT OR Apache-2.0 |
| assets/locales/zh.json | SolidCraft contributors | Original SolidCraft resource data or drawing code, collected without redesign | MIT OR Apache-2.0 |
| assets/locales/zh-TW.json | SolidCraft contributors | Original Traditional Chinese (Taiwan) translation of SolidCraft's English catalog; no commercial CAD text used | MIT OR Apache-2.0 |
| assets/materials/library.rs | SolidCraft contributors | Original SolidCraft resource data or drawing code, collected without redesign | MIT OR Apache-2.0 |
| assets/platforms/linux/ai.storyteller.solidcraft.desktop | PhotoCraft / SolidCraft contributors | PhotoCraft packaging templates (https://github.com/storytold/photocraft), adapted for SolidCraft | Apache-2.0 |
| assets/platforms/linux/ai.storyteller.solidcraft.metainfo.xml.in | PhotoCraft / SolidCraft contributors | PhotoCraft packaging templates (https://github.com/storytold/photocraft), adapted for SolidCraft | Apache-2.0 |
@@ -137,6 +164,10 @@ Editable drawing code, native projects and shipped exports are collected under `
| assets/icons/ui/design/palette.toml | SolidCraft contributors | icon colour roles (light and dark) and accents, original SolidCraft data | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/bug_report.svg | SolidCraft contributors | single-colour application/navigation icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/chevron.svg | SolidCraft contributors | single-colour application/navigation icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/chevron_right.svg | SolidCraft contributors | single-colour application/navigation icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/close.svg | SolidCraft contributors | single-colour application/navigation icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/disclosure_closed.svg | SolidCraft contributors | single-colour application/navigation icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/disclosure_open.svg | SolidCraft contributors | single-colour application/navigation icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/fit.svg | SolidCraft contributors | single-colour application/navigation icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/folder.svg | SolidCraft contributors | single-colour application/navigation icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/help.svg | SolidCraft contributors | single-colour application/navigation icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
@@ -145,6 +176,7 @@ Editable drawing code, native projects and shipped exports are collected under `
| assets/icons/ui/design/svg/flat/moon.svg | SolidCraft contributors | single-colour application/navigation icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/nav_settings.svg | SolidCraft contributors | single-colour application/navigation icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/orbit.svg | SolidCraft contributors | single-colour application/navigation icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/plus.svg | SolidCraft contributors | single-colour application/navigation icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/pan.svg | SolidCraft contributors | single-colour application/navigation icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/perspective.svg | SolidCraft contributors | single-colour application/navigation icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/redo.svg | SolidCraft contributors | single-colour application/navigation icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
@@ -230,7 +262,7 @@ Editable drawing code, native projects and shipped exports are collected under `
| assets/icons/ui/design/svg/model/export.svg | SolidCraft contributors | two-tone modelling icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/extend.svg | SolidCraft contributors | two-tone modelling icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/extrude.svg | SolidCraft contributors | two-tone modelling icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/eye.svg | SolidCraft contributors | two-tone modelling icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/eye.svg | SolidCraft contributors | single-colour application/navigation icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/fastener.svg | SolidCraft contributors | two-tone modelling icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/fillet.svg | SolidCraft contributors | two-tone modelling icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/filter.svg | SolidCraft contributors | two-tone modelling icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
@@ -281,6 +313,12 @@ Editable drawing code, native projects and shipped exports are collected under `
| assets/icons/ui/design/svg/model/perforate.svg | SolidCraft contributors | two-tone modelling icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/pipe.svg | SolidCraft contributors | two-tone modelling icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/silhouette.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/assistant.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/agent_link.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/offset_two_sides.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/offset_symmetric.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/import_tray.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/export_tray.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/placeholder.svg | SolidCraft contributors | two-tone modelling icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/plane.svg | SolidCraft contributors | two-tone modelling icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/plane_angle.svg | SolidCraft contributors | two-tone modelling icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
@@ -332,7 +370,7 @@ Editable drawing code, native projects and shipped exports are collected under `
| assets/icons/ui/design/svg/model/status_error.svg | SolidCraft contributors | two-tone modelling icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/status_warning.svg | SolidCraft contributors | two-tone modelling icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/stitch.svg | SolidCraft contributors | two-tone modelling icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/suppress.svg | SolidCraft contributors | two-tone modelling icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/flat/suppress.svg | SolidCraft contributors | single-colour application/navigation icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/surface.svg | SolidCraft contributors | two-tone modelling icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/svg.svg | SolidCraft contributors | two-tone modelling icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/sweep.svg | SolidCraft contributors | two-tone modelling icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
@@ -416,6 +454,7 @@ Editable drawing code, native projects and shipped exports are collected under `
| assets/icons/ui/design/tools/lint_icons.py | SolidCraft contributors | icon lint, status page or preview tool (standard-library Python), original SolidCraft code | MIT OR Apache-2.0 |
| assets/icons/ui/design/tools/preview.py | SolidCraft contributors | icon lint, status page or preview tool (standard-library Python), original SolidCraft code | MIT OR Apache-2.0 |
| assets/icons/ui/design/tools/review_state.py | SolidCraft contributors | icon review state helper (standard-library Python), original SolidCraft code | MIT OR Apache-2.0 |
| assets/icons/ui/design/requested_icons.txt | SolidCraft contributors | Runtime icon requests collected by the headless menu audit, original SolidCraft data | MIT OR Apache-2.0 |
| assets/icons/ui/design/review.json | SolidCraft contributors | icon review state (accepted icons and notes), original SolidCraft data | MIT OR Apache-2.0 |
| assets/icons/ui/design/tools/author/lib.py | SolidCraft contributors | icon authoring script (standard-library Python), original SolidCraft code | MIT OR Apache-2.0 |
+1 -1
View File
@@ -199,7 +199,7 @@ Check that validation after merging. They are listed in
`.config/nextest.toml` and the [testing page](docs/book/src/dev/testing.md):
- `solidcraft-engine`: `tests::hostile_params_never_panic`, everything in `tests::robustness` and
the seeds of `tests::robustness_goldens` (`seed_00` … `seed_95`; pull requests matching the workflow's geometry paths run all but the slowest, `SLOW_SEEDS` in `ci.yml`),
the seeds of `tests::robustness_goldens` (`seed_00` … `seed_95`; pull requests that change the kernel, truck or the goldens run all but the slowest, `SLOW_SEEDS` in `ci.yml`),
`pattern_suppress_tests::enclosure_arc_extinction_keeps_a_real_sharp_inner_lip_after_resize_and_reopen`,
`pattern_suppress_tests::enclosure_datums_follow_wall_height_and_board_depth_with_material_proofs`,
`cmd::plastic::tests::enclosure_snap_fit_follows_resized_front_wall`, the `atomic_commands` contract tests
Generated
+18 -17
View File
@@ -4162,7 +4162,7 @@ dependencies = [
[[package]]
name = "solidcraft"
version = "0.17.0"
version = "0.18.1"
dependencies = [
"block2 0.6.2",
"eframe",
@@ -4193,11 +4193,11 @@ dependencies = [
[[package]]
name = "solidcraft-3dx-client"
version = "0.17.0"
version = "0.18.1"
[[package]]
name = "solidcraft-cli"
version = "0.17.0"
version = "0.18.1"
dependencies = [
"image",
"serde_json",
@@ -4208,7 +4208,7 @@ dependencies = [
[[package]]
name = "solidcraft-derive"
version = "0.17.0"
version = "0.18.1"
dependencies = [
"proc-macro2",
"quote",
@@ -4217,7 +4217,7 @@ dependencies = [
[[package]]
name = "solidcraft-doc"
version = "0.17.0"
version = "0.18.1"
dependencies = [
"log",
"serde",
@@ -4234,7 +4234,7 @@ dependencies = [
[[package]]
name = "solidcraft-engine"
version = "0.17.0"
version = "0.18.1"
dependencies = [
"log",
"schemars",
@@ -4253,7 +4253,7 @@ dependencies = [
[[package]]
name = "solidcraft-geom"
version = "0.17.0"
version = "0.18.1"
dependencies = [
"serde",
"serde_json",
@@ -4262,7 +4262,7 @@ dependencies = [
[[package]]
name = "solidcraft-gpu"
version = "0.17.0"
version = "0.18.1"
dependencies = [
"log",
"naga",
@@ -4279,7 +4279,7 @@ dependencies = [
[[package]]
name = "solidcraft-io"
version = "0.17.0"
version = "0.18.1"
dependencies = [
"cadmpeg-core",
"cadmpeg-ir",
@@ -4302,7 +4302,7 @@ dependencies = [
[[package]]
name = "solidcraft-kernel"
version = "0.17.0"
version = "0.18.1"
dependencies = [
"log",
"proptest",
@@ -4322,7 +4322,7 @@ dependencies = [
[[package]]
name = "solidcraft-mcp"
version = "0.17.0"
version = "0.18.1"
dependencies = [
"serde_json",
"solidcraft-engine",
@@ -4331,7 +4331,7 @@ dependencies = [
[[package]]
name = "solidcraft-render"
version = "0.17.0"
version = "0.18.1"
dependencies = [
"image",
"moxcms",
@@ -4345,7 +4345,7 @@ dependencies = [
[[package]]
name = "solidcraft-sketch"
version = "0.17.0"
version = "0.18.1"
dependencies = [
"serde",
"serde_json",
@@ -4356,14 +4356,14 @@ dependencies = [
[[package]]
name = "solidcraft-space-mouse"
version = "0.17.0"
version = "0.18.1"
dependencies = [
"serde",
]
[[package]]
name = "solidcraft-ui-egui"
version = "0.17.0"
version = "0.18.1"
dependencies = [
"arboard",
"egui",
@@ -4379,6 +4379,7 @@ dependencies = [
"solidcraft-engine",
"solidcraft-gpu",
"solidcraft-space-mouse",
"syn 2.0.119",
"tiny-skia",
"toml",
"ttf-parser",
@@ -4389,7 +4390,7 @@ dependencies = [
[[package]]
name = "solidcraft-web"
version = "0.17.0"
version = "0.18.1"
dependencies = [
"eframe",
"egui",
@@ -5919,7 +5920,7 @@ checksum = "e450f9b2ed1dff33c94c12589a87338689467b9c4f5d8a5710bd09a847d2c8a7"
[[package]]
name = "xtask"
version = "0.17.0"
version = "0.18.1"
dependencies = [
"serde_json",
"sha2 0.10.9",
+1 -1
View File
@@ -5,7 +5,7 @@ members = ["crates/*", "apps/*", "xtask"]
exclude = ["vendor"]
[workspace.package]
version = "0.17.0"
version = "0.18.1"
edition = "2024"
license = "MIT OR Apache-2.0"
rust-version = "1.95"
+5
View File
@@ -41,6 +41,11 @@
<a href="https://github.com/tangency-labs/solidcraft/stargazers"><img src="https://img.shields.io/github/stars/tangency-labs/solidcraft?style=flat" alt="Stars" /></a>
</p>
<p align="center">
<a href="https://trendshift.io/repositories/296178?utm_source=trendshift-badge&amp;utm_medium=badge&amp;utm_campaign=badge-trendshift-296178" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/trendshift/repositories/296178/daily" alt="tangency-labs%2Fsolidcraft | Trendshift" width="250" height="55"/></a>
<a href="https://trendshift.io/repositories/296178?utm_source=trendshift-badge&amp;utm_medium=badge&amp;utm_campaign=badge-trendshift-296178" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/trendshift/repositories/296178/daily?language=Rust" alt="tangency-labs%2Fsolidcraft | Trendshift (Rust)" width="250" height="55"/></a>
</p>
![SolidCraft 0.8: model a Raspberry Pi 4 case from an empty design: a sketch dimensioned with parameters, extrude, fillets and shell, a rim rounded in one pick, standoffs patterned on the board's mounting holes, port cut-outs, vent slots, "Pi 4" engraved in a font, a PETG material with its mass in Properties, and a height change in Parameters](docs/screenshots/demo-0.8.gif)
*A Raspberry Pi 4 case from an empty design: a sketch dimensioned with the board's parameters, extrude, fillet, shell, the rim rounded in one pick, an M2.5 standoff patterned on the 58 × 49 mm hole rectangle, port cut-outs, vent slots, "Pi 4" engraved in DejaVu Sans Bold and a PETG material defined in the design, with its mass in Properties, then height 24 → 32 mm in Parameters and it all rebuilds. 9× playback. [Full video](https://github.com/tangency-labs/solidcraft/raw/refs/heads/main/docs/screenshots/demo-0.8.mp4), recorded with the [demo script](examples/demo/record_ui.sh).*
+1 -1
View File
@@ -50,7 +50,7 @@ PCB / Package, Render, Animation, Simulation, Manufacture/CAM, Drawings.
| # | Milestone | Status | Hours left |
|---|---|---|---|
| M0 | Vertical slice | done | — |
| M1 | Sketch depth: command coverage implemented — projection/intersect/include as linked geometry (auto-project on faces, lost-reference warnings, break link), full SKETCH create/modify set (slots, polygons, tangent arcs/circles, ellipse, fit/control splines, conic, text, trim/extend/break, fillet/chamfer, offset, mirror, patterns, move/scale, blend curve, centerline), constraints incl. curvature and polygon with over-constraint refusal, driven/arc-length/linear-diameter dimensions, AutoConstrain, DXF/SVG insert, 3D sketch curves (Project To Surface, Intersection Curve, Isoparametric Curve, Spun Profile), exact free-form profiles (B-spline/NURBS edges, any spline degree, curves cut by crossings), sweeps and pipes along 3D sketch curves, canvases and decals, curvature combs. Exact free-form Extend and certified associative offsets now preserve geometry and IDs through edits, deletion and reopen. Occurrence-frame 3D wires now store explicit coordinate spaces and retain points and analytic fits through moves, refresh, undo and reopen. Constructive sketch solving (Owen 1991, Fudos & Hoffmann 1997) ahead of the numerical solver: points and rigid clusters are placed on the branch the sketch had, so dimension edits and drags keep their solution; edits that would turn a sketch over are refused; drags re-solve only the sketch per frame. Remaining workflow repairs: mixed offset junctions, legacy natural-spline seams, and damaged legacy wire links lacking frame provenance | in progress | 4 |
| M1 | Sketch depth: command coverage implemented — projection/intersect/include as linked geometry (auto-project on faces, lost-reference warnings, break link), full SKETCH create/modify set (slots, polygons, tangent arcs/circles, ellipse, fit/control splines, conic, text, trim/extend/break, fillet/chamfer, offset, mirror, patterns, move/scale, blend curve, centerline), constraints incl. curvature and polygon with over-constraint refusal, Shift-picked line midpoints in Horizontal/Vertical using persisted points and ordinary constraints (#2087), driven/arc-length/linear-diameter dimensions, AutoConstrain, DXF/SVG insert, 3D sketch curves (Project To Surface, Intersection Curve, Isoparametric Curve, Spun Profile), exact free-form profiles (B-spline/NURBS edges, any spline degree, curves cut by crossings), sweeps and pipes along 3D sketch curves, canvases and decals, curvature combs. Exact free-form Extend and certified associative offsets now preserve geometry and IDs through edits, deletion and reopen. Occurrence-frame 3D wires now store explicit coordinate spaces and retain points and analytic fits through moves, refresh, undo and reopen. Constructive sketch solving (Owen 1991, Fudos & Hoffmann 1997) ahead of the numerical solver: points and rigid clusters are placed on the branch the sketch had, so dimension edits and drags keep their solution; edits that would turn a sketch over are refused; drags re-solve only the sketch per frame. Remaining workflow repairs: mixed offset junctions, legacy natural-spline seams, and damaged legacy wire links lacking frame provenance | in progress | 4 |
| M2 | Robust modelling: coplanar booleans in the kernel, holes (placed on faces and on sketch points), threads (cosmetic, then modelled), patterns, mirror, shell, draft, split, construction geometry | in progress | 16 |
| M3 | General fillets and chamfers (curved edges, chains, vertex blends); saddle rolling-ball surfaces now use continuous seam sections and arc-length fitting with shared contact boundaries and bounded accuracy checks; tiny rim edges remain selected instead of nearby wall seams when filleting (#2498) | in progress | 30 |
| M4 | Files: STEP import (done — [docs/step-import.md](docs/step-import.md)), FreeCAD/Rhino solids through cadmpeg (done, desktop and CLI), 3MF export and 3MF/STL import as mesh bodies (done), 3MF with B-rep face tags ([docs/formats/3mf-face-tags.md](docs/formats/3mf-face-tags.md), done), constant-section parametric mesh reconstruction, DXF sketches, OBJ import | in progress | 6 |
+8
View File
@@ -16144,6 +16144,14 @@ Bundled fonts (SIL Open Font License, Version 1.1)
Copyright 2022 The BIZ UDGothic Project Authors (https://github.com/googlefonts/morisawa-biz-ud-mincho)
Source: https://github.com/googlefonts/morisawa-biz-ud-gothic
- IBMPlexSans-Variable.ttf (assets/fonts/ibm-plex-sans/IBMPlexSans-Variable.ttf)
Copyright 2017 IBM Corp. with Reserved Font Name "Plex"
Source: https://github.com/IBM/plex
- IBMPlexMono-Regular.ttf (assets/fonts/ibm-plex-mono/IBMPlexMono-Regular.ttf)
Copyright 2017 IBM Corp. with Reserved Font Name "Plex"
Source: https://github.com/IBM/plex
================================================================================
SIL OPEN FONT LICENSE Version 1.1
================================================================================
+38 -2
View File
@@ -4,7 +4,7 @@
//! solidcraft-cli run <script.json|design.solidcraft|part.step|mesh.3mf> [--in design|part.step|mesh.3mf] [--out FILE]... [--save FILE] [--quiet] [--results] [--timings] [--profile] [--json-out FILE] [--allow-errors]
//! solidcraft-cli eval <script.json|design.solidcraft|part.step> [--json-out FILE] (alias: inspect) measurements as JSON
//! solidcraft-cli script <design.solidcraft> [--out script.json] the design's history as a command script
//! solidcraft-cli bench-open <design.solidcraft | part.step> [--parse-only] [--save OUT.solidcraft] [--display LEVEL [--edit NAME EXPR]] loading timings as JSON lines
//! solidcraft-cli bench-open <design.solidcraft | part.step> [--parse-only] [--save OUT.solidcraft] [--display LEVEL [--edit NAME EXPR]] [--ops script.json] loading timings as JSON lines
//! solidcraft-cli bench-agent [--scale F] [--save F] [--script-out F] a ~1,300-body design built command by command, timed
//! solidcraft-cli snapshot <script|design> [--in design|part.step] --out shot.png [--width W] [--height H] [--view iso|front|top|…|NAME | --camera JSON] [--no-grid] [--no-axes]
//! [--body NAME:opacity=0.3,color=#rrggbb]... [--tool NAME]... [--section z=27]
@@ -50,7 +50,9 @@ const USAGE: &str = "usage:
as plain UTF-8: a shell's > can re-encode it, e.g. Windows PowerShell adds a byte-order mark)
solidcraft-cli script <design.solidcraft|script.json> [--out script.json]
(the design's history as a command script that eval and run replay)
solidcraft-cli bench-open <design.solidcraft | part.step> [--parse-only] [--save OUT.solidcraft] [--display LEVEL [--edit NAME EXPR]]
solidcraft-cli bench-open <design.solidcraft | part.step> [--parse-only] [--save OUT.solidcraft] [--display LEVEL [--edit NAME EXPR]] [--ops script.json]
(--ops: after opening, run each command of the script and undo it, timing the command, the
evaluations it caused and the view it leaves: placing and meshing what changed)
solidcraft-cli bench-agent [--scale F] [--save design.solidcraft] [--script-out script.json]
solidcraft-cli snapshot <script|design> [--in design.solidcraft|part.step] --out shot.png [--width W] [--height H] [--view NAME | --camera JSON] [--no-grid] [--no-axes]
[--body NAME:opacity=0.3,color=#rrggbb]... [--tool NAME]... [--section z=27]
@@ -642,6 +644,36 @@ fn cmd_bench_edit(args: &[String]) -> Result<(), String> {
Ok(())
}
/// `bench-open --ops`: each command of `script` (a JSON array of `{command, params}`), then its
/// undo, each timed as the command and the view it leaves (the placed model, its meshes warmed
/// as the desktop does after a change).
fn bench_ops(mut s: Session, script: &str) -> Result<Session, String> {
let text = std::fs::read_to_string(script).map_err(|e| format!("{script}: {e}"))?;
let ops: Value = serde_json::from_str(&text).map_err(|e| format!("{script}: {e}"))?;
let ops = ops.as_array().ok_or("--ops: a JSON array of {command, params}")?;
let time = |s: &mut Session, id: &str, p: &Value| -> Result<Value, String> {
let t = std::time::Instant::now();
let r = s.execute(id, p).map_err(|e| format!("{id}: {e}"))?;
let ms = t.elapsed().as_secs_f64() * 1000.0;
let t = std::time::Instant::now();
s.world_state().warm_meshes(|| true);
let view_ms = t.elapsed().as_secs_f64() * 1000.0;
println!("{}", json!({"step": "op", "command": id, "ms": ms, "view_ms": view_ms}));
Ok(r)
};
for op in ops {
let id = op.get("command").and_then(Value::as_str).ok_or("--ops: each step needs a `command`")?;
let p = op.get("params").cloned().unwrap_or(Value::Null);
let steps = s.undo.len();
time(&mut s, id, &p)?;
// Commands that are no undo step (showing and hiding) have nothing to undo.
if op.get("undo").and_then(Value::as_bool) != Some(false) && s.undo.len() > steps {
time(&mut s, "edit.undo", &json!({}))?;
}
}
Ok(s)
}
/// Separate file reading, JSON decoding/repair, timeline evaluation and display meshing.
pub(crate) fn cmd_bench_open(args: &[String]) -> Result<(), String> {
let path = args.first().ok_or("usage: solidcraft-cli bench-open <design.solidcraft | part.step> [--parse-only]")?;
@@ -727,6 +759,10 @@ pub(crate) fn cmd_bench_open(args: &[String]) -> Result<(), String> {
// `--display LEVEL`: the display plans the viewport draws at that display quality, made as
// the GPU viewport makes them; `--edit NAME EXPR` then changes a parameter and times the
// evaluation and the plans of the bodies it changed.
let s = match flag("--ops").and_then(|i| args.get(i + 1)) {
Some(ops) => bench_ops(s, ops)?,
None => s,
};
let Some(level) = flag("--display").and_then(|i| args.get(i + 1)) else { return Ok(()) };
let q = solidcraft_engine::doc::display_quality::DisplayQuality::parse(level).ok_or("--display: coarse, normal, fine or ultra")?;
let plans = |st: &solidcraft_engine::doc::ModelState| {
+1
View File
@@ -29,5 +29,6 @@ web-sys = { version = "0.3", features = [
"Window", "Document", "Element", "HtmlElement", "HtmlCanvasElement", "Location", "Navigator", "StorageManager",
"FileSystemHandle", "FileSystemDirectoryHandle", "FileSystemFileHandle", "FileSystemGetFileOptions", "FileSystemWritableFileStream",
"WritableStream", "File", "FileList", "Blob", "BlobPropertyBag", "Url", "HtmlAnchorElement", "HtmlInputElement", "Storage",
"IdbFactory", "IdbDatabase", "IdbObjectStore", "IdbOpenDbRequest", "IdbRequest", "IdbTransaction", "IdbTransactionMode",
"BeforeUnloadEvent", "Event", "EventTarget", "Response", "MediaQueryList", "Gamepad", "Performance",
] }
+637
View File
@@ -0,0 +1,637 @@
//! Designs on the computer's own disk, through the File System Access API (Chrome, Edge, Opera):
//! Save As and Open show the browser's file dialogs, and the chosen file's handle is kept (in
//! memory, and in IndexedDB so Save still reaches the file after a reload) so Save writes back to
//! it. To the engine such a design is the file `/disk/<id>/<name>`: it saves there like anywhere,
//! and [`flush`](crate::files::flush) forwards what it wrote to the handle. Browsers without the
//! API (Firefox, Safari) keep designs in the origin private file system (`files.rs`).
//!
//! The path logic is plain Rust and tested natively; the browser calls are wasm-only.
#![cfg_attr(not(target_arch = "wasm32"), allow(dead_code))]
/// Where a design on the computer's disk appears to the engine.
pub const PREFIX: &str = "/disk/";
/// Where designs kept in the browser's private storage (OPFS) appear to the engine.
pub const OPFS_PREFIX: &str = "/opfs/";
/// Paths written here are handed to the user as downloads.
pub const DOWNLOADS_PREFIX: &str = "/downloads/";
/// Where what a command wrote to an engine path goes.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum WriteTo {
/// The browser's private storage: the design (or recovery file) keeps its name there.
Opfs,
/// A file on the computer, through its handle.
Disk,
/// A download.
Download,
/// Kept only in memory (uploads).
Memory,
}
/// Route a write by its path. OPFS paths always go to OPFS, whatever the browser can do: a design
/// opened from the browser's storage keeps saving there until the user picks Save As.
pub fn route(path: &str) -> WriteTo {
if path.starts_with(OPFS_PREFIX) {
WriteTo::Opfs
} else if parse(path).is_some() {
WriteTo::Disk
} else if path.starts_with(DOWNLOADS_PREFIX) {
WriteTo::Download
} else {
WriteTo::Memory
}
}
/// The engine path of the design `name` kept under handle `id`.
pub fn path(id: &str, name: &str) -> String {
format!("{PREFIX}{id}/{name}")
}
/// The handle id and file name of an engine path made by [`path`].
pub fn parse(path: &str) -> Option<(&str, &str)> {
let (id, name) = path.strip_prefix(PREFIX)?.split_once('/')?;
(!id.is_empty() && !name.is_empty() && !name.contains('/')).then_some((id, name))
}
/// A handle id from the time and a random number.
pub fn make_id(ms: u64, random: u64) -> String {
format!("{ms:x}-{random:x}")
}
/// How long a remembered file may go unused, and unreferenced by the recent designs and the
/// autosave, before the browser's copy of its handle is dropped.
pub const HANDLE_MAX_AGE_MS: f64 = 90.0 * 24.0 * 3600.0 * 1000.0;
/// What to do with the remembered handles.
#[derive(Debug, Default, PartialEq)]
pub struct PrunePlan {
/// Handles to forget.
pub delete: Vec<String>,
/// Handles seen unused for the first time: start their clock.
pub stamp: Vec<String>,
}
/// Decide which of the remembered handles `ids` (id, when it was last stamped) to forget: those
/// that no recent design or autosave (`keep`) names and that have gone unused for `max_age`. One
/// never stamped starts its clock now, so a file another tab has just saved is never dropped.
pub fn plan_prune(ids: &[(String, Option<f64>)], keep: &std::collections::HashSet<String>, now: f64, max_age: f64) -> PrunePlan {
let mut plan = PrunePlan::default();
for (id, stamped) in ids.iter().filter(|(id, _)| !keep.contains(id)) {
match stamped {
None => plan.stamp.push(id.clone()),
Some(at) if now - at > max_age => plan.delete.push(id.clone()),
Some(_) => {}
}
}
plan
}
/// Where a design saved with Save As goes.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SaveTarget {
/// A file the user picks on their computer.
Disk,
/// The browser's private storage (OPFS): what browsers without the API have always done.
Browser,
/// Not a design (an export): handed over as a download.
Download,
}
/// Choose where a Save As goes: designs to the disk when the browser can pick a file, else into
/// the browser's storage; anything else is a download.
pub fn save_target(api: bool, design: bool) -> SaveTarget {
match (design, api) {
(false, _) => SaveTarget::Download,
(true, true) => SaveTarget::Disk,
(true, false) => SaveTarget::Browser,
}
}
/// Does a picker for `extensions` (what the command reads; `&[]`: anything) belong to a design?
/// Only those go through the disk dialog; inserting a STEP or mesh keeps using the file window.
pub fn opens_design(extensions: &[&str]) -> bool {
extensions.is_empty() || extensions.iter().any(|e| solidcraft_engine::io::DESIGN_EXTS.contains(e))
}
/// The file type a Save As dialog offers for `name`: (description, MIME type, extension).
pub fn save_type(name: &str) -> (&'static str, &'static str, &'static str) {
let json = std::path::Path::new(name).extension().is_some_and(|e| e.eq_ignore_ascii_case("json"));
if json { ("Plain JSON design", "application/json", ".json") } else { ("SolidCraft design", "application/octet-stream", ".solidcraft") }
}
/// Is this a cancelled dialog (the browser's `AbortError`)? Not an error to show.
pub fn is_abort(error_name: &str) -> bool {
error_name == "AbortError"
}
#[cfg(target_arch = "wasm32")]
pub use web::*;
#[cfg(target_arch = "wasm32")]
mod web {
use std::cell::{Cell, RefCell};
use std::collections::{HashMap, VecDeque};
use std::rc::Rc;
use js_sys::{Array, Function, Object, Promise, Reflect};
use solidcraft_engine::io::vfs;
use wasm_bindgen::{JsCast as _, JsValue, closure::Closure};
use wasm_bindgen_futures::{JsFuture, spawn_local};
use web_sys::{FileSystemFileHandle, IdbDatabase, IdbObjectStore, IdbOpenDbRequest, IdbRequest, IdbTransactionMode};
use super::{HANDLE_MAX_AGE_MS, is_abort, make_id, opens_design, parse, path, plan_prune, save_type};
use crate::files::{Files, err, read_handle};
const DB: &str = "solidcraft-disk-handles";
const STORE: &str = "handles";
thread_local! {
/// Handles picked or looked up in this session, by id.
static HANDLES: RefCell<HashMap<String, FileSystemFileHandle>> = RefCell::new(HashMap::new());
/// Writes waiting for their turn (a file takes one writer at a time), latest per id.
static QUEUE: RefCell<VecDeque<(String, Vec<u8>)>> = const { RefCell::new(VecDeque::new()) };
static BUSY: Cell<bool> = const { Cell::new(false) };
}
/// Why a dialog gave no file.
enum Pick {
Cancelled,
Failed(String),
}
fn failed(e: JsValue) -> Pick {
let name = Reflect::get(&e, &"name".into()).ok().and_then(|n| n.as_string()).unwrap_or_default();
if is_abort(&name) { Pick::Cancelled } else { Pick::Failed(err(e)) }
}
/// Can this page show the browser's file dialogs and write to the files they pick?
pub fn supported() -> bool {
let Some(window) = web_sys::window() else { return false };
let has = |name: &str| Reflect::get(&window, &name.into()).is_ok_and(|f| f.is_function());
window.is_secure_context() && has("showSaveFilePicker") && has("showOpenFilePicker")
}
fn types(description: &str, mime: &str, extension: &str) -> Array {
let accept = Object::new();
let _ = Reflect::set(&accept, &mime.into(), &Array::of1(&extension.into()));
let entry = Object::new();
let _ = Reflect::set(&entry, &"description".into(), &description.into());
let _ = Reflect::set(&entry, &"accept".into(), &accept);
Array::of1(&entry)
}
/// Start a dialog. This must run in the click that asked for it (the browser wants a user
/// gesture), so it is called before anything is awaited.
fn start_picker(function: &str, options: &Object) -> Result<Promise, Pick> {
let window = web_sys::window().ok_or_else(|| Pick::Failed("no window".into()))?;
let f: Function = Reflect::get(&window, &function.into())
.map_err(failed)?
.dyn_into()
.map_err(|_| Pick::Failed("this browser can't open file dialogs".into()))?;
f.call1(&window, options).map_err(failed)?.dyn_into().map_err(|_| Pick::Failed("no answer from the file dialog".into()))
}
/// Remember a picked file: in memory now, in IndexedDB for the next session.
fn register(handle: &FileSystemFileHandle) -> String {
let id = make_id(js_sys::Date::now() as u64, (js_sys::Math::random() * 1e15) as u64);
HANDLES.with(|h| h.borrow_mut().insert(id.clone(), handle.clone()));
let (key, kept, name) = (id.clone(), handle.clone(), handle.name());
spawn_local(async move {
if let Err(e) = remember(&key, &kept).await {
log::warn!("the file's handle isn't kept for the next session: {e}");
}
});
path(&id, &name)
}
fn save_options(name: &str) -> Object {
let options = Object::new();
let _ = Reflect::set(&options, &"suggestedName".into(), &name.into());
let (description, mime, extension) = save_type(name);
let _ = Reflect::set(&options, &"types".into(), &types(description, mime, extension));
options
}
/// "Save to computer…" for a design kept in the browser: write a copy of `bytes` to a file the
/// user picks (call in the click), or download it where the browser can't pick one. The design
/// itself stays where it is.
pub fn save_copy(files: &Rc<Files>, name: &str, bytes: Vec<u8>) {
if !supported() {
if let Err(e) = crate::files::download(name, &bytes) {
files.say(format!("{name}: {e}"), true);
}
return;
}
let promise = start_picker("showSaveFilePicker", &save_options(name));
let files = files.clone();
spawn_local(async move {
let r = async {
let h: FileSystemFileHandle =
JsFuture::from(promise?).await.map_err(failed)?.dyn_into().map_err(|_| Pick::Failed("not a file".into()))?;
write_handle(&h, &bytes).await.map_err(Pick::Failed)?;
Ok::<String, Pick>(h.name())
};
match r.await {
Ok(saved) => files.say(format!("Saved a copy to {saved}"), false),
Err(Pick::Cancelled) => {}
Err(Pick::Failed(e)) => files.say(format!("save to computer: {e}"), true),
}
});
}
/// Save As, step one: show the browser's save dialog (call in the click). The answer is the
/// engine path the design is saved to, `None` when the dialog was closed.
pub fn pick_save(files: &Rc<Files>, name: &str) -> impl std::future::Future<Output = Option<String>> + use<> {
let promise = start_picker("showSaveFilePicker", &save_options(name));
let files = files.clone();
async move {
let handle = match promise {
Ok(p) => JsFuture::from(p)
.await
.map_err(failed)
.and_then(|h| h.dyn_into::<FileSystemFileHandle>().map_err(|_| Pick::Failed("not a file".into()))),
Err(e) => Err(e),
};
match handle {
Ok(h) => Some(register(&h)),
Err(Pick::Cancelled) => None,
Err(Pick::Failed(e)) => {
files.say(format!("save: {e}"), true);
None
}
}
}
}
/// Open, step one: show the browser's open dialog for a design (call in the click). The
/// answer is the engine path of the file, read into the engine's files; a design keeps its
/// handle, so Save writes back to the file. `None`: closed, or it could not be read.
pub fn pick_open(files: &Rc<Files>) -> impl std::future::Future<Output = Option<String>> + use<> {
let promise = start_picker("showOpenFilePicker", &open_options());
let files = files.clone();
async move {
let r = async {
let p = promise?;
let handles: Array = JsFuture::from(p).await.map_err(failed)?.dyn_into().map_err(|_| Pick::Failed("no file".into()))?;
let h: FileSystemFileHandle = handles.get(0).dyn_into().map_err(|_| Pick::Cancelled)?;
let bytes = read_handle(&h).await.map_err(Pick::Failed)?;
Ok((h, bytes))
};
match r.await {
Ok((h, bytes)) => Some(arrive(&h, bytes)),
Err(Pick::Cancelled) => None,
Err(Pick::Failed(e)) => {
files.say(format!("open: {e}"), true);
None
}
}
}
}
/// The options of the open dialog: a single file, designs and CAD files listed first.
pub fn open_options() -> Object {
let options = Object::new();
let _ = Reflect::set(&options, &"multiple".into(), &JsValue::FALSE);
let extensions = Array::new();
for e in [".solidcraft", ".solvecraft", ".json", ".step", ".stp", ".3mf", ".stl", ".obj", ".glb", ".gltf"] {
extensions.push(&e.into());
}
let accept = Object::new();
let _ = Reflect::set(&accept, &"application/octet-stream".into(), &extensions);
let entry = Object::new();
let _ = Reflect::set(&entry, &"description".into(), &"Designs and CAD files".into());
let _ = Reflect::set(&entry, &"accept".into(), &accept);
let _ = Reflect::set(&options, &"types".into(), &Array::of1(&entry));
options
}
/// A file read from a dialog's handle: a design lives at its disk path from now on (and
/// keeps the handle); anything else is an upload to open or insert.
pub fn arrive(handle: &FileSystemFileHandle, bytes: Vec<u8>) -> String {
let name = handle.name();
let p = if solidcraft_engine::io::is_design_path(&name) { register(handle) } else { format!("{}{name}", crate::files::UPLOAD) };
vfs::insert(&p, bytes);
p
}
/// Does an Open for `extensions` use the browser's open dialog?
pub fn handles_open(extensions: &[&str]) -> bool {
supported() && opens_design(extensions)
}
// ---- writing ------------------------------------------------------------------------
/// Queue what a command wrote to a disk path. Writes go one at a time; a newer write of the
/// same file replaces one still waiting.
pub fn enqueue(files: &Rc<Files>, path: &str, bytes: Vec<u8>) {
QUEUE.with(|q| {
let mut q = q.borrow_mut();
q.retain(|(p, _)| p != path);
q.push_back((path.to_string(), bytes));
});
if BUSY.with(|b| b.replace(true)) {
return;
}
let files = files.clone();
spawn_local(async move {
while let Some((path, bytes)) = QUEUE.with(|q| q.borrow_mut().pop_front()) {
if let Err(e) = write(&path, &bytes).await {
files.failed_disk(&path, &e);
}
}
BUSY.with(|b| b.set(false));
});
}
async fn write(path: &str, bytes: &[u8]) -> Result<(), String> {
let (id, name) = parse(path).ok_or("not a file on the computer")?;
let handle = handle_for(id).await.map_err(|e| format!("{name}: {e}"))?;
write_handle(&handle, bytes).await.map_err(|e| format!("{name}: {e}"))
}
async fn write_handle(handle: &FileSystemFileHandle, bytes: &[u8]) -> Result<(), String> {
ensure_readwrite(handle).await?;
let w: web_sys::FileSystemWritableFileStream =
JsFuture::from(handle.create_writable()).await.map_err(err)?.dyn_into().map_err(|_| "no writable stream".to_string())?;
JsFuture::from(w.write_with_u8_array(bytes).map_err(err)?).await.map_err(err)?;
JsFuture::from(w.close()).await.map_err(err)?;
Ok(())
}
async fn handle_for(id: &str) -> Result<FileSystemFileHandle, String> {
if let Some(h) = HANDLES.with(|h| h.borrow().get(id).cloned()) {
return Ok(h);
}
let h: FileSystemFileHandle = recall(id)
.await?
.and_then(|v| v.dyn_into().ok())
.ok_or("the browser no longer remembers where this design was saved: use Save As to choose the file again")?;
HANDLES.with(|m| m.borrow_mut().insert(id.to_string(), h.clone()));
Ok(h)
}
/// Ask for write access if the browser doesn't hold it any more (after a reload): one prompt,
/// from the click that asked for the save.
async fn ensure_readwrite(handle: &FileSystemFileHandle) -> Result<(), String> {
let options = Object::new();
let _ = Reflect::set(&options, &"mode".into(), &"readwrite".into());
for method in ["queryPermission", "requestPermission"] {
let Some(f) = Reflect::get(handle, &method.into()).ok().and_then(|f| f.dyn_into::<Function>().ok()) else {
// A browser without the permission calls hands out writable handles as they are.
return Ok(());
};
let answer = f.call1(handle, &options).map_err(err)?;
let state = JsFuture::from(Promise::resolve(&answer)).await.map_err(err)?;
if state.as_string().as_deref() == Some("granted") {
return Ok(());
}
}
Err("the browser wasn't given permission to write the file: use Save As to choose it again".into())
}
// ---- IndexedDB: the handles, by id --------------------------------------------------
/// The result of an IndexedDB request.
async fn result(req: &IdbRequest) -> Result<JsValue, String> {
let promise = Promise::new(&mut |resolve, reject| {
let r = req.clone();
let ok = Closure::once_into_js(move || {
let _ = resolve.call1(&JsValue::NULL, &r.result().unwrap_or(JsValue::UNDEFINED));
});
let bad = Closure::once_into_js(move || {
let _ = reject.call1(&JsValue::NULL, &"IndexedDB failed".into());
});
req.set_onsuccess(Some(ok.unchecked_ref()));
req.set_onerror(Some(bad.unchecked_ref()));
});
JsFuture::from(promise).await.map_err(err)
}
async fn open_db() -> Result<IdbDatabase, String> {
let factory = web_sys::window().ok_or("no window")?.indexed_db().map_err(err)?.ok_or("this browser has no IndexedDB")?;
let request: IdbOpenDbRequest = factory.open_with_u32(DB, 1).map_err(err)?;
let target = request.clone();
let upgrade = Closure::once_into_js(move || {
if let Ok(db) = target.result().and_then(|r| r.dyn_into::<IdbDatabase>()) {
let _ = db.create_object_store(STORE);
}
});
request.set_onupgradeneeded(Some(upgrade.unchecked_ref()));
result(&request).await?.dyn_into().map_err(|_| "no database".to_string())
}
fn store(db: &IdbDatabase, mode: IdbTransactionMode) -> Result<IdbObjectStore, String> {
db.transaction_with_str_and_mode(STORE, mode).map_err(err)?.object_store(STORE).map_err(err)
}
/// Key of the time a handle was last used (the handle's own key is its id).
fn stamp_key(id: &str) -> String {
format!("at:{id}")
}
async fn remember(id: &str, handle: &FileSystemFileHandle) -> Result<(), String> {
let db = open_db().await?;
let store = store(&db, IdbTransactionMode::Readwrite)?;
result(&store.put_with_key(handle, &id.into()).map_err(err)?).await?;
result(&store.put_with_key(&js_sys::Date::now().into(), &stamp_key(id).into()).map_err(err)?).await.map(|_| ())
}
/// Note that the file was used now.
async fn touch(id: &str) {
let Ok(db) = open_db().await else { return };
if let Ok(store) = store(&db, IdbTransactionMode::Readwrite)
&& let Ok(req) = store.put_with_key(&js_sys::Date::now().into(), &stamp_key(id).into())
{
let _ = result(&req).await;
}
}
/// The ids of every remembered file.
pub async fn known_ids() -> Result<Vec<String>, String> {
let db = open_db().await?;
let req = store(&db, IdbTransactionMode::Readonly)?.get_all_keys().map_err(err)?;
let keys: Array = result(&req).await?.dyn_into().map_err(|_| "no keys".to_string())?;
Ok(keys.iter().filter_map(|k| k.as_string()).filter(|k| !k.starts_with("at:")).collect())
}
async fn forget(ids: &[String]) -> Result<(), String> {
let db = open_db().await?;
let store = store(&db, IdbTransactionMode::Readwrite)?;
for id in ids {
result(&store.delete(&id.as_str().into()).map_err(err)?).await?;
result(&store.delete(&stamp_key(id).into()).map_err(err)?).await?;
HANDLES.with(|h| h.borrow_mut().remove(id));
}
Ok(())
}
/// Forget the remembered files nothing refers to any more (`keep`: the ids the recent designs
/// and the autosave name), once they have gone unused for a long while.
pub fn prune(keep: std::collections::HashSet<String>) {
spawn_local(async move {
let r = async {
// A transaction ends when the page awaits anything but its own requests, so each
// step opens its own.
let known = known_ids().await?;
let db = open_db().await?;
let reading = store(&db, IdbTransactionMode::Readonly)?;
let mut ids = Vec::new();
for id in known {
let at = result(&reading.get(&stamp_key(&id).into()).map_err(err)?).await?.as_f64();
ids.push((id, at));
}
let plan = plan_prune(&ids, &keep, js_sys::Date::now(), HANDLE_MAX_AGE_MS);
for id in &plan.stamp {
touch(id).await;
}
forget(&plan.delete).await
};
if let Err(e) = r.await {
log::warn!("remembered files weren't tidied: {e}");
}
});
}
/// 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) {
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 {
let r = async {
let handle = handle_for(&id).await?;
ensure_readwrite(&handle).await?;
read_handle(&handle).await
};
match r.await {
Ok(bytes) => {
vfs::insert(&path, bytes);
files.opened.borrow_mut().push(path);
files.wake();
touch(&id).await;
}
Err(e) => {
let gone = e.contains("NotFoundError") || e.contains("could not be found") || e.contains("no longer remembers");
if gone {
let _ = forget(std::slice::from_ref(&id)).await;
files.disk_known.borrow_mut().remove(&id);
files.gone.borrow_mut().push(path);
files.say(format!("{name} isn't where it was saved any more"), true);
} else {
files.say(format!("{name}: {e}"), true);
}
}
}
});
}
async fn recall(id: &str) -> Result<Option<JsValue>, String> {
let db = open_db().await?;
let req = store(&db, IdbTransactionMode::Readonly)?.get(&id.into()).map_err(err)?;
Ok(Some(result(&req).await?).filter(|v| !v.is_undefined()))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_disk_path_round_trips_its_handle_id_and_name() {
let p = path("18f-2a", "Bracket v2.solidcraft");
assert_eq!(p, "/disk/18f-2a/Bracket v2.solidcraft");
assert_eq!(parse(&p), Some(("18f-2a", "Bracket v2.solidcraft")));
}
#[test]
fn only_disk_paths_parse() {
for not in [
"/opfs/a.solidcraft",
"/downloads/a.step",
"/upload/a.step",
"/disk/",
"/disk/id",
"/disk/id/",
"/disk//a",
"/disk/id/x/y",
"a.solidcraft",
] {
assert_eq!(parse(not), None, "{not}");
}
}
/// Designs already stored in OPFS (and the recovery files) keep saving to OPFS under their own
/// names, whether or not the browser can write to the disk.
#[test]
fn writes_to_opfs_paths_still_go_to_opfs() {
for p in
["/opfs/Bracket.solidcraft", "/opfs/Bracket (2).solidcraft", "/opfs/recovery.solidcraft", "/opfs/recovery.json", "/opfs/old.solvecraft"]
{
assert_eq!(route(p), WriteTo::Opfs, "{p}");
}
// A design opened from OPFS saves to its own path; only a Save As can pick the disk.
assert_eq!(save_target(false, true), SaveTarget::Browser);
assert_eq!(route(&format!("{OPFS_PREFIX}Bracket.solidcraft")), WriteTo::Opfs);
assert_eq!(route("/disk/18f-2a/Bracket.solidcraft"), WriteTo::Disk);
assert_eq!(route("/downloads/part.step"), WriteTo::Download);
assert_eq!(route("/upload/part.step"), WriteTo::Memory);
}
#[test]
fn only_unreferenced_handles_unused_for_long_are_forgotten() {
let keep: std::collections::HashSet<String> = ["recent".to_string()].into();
let day = 24.0 * 3600.0 * 1000.0;
let now = 1000.0 * day;
let ids = vec![
("recent".to_string(), Some(now - 200.0 * day)),
("old".to_string(), Some(now - 100.0 * day)),
("fresh".to_string(), Some(now - 5.0 * day)),
("unstamped".to_string(), None),
];
let plan = plan_prune(&ids, &keep, now, HANDLE_MAX_AGE_MS);
assert_eq!(plan, PrunePlan { delete: vec!["old".to_string()], stamp: vec!["unstamped".to_string()] });
assert_eq!(plan_prune(&[], &keep, now, HANDLE_MAX_AGE_MS), PrunePlan::default());
}
#[test]
fn handle_ids_differ_by_time_and_by_chance() {
assert_ne!(make_id(1, 5), make_id(2, 5));
assert_ne!(make_id(1, 5), make_id(1, 6));
assert!(!make_id(0, 0).contains('/'));
}
/// Save As: a design goes to the disk where the browser can pick files and to the browser's
/// storage where it cannot (today's behaviour); an export is a download either way.
#[test]
fn save_as_chooses_between_the_disk_the_browsers_storage_and_a_download() {
assert_eq!(save_target(true, true), SaveTarget::Disk);
assert_eq!(save_target(false, true), SaveTarget::Browser);
assert_eq!(save_target(true, false), SaveTarget::Download);
assert_eq!(save_target(false, false), SaveTarget::Download);
}
#[test]
fn only_design_opens_use_the_disk_dialog() {
assert!(opens_design(&[]));
assert!(opens_design(&["solidcraft"]));
assert!(opens_design(&["solvecraft"]));
assert!(!opens_design(&["step", "stp"]));
assert!(!opens_design(&["json"]));
}
#[test]
fn the_save_dialog_offers_the_type_of_the_name() {
assert_eq!(save_type("Plate.solidcraft").2, ".solidcraft");
assert_eq!(save_type("Plate.JSON").2, ".json");
assert_eq!(save_type("Plate").2, ".solidcraft");
}
#[test]
fn closing_a_dialog_is_not_an_error() {
assert!(is_abort("AbortError"));
assert!(!is_abort("NotAllowedError"));
assert!(!is_abort("SecurityError"));
}
}
+68 -27
View File
@@ -2,22 +2,26 @@
//! the engine's in-memory files (`solidcraft_engine::io::vfs`) at start, written back whenever a
//! command writes one. Files come in from the computer through a file picker (the File System
//! Access API where the browser has it, an `<input type=file>` otherwise) or a drop on the page,
//! and go out as downloads (exports, and "Download design"). An autosave of unsaved work is kept
//! in OPFS too and offered again after a reload or a crash.
//! 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.
use std::cell::{Cell, RefCell};
use std::rc::Rc;
use js_sys::{Array, Function, Object, Reflect, Uint8Array};
use solidcraft_engine::io::vfs;
use crate::disk;
use wasm_bindgen::{JsCast as _, JsValue, closure::Closure};
use wasm_bindgen_futures::{JsFuture, spawn_local};
use web_sys::{FileSystemDirectoryHandle, FileSystemFileHandle, FileSystemGetFileOptions, FileSystemWritableFileStream};
/// Where OPFS files appear to the engine.
pub const OPFS: &str = "/opfs/";
pub const OPFS: &str = disk::OPFS_PREFIX;
/// Paths written here are handed to the user as downloads.
pub const DOWNLOADS: &str = "/downloads/";
pub const DOWNLOADS: &str = disk::DOWNLOADS_PREFIX;
/// Files brought in from the computer.
pub const UPLOAD: &str = "/upload/";
/// The autosave of unsaved work (in OPFS: `recovery.solidcraft` and `recovery.json`).
@@ -51,6 +55,14 @@ pub struct Files {
pub entries: RefCell<Vec<Entry>>,
/// Files read from the computer: (name, bytes, what to do).
pub incoming: RefCell<Vec<(String, Vec<u8>, Intent)>>,
/// Designs opened from the computer's disk: their paths in the engine's files.
pub opened: RefCell<Vec<String>>,
/// The ids of the files remembered from earlier sessions (see `disk::restore`).
pub disk_known: RefCell<std::collections::HashSet<String>>,
/// Recent designs whose file is gone: to drop from the recent list.
pub gone: RefCell<Vec<String>>,
/// Disk designs whose last write failed: they are not saved.
pub unsaved: RefCell<Vec<String>>,
/// Messages for the status line (errors and confirmations).
pub messages: RefCell<Vec<(String, bool)>>,
/// The stored designs are loaded (the app may open them).
@@ -64,11 +76,17 @@ pub struct Files {
}
impl Files {
fn say(&self, text: impl Into<String>, error: bool) {
pub fn say(&self, text: impl Into<String>, error: bool) {
self.messages.borrow_mut().push((text.into(), error));
self.wake();
}
/// Writing the design at disk `path` failed: say so, and take its "saved" back.
pub fn failed_disk(&self, path: &str, error: &str) {
self.unsaved.borrow_mut().push(path.to_string());
self.say(format!("Not saved to your computer: {error}"), true);
}
/// A file arrived from the computer.
fn arrive(&self, name: String, bytes: Vec<u8>, intent: Intent) {
self.incoming.borrow_mut().push((name, bytes, intent));
@@ -76,7 +94,7 @@ impl Files {
}
/// Run a frame (the app sleeps until something happens).
fn wake(&self) {
pub fn wake(&self) {
if let Some(c) = self.ctx.borrow().as_ref() {
c.request_repaint();
}
@@ -127,7 +145,7 @@ pub fn local_only() -> bool {
LOCAL.with(Cell::get)
}
fn err(e: JsValue) -> String {
pub(crate) fn err(e: JsValue) -> String {
e.as_string().or_else(|| Reflect::get(&e, &"message".into()).ok().and_then(|m| m.as_string())).unwrap_or_else(|| format!("{e:?}"))
}
@@ -150,7 +168,7 @@ async fn root() -> Result<FileSystemDirectoryHandle, String> {
dir.dyn_into::<FileSystemDirectoryHandle>().map_err(|_| "this browser has no private file storage (OPFS)".to_string())
}
async fn read_handle(h: &FileSystemFileHandle) -> Result<Vec<u8>, String> {
pub(crate) async fn read_handle(h: &FileSystemFileHandle) -> Result<Vec<u8>, String> {
let file: web_sys::File = JsFuture::from(h.get_file()).await.map_err(err)?.dyn_into().map_err(|_| "not a file".to_string())?;
blob_bytes(&file).await
}
@@ -253,6 +271,10 @@ pub fn start(files: &Rc<Files>) {
}
let files = files.clone();
spawn_local(async move {
match disk::known_ids().await {
Ok(ids) => files.disk_known.borrow_mut().extend(ids),
Err(e) => log::info!("no remembered files: {e}"),
}
match read_all().await {
Ok(all) => {
if local_only() {
@@ -295,26 +317,32 @@ pub fn start(files: &Rc<Files>) {
/// Store what commands wrote: OPFS paths into OPFS, download paths as downloads.
pub fn flush(files: &Rc<Files>) {
for (path, bytes) in vfs::take_writes() {
if let Some(name) = path.strip_prefix(OPFS) {
let name = name.to_string();
{
let mut e = files.entries.borrow_mut();
e.retain(|x| x.name != name);
if name != RECOVERY && name != RECOVERY_META {
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());
match disk::route(&path) {
disk::WriteTo::Opfs => {
let name = path.strip_prefix(OPFS).unwrap_or(&path).to_string();
{
let mut e = files.entries.borrow_mut();
e.retain(|x| x.name != name);
if name != RECOVERY && name != RECOVERY_META {
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());
}
}
let files = files.clone();
spawn_local(async move {
if let Err(e) = write_file(&name, &bytes).await {
files.say(format!("{name}: {e}"), true);
}
});
}
let files = files.clone();
spawn_local(async move {
if let Err(e) = write_file(&name, &bytes).await {
disk::WriteTo::Disk => disk::enqueue(files, &path, bytes),
disk::WriteTo::Download => {
let name = path.strip_prefix(DOWNLOADS).unwrap_or(&path);
if let Err(e) = download(name, &bytes) {
files.say(format!("{name}: {e}"), true);
}
});
} else if let Some(name) = path.strip_prefix(DOWNLOADS)
&& let Err(e) = download(name, &bytes)
{
files.say(format!("{name}: {e}"), true);
}
disk::WriteTo::Memory => {}
}
}
}
@@ -427,8 +455,15 @@ pub fn pick_from_disk(files: &Rc<Files>, intent: Intent) {
let picker = Reflect::get(&window, &"showOpenFilePicker".into()).ok().and_then(|f| f.dyn_into::<Function>().ok());
match picker {
Some(f) => {
let opts = Object::new();
let _ = Reflect::set(&opts, &"multiple".into(), &JsValue::FALSE);
// A design from the computer's disk keeps its file, so Save writes back to it.
let on_disk = intent == Intent::Open && disk::supported();
let opts = if on_disk {
disk::open_options()
} else {
let opts = Object::new();
let _ = Reflect::set(&opts, &"multiple".into(), &JsValue::FALSE);
opts
};
let promise = f.call1(&window, &opts);
let files = files.clone();
spawn_local(async move {
@@ -438,7 +473,13 @@ pub fn pick_from_disk(files: &Rc<Files>, intent: Intent) {
for h in handles.iter() {
let h: FileSystemFileHandle = h.dyn_into().map_err(|_| "not a file".to_string())?;
let bytes = read_handle(&h).await?;
files.arrive(h.name(), bytes, intent);
if on_disk && solidcraft_engine::io::is_design_path(&h.name()) {
let path = disk::arrive(&h, bytes);
files.opened.borrow_mut().push(path);
files.wake();
} else {
files.arrive(h.name(), bytes, intent);
}
}
Ok::<(), String>(())
};
+69 -7
View File
@@ -8,6 +8,8 @@
#[cfg(target_arch = "wasm32")]
mod api;
#[cfg(any(target_arch = "wasm32", test))]
mod disk;
#[cfg(target_arch = "wasm32")]
mod files;
#[cfg(target_arch = "wasm32")]
@@ -25,6 +27,7 @@ mod web {
use wasm_bindgen::JsCast as _;
use crate::api::Api;
use crate::disk::{self, SaveTarget};
use crate::files::{self, Files, Intent};
use crate::space_mouse::SpaceMouse;
@@ -121,6 +124,23 @@ mod web {
return;
}
files::flush(&self.files);
// A write to the computer's disk that failed: the design is not saved after all.
for path in self.files.unsaved.borrow_mut().drain(..) {
if self.app.session.path.as_deref() == Some(path.as_str()) {
self.app.session.mark_unsaved();
}
}
// Designs opened from the computer's disk (the files window): open them.
let opened: Vec<_> = self.files.opened.borrow_mut().drain(..).collect();
for path in opened {
self.app.open_path(&path);
self.files.panel.set(false);
}
// Recent designs whose file is gone are dropped from the list.
for path in self.files.gone.borrow_mut().drain(..) {
self.app.home.recent.retain(|r| *r != path);
self.app.home.opened_at.remove(&path);
}
for (text, error) in self.files.messages.borrow_mut().drain(..) {
self.app.set_status(text, error);
}
@@ -188,7 +208,9 @@ mod web {
let mut entries = self.files.entries.borrow().clone();
entries.sort_by(|a, b| b.modified.total_cmp(&a.modified));
let recent = &mut self.app.home.recent;
recent.retain(|p| vfs::exists(p));
let known = self.files.disk_known.borrow().clone();
// A design on the computer's disk stays listed while its file is remembered.
recent.retain(|p| vfs::exists(p) || disk::parse(p).is_some_and(|(id, _)| known.contains(id)));
for e in entries.iter().filter(|e| solidcraft_engine::io::is_design_path(&e.name)) {
let p = format!("{}{}", files::OPFS, e.name);
if !recent.contains(&p) {
@@ -196,6 +218,10 @@ mod web {
}
}
recent.truncate(solidcraft_ui_egui::home::MAX_RECENT);
// Remembered files that no recent design or autosave names are forgotten in time.
let recovered = self.files.recovery.borrow().as_ref().and_then(|(_, p)| p.clone());
let keep = recent.iter().chain(recovered.as_ref()).filter_map(|p| disk::parse(p)).map(|(id, _)| id.to_string()).collect();
disk::prune(keep);
}
/// The stored designs: open, insert, rename, delete, download; open from the computer.
@@ -224,6 +250,9 @@ mod web {
act = Some(("download_open", String::new()));
}
});
if !disk::supported() {
ui.weak("This browser can't save to a folder: designs stay in this browser. Download design saves a file.");
}
ui.separator();
if !self.files.ready.get() {
ui.horizontal(|ui| {
@@ -270,6 +299,13 @@ mod web {
self.renaming = Some((e.name.clone(), e.name.clone()));
self.rename_focus = true;
}
if ui
.small_button("Save to computer…")
.on_hover_text("A copy in a file you choose; the stored design stays here")
.clicked()
{
act = Some(("save_copy", e.name.clone()));
}
if ui.small_button("Download").clicked() {
act = Some(("download", e.name.clone()));
}
@@ -305,6 +341,10 @@ mod web {
}
Err(e) => self.app.set_status(e.to_string(), true),
},
"save_copy" => match vfs::read(&path, solidcraft_engine::io::MAX_DESIGN_BYTES) {
Ok(b) => disk::save_copy(&self.files, &name, b),
Err(e) => self.app.set_status(e.to_string(), true),
},
"download_open" => {
let d = &self.app.session.doc;
let result = self
@@ -533,18 +573,40 @@ mod web {
solidcraft_ui_egui::fonts::setup_fonts(&cc.egui_ctx);
let files = Rc::new(Files::default());
files::start(&files);
let panel = files.clone();
let (panel, opener, saver, shown) = (files.clone(), files.clone(), files.clone(), files.clone());
let (restorable, restorer) = (files.clone(), files.clone());
let services = Services {
// Open shows the browser's files (and the computer's, from there).
pick_open: Some(Box::new(move |_: &[&str]| {
// 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); otherwise,
// and for inserting STEP or meshes, the browser's files window.
pick_open: Some(Box::new(move |exts: &[&str]| {
if disk::handles_open(exts) {
return Box::pin(disk::pick_open(&opener));
}
panel.panel.set(true);
Box::pin(std::future::ready(None))
})),
// Designs are kept in the browser; everything else is downloaded.
pick_save: Some(Box::new(|name: &str, exts: &[&str]| {
// A design is saved to a file the user picks, or kept in the browser
// where the browser can't pick one; everything else is downloaded.
pick_save: Some(Box::new(move |name: &str, exts: &[&str]| {
let design = exts.contains(&solidcraft_engine::io::DESIGN_EXT);
Box::pin(std::future::ready(Some(format!("{}{name}", if design { files::OPFS } else { files::DOWNLOADS }))))
match disk::save_target(disk::supported(), design) {
SaveTarget::Disk => Box::pin(disk::pick_save(&saver, name)),
SaveTarget::Browser => {
saver.say("This browser can't save to a folder: the design is kept in this browser. File > Download design saves a file.", false);
Box::pin(std::future::ready(Some(format!("{}{name}", files::OPFS))))
}
SaveTarget::Download => Box::pin(std::future::ready(Some(format!("{}{name}", files::DOWNLOADS)))),
}
})),
// A recent design on the disk, from an earlier session, opens by asking
// the browser for its remembered file.
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))),
download_design: Some(Box::new(files::download)),
browser_files: Some(Box::new(move || shown.panel.set(true))),
install_app: Some(crate::install::service(&cc.egui_ctx)),
..Default::default()
};
+4
View File
@@ -210,6 +210,10 @@ fn services(cc: &eframe::CreationContext<'_>) -> Services {
http_download: Some(std::sync::Arc::new(http_download)),
// Already an installed app: only the browser build offers to install.
install_app: None,
can_restore: None,
restore_file: None,
download_design: None,
browser_files: None,
agent_http: Some(std::sync::Arc::new(agent_http)),
secrets: Some(std::sync::Arc::new(Keychain)),
show_in_folder: Some(Box::new(|path| {
Binary file not shown.
+93
View File
@@ -0,0 +1,93 @@
Copyright © 2017 IBM Corp. with Reserved Font Name "Plex"
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.
+93
View File
@@ -0,0 +1,93 @@
Copyright © 2017 IBM Corp. with Reserved Font Name "Plex"
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.
+6
View File
@@ -29,3 +29,9 @@ request files under target, and do not retry them. The server never invokes a mo
Canonical paths, launch command and export schema: [README.md](README.md).
There are no numbered bulk authoring scripts or generated inventory files to maintain.
Runtime discovery is also checked by `crates/ui-egui/tests/icon_audit.rs`. New menu
surfaces must extend that Harness traversal. Regenerate the committed
`design/requested_icons.txt` with `UPDATE_ICON_MANIFEST=1 cargo test -p
solidcraft-ui-egui --test icon_audit`; review the sorted id/placeholder diff. The
review tool reads this fixture in addition to source scans, masters and aliases.
+25 -4
View File
@@ -18,8 +18,8 @@ artwork changes trigger the live revision check. The server binds only to loopba
Every opening scans SVG masters, aliases and source references. New, unused,
missing and changed icons are discoverable. **All discovered icons without a valid
acceptance are Open, including those with notes.** No inventory file or allowlist
is needed. Rescan sees additions immediately; a running page also polls revisions.
acceptance are Open, including those with notes.** Discovery also reads `design/requested_icons.txt`, the sorted runtime audit
manifest; it supplements source scans and never restricts discovery. Rescan sees additions immediately; a running page also polls revisions.
The internal placeholder glyph is the only excluded card. Shared masters are shown
separately even when an accepted alias already uses them; reopen that alias before
requesting a redraw of its protected artwork. Unassigned commands
@@ -82,6 +82,7 @@ Palette changes preserve accepted artwork and never update review hashes.
| `design/palette.toml`, `aliases.toml` | Shared theme roles, weights and semantic aliases |
| `design/review.json` | Owner acceptance hashes and current notes |
| `design/references.toml` | Curated style references; does not control discovery |
| `design/requested_icons.txt` | Sorted runtime requests and placeholder flags, checked by the Harness audit |
| `design/tools/author/{lib,bases}.py` | Reusable geometry and canonical cube/slab/arrow bases |
| `design/tools/author/new_icon.py` | Safe new-master scaffold, never overwrites an id |
| `design/tools/icon_status.py`, `review_state.py` | Discovery, review backend and state |
@@ -104,5 +105,25 @@ python3 -B assets/icons/ui/design/tools/icon_status.py --csv
```
Reports, screenshots, requests, generated contact sheets and previews stay under
ignored `target/`. Do not commit generated inventories or historical authoring
batches. Every non-Markdown resource has an exact row in `ATTRIBUTION.md`.
ignored `target/`. The runtime request manifest is a committed regression fixture; other generated
inventories and historical authoring batches do not belong in the repository. Every non-Markdown resource has an exact row in `ATTRIBUTION.md`.
## Runtime menu audit
`cargo test -p solidcraft-ui-egui --test icon_audit` drives the real UI through the
headless scenario Harness and rejects placeholder requests and unexpected blank
context-menu icons. It compares sorted requested ids with
`design/requested_icons.txt` (`id<TAB>placeholder`, one per line). After deliberately
changing menu coverage or assignments, regenerate and review its diff:
```sh
UPDATE_ICON_MANIFEST=1 cargo test -p solidcraft-ui-egui --test icon_audit
```
The review page includes every manifest id, even ids used only in tables or helper
arguments. A true placeholder flag, missing master or broken alias is reported as
missing; runtime collection never changes acceptance hashes. The live control
method `ui.icon_audit` can diagnose requests in a running UI (see
[control protocol](../../../docs/control-protocol.md)). Keep the Harness traversal
current when adding menus. A successful traversal covers only the contexts it
actually opens; document unreachable or untested contexts in the audit report.
+6
View File
@@ -207,3 +207,9 @@ All artwork must be original SolidCraft work or an explicitly licensed resource
with provenance. Commercial CAD artwork, screenshots and extracted assets are
forbidden even as hidden references. This guide and the local finished set provide
all required visual context for future agents.
Runtime discovery is also checked by `crates/ui-egui/tests/icon_audit.rs`. New menu
surfaces must extend that Harness traversal. Regenerate the committed
`design/requested_icons.txt` with `UPDATE_ICON_MANIFEST=1 cargo test -p
solidcraft-ui-egui --test icon_audit`; review the sorted id/placeholder diff. The
review tool reads this fixture in addition to source scans, masters and aliases.
+8
View File
@@ -35,4 +35,12 @@ sketch_scale = { master = "scale", accent = "sketch" }
sketch_text = { master = "text", accent = "sketch" }
surface_fair = { master = "spline_cv", accent = "surface" }
mesh_export = { master = "export", accent = "mesh" }
# Legacy feature-history id for the same Split Body operation.
split = "split_body"
# Sketch dialog choices share the corresponding finished tool artwork.
sketch_chamfer_equal = { master = "sketch_chamfer", accent = "sketch" }
sketch_chamfer_two = { master = "sketch_chamfer_two_distances", accent = "sketch" }
offset_one_side = { master = "offset", accent = "sketch" }
bom = "list"
+26 -22
View File
@@ -10,7 +10,7 @@
[roles.ink] # outlines and details drawn over a face
source = "#34465b"
light = "#34465b"
dark = "#d8dfeb"
dark = "#1f2021"
[roles.line] # line art that sits directly on the UI background (SVG: currentColor)
source = "#34465b"
light = "#34465b"
@@ -66,13 +66,13 @@ parameters = 4.2
[accents.surface.active-1]
light = "#f1d2af"
dark = "#ba8f6a"
dark = "#cc9362"
[accents.surface.active-2]
light = "#c06f00"
dark = "#b86f04"
dark = "#bd750d"
[accents.surface.active-3]
light = "#a45000"
dark = "#95682d"
dark = "#955a0e"
[accents.form.active-1]
light = "#decff8"
@@ -85,41 +85,41 @@ light = "#7349cd"
dark = "#7b63a6"
[accents.mesh.active-1]
light = "#e5d99b"
dark = "#a9985f"
light = "#ffec8e"
dark = "#ffe282"
[accents.mesh.active-2]
light = "#a28200"
dark = "#9a8100"
light = "#bb9600"
dark = "#ab8f00"
[accents.mesh.active-3]
light = "#826700"
dark = "#857100"
light = "#e0b200"
dark = "#c6a800"
[accents.sketch.active-1]
light = "#bfe2cc"
dark = "#81a28e"
dark = "#7ab792"
[accents.sketch.active-2]
light = "#439a65"
dark = "#459665"
dark = "#3a965e"
[accents.sketch.active-3]
light = "#057f45"
dark = "#27864e"
dark = "#128d45"
[accents.constraint.active-1]
light = "#f0cfca"
dark = "#b98c88"
[accents.constraint.active-2]
light = "#c1685f"
dark = "#b66b61"
dark = "#c27267"
[accents.constraint.active-3]
light = "#af4038"
dark = "#a05c54"
light = "#ca473d"
dark = "#c16358"
[accents.construction.active-1]
light = "#ecd3b6"
dark = "#b19373"
dark = "#b99a78"
[accents.construction.active-2]
light = "#b8741f"
dark = "#b4711c"
dark = "#ad7023"
[accents.construction.active-3]
light = "#9c5600"
dark = "#a75d00"
@@ -134,12 +134,16 @@ dark = "#1d7f89"
[variants.red-green.construction.active-3]
light = "#b97300"
dark = "#a75d00"
# The constraint red stays darker than the sketch green for a weak red sense.
[variants.red-green.constraint.active-3]
light = "#af4038"
dark = "#c16358"
[variants.blue-yellow.construction.active-3]
light = "#b97300"
dark = "#c37d00"
[variants.blue-yellow.constraint.active-3]
light = "#af4038"
dark = "#986255"
dark = "#85554a"
# Default accent by requested icon id. Explicit alias accents in aliases.toml win.
# Deliberately not inherited from a master: ground reuses c_fix but is not a constraint.
@@ -153,9 +157,9 @@ sketch = [
"arc3", "arc_center", "arc_slot", "arc_slot_center", "arc_tangent",
"blend", "circle", "circle2", "circle3", "circle_tt",
"circle_ttt", "conic", "constrainer", "dimension", "ellipse",
"extend", "finish", "fit_section", "include", "infinite",
"extend", "finish", "fit_section", "include",
"intersect", "intersection_curve", "iso_curve", "line", "line_mid",
"offset", "point", "polygon", "polygon_circumscribed", "polygon_edge",
"offset", "offset_two_sides", "offset_symmetric", "point", "polygon", "polygon_circumscribed", "polygon_edge",
"project", "project_surface", "rect", "rect3", "rect_center",
"rect_rounded", "sketch", "sketch_chamfer", "sketch_chamfer_angle", "sketch_chamfer_two_distances",
"sketch_fillet", "sketch_mirror", "slot", "slot_center", "slot_overall",
@@ -168,5 +172,5 @@ constraint = [
"c_polygon", "c_smooth", "c_symmetry", "c_tangent",
]
construction = [
"centerline", "construction",
"centerline", "construction", "infinite",
]
+282
View File
@@ -0,0 +1,282 @@
accessibility false
activate false
align false
appearance false
arc3 false
arc_center false
arc_slot false
arc_slot_center false
arc_tangent false
as_built false
assistant false
auto_constrain false
axis false
axis_edge false
axis_normal_to_face false
axis_through_cylinder false
axis_two_planes false
axis_two_points false
bendtable false
blend false
body false
bom false
boss false
boundary_fill false
bounding_solid false
box false
break false
bug_report false
c_coincident false
c_collinear false
c_concentric false
c_equal false
c_fix false
c_hv false
c_midpoint false
c_parallel false
c_perpendicular false
c_polygon false
c_smooth false
c_symmetry false
c_tangent false
cam false
camera false
canvas false
capture false
center_of_mass false
centerline false
chain false
chamfer false
checkpoint false
chevron false
chevron_right false
circle false
circle2 false
circle3 false
circle_tt false
circle_ttt false
close false
coil false
combine false
compare false
component false
compute false
configuration_table false
conic false
constrainer false
construction false
contact_all false
contact_new false
contact_off false
contact_sets false
continuity false
convert_sheet false
copy false
curvature_comb false
curvature_map false
cut false
cylinder false
decal false
delete false
delete_face false
dimension false
draft false
draft_analysis false
drive false
dxf false
edit false
ellipse false
emboss false
environment_map false
error false
explode false
export false
export_tray false
extend false
extrude false
eye false
fastener false
fillet false
finish false
fit false
fit_section false
flange false
flat false
fold false
folder false
gear false
graph false
ground false
help false
hem false
hole false
home false
import false
import_tray false
include false
infinite false
inspect false
interference false
intersect false
intersection_curve false
iso_analysis false
iso_curve false
joint false
joint_origin false
library false
line false
line_mid false
lip false
list false
loft false
lookat false
material false
measure false
mesh_export false
min_radius false
mirror false
motion_link false
move false
new false
normal false
offset false
offset_face false
offset_one_side false
offset_symmetric false
offset_two_sides false
open false
orbit false
origin false
pan false
params false
paste false
pattern_center false
pattern_circ false
pattern_flip false
pattern_path false
pattern_rect false
perforate false
perspective false
pipe false
plane false
plane_along_path false
plane_angle false
plane_midplane false
plane_perpendicular false
plane_tangent false
plane_three_points false
plane_two_edges false
plastic_assign false
plastic_rule false
play false
plus false
point false
point_along_path false
point_center false
point_edge_plane false
point_three_planes false
point_two_edges false
point_vertex false
polygon false
polygon_circumscribed false
polygon_edge false
presspull false
project false
project_surface false
rack_pinion false
reach false
rect false
rect3 false
rect_center false
rect_rounded false
redo false
refold false
rename false
render_image false
render_scene false
reorder false
replace_face false
rest false
restore false
revert false
revolve false
rib false
rigid_group false
rollback false
save false
scale false
screw false
section false
select false
settings false
sheet_rules false
shell false
silhouette false
sketch false
sketch_break false
sketch_chamfer false
sketch_chamfer_angle false
sketch_chamfer_equal false
sketch_chamfer_two false
sketch_chamfer_two_distances false
sketch_circ_pattern false
sketch_dxf false
sketch_fillet false
sketch_mirror false
sketch_move false
sketch_rect_pattern false
sketch_scale false
sketch_text false
slot false
slot_center false
slot_overall false
snap false
snap_fit false
sphere false
spin false
spline false
spline_cv false
split false
split_body false
split_face false
spun false
star false
status_error false
style false
suppress false
surface_blend false
surface_boundary false
surface_extend false
surface_extrude false
surface_fair false
surface_fillet false
surface_loft false
surface_offset false
surface_patch false
surface_reverse_normal false
surface_revolve false
surface_ruled false
surface_stitch false
surface_sweep false
surface_thicken false
surface_trim false
surface_unstitch false
surface_untrim false
svg false
sweep false
text false
thread false
torus false
trace false
trim false
undo false
unfold false
update false
warning false
web false
ws_animate false
ws_design false
ws_drawing false
ws_render false
zebra false
zoom false
+54 -38
View File
@@ -4,6 +4,7 @@
"accessibility": "012aa26ece9ffa8bd6a850b7a19279c3675164b12438f5fbfe560e575c8fe2eb",
"activate": "9d28c47dcb0b733095eadc840a30fa898a19f9cf9eee203496386546666cb1f8",
"agent_chat": "4a9ae0e7e2c360b4858b3175674c93d096705c92318fecd481e849f3ae650bf4",
"agent_link": "2f20d89a0e3e9b28744910dddf7f282a1099ccd91aa160c9841c5b1d9ea9ed0f",
"align": "05615db7dd9e4be56ae898e59a68c548b8a8447794442a9a394940651204bc4f",
"appearance": "b3162edd92291a5ef402e8499cd52be05beb2fa7eda641163c3f6e98325de2bf",
"arc3": "fdca3d501fd557ac989376a12e9c4fa8231fde2b455c20724d1079a466153e9c",
@@ -12,10 +13,11 @@
"arc_slot_center": "ba93e773e06e948fcf3c6dab28df341c07d35c6a3afa33a840b261bb82bcf32b",
"arc_tangent": "d6910c8d42878cc34617b96e26c3018f4a6e4e793443ac81f2c294f88d70fa28",
"as_built": "0f481932deca5586a8a8ea3d707efca0747f5c59559d53830b694d3458531013",
"assistant": "8fac392b16b4f13c6a399c59962b97c1912282e0ec909976496959472f1d642d",
"auto_constrain": "012e5da4ff83449f6a3a92ee2d9924998cbeea545281bdf97873710b7436183d",
"axis": "66ebabe43a7b1b73b59a3a03b7bd92ab54d899947d0cc2d93ad6e6aac4d76501",
"axis": "acdab0eb7b99b1579d64ae190082df48b65174e7b8ad3104967bcc59f6154206",
"axis_edge": "5a65898bc38bdaaaf5b04f2c2f7556915b5b7b1fdc4de9708fcc30f64aa1ab18",
"axis_normal_to_face": "0824bb37e80928e9ac880af5ca5fdb3b52084af9e73db73a5a5a7bc8f1f899f7",
"axis_normal_to_face": "597b5b96ce787b70791133267108c5196aca09d5e3f94e7dc92199a63fe49ca6",
"axis_through_cylinder": "804eb75631f4ee71ac8db51209237bb19d82bafd326abdc81b124b875c0874b4",
"axis_two_planes": "80162aa79ca7baf6d60490414a4fb53d87e6f2b85e901c452875a5f3ed86d35a",
"axis_two_points": "5064f7544ce41ae7c1f05a90c958222bbcee03723df67d34cd3e209d25a1f71f",
@@ -28,36 +30,38 @@
"boundary_fill": "17ccb419a46c04a44ec864af968a32984dc81f6b3196a821bfff7f54141dfdd1",
"bounding_solid": "e2deb6fff292f0f295022583ba7ddb86896eb79d9dd7268e653713b9c62f0580",
"box": "72cd44c3c4fc5ae71242fca8c60af47371930201276df5b7db9d2f16a2739c31",
"break": "430582243311f304dbab397ce58c8c9aef9c78ed88d3e921a57e584ddb95d94c",
"break": "67a5fab332616e6d6a9aeae3bedc04ee0768179143ba34b795bdd98cea04bb24",
"bug_report": "8cf7afbe0d3c34988fd2748de148f56a7e939fd8273ba01a79a976f879c74831",
"c_coincident": "32fb535b3dd7631dc2e9ea284f445ad54ea0e5c8b0e1bb27a9984c849746917d",
"c_coincident": "a93cc58acc5090c24c973cb558920bff4e35c8f99071135f16a115256d41bf98",
"c_collinear": "3bcf1dc6b221a1fa9a4445ee8afc7d858dc28a5270d22d10fef86fa4997f1bdf",
"c_concentric": "90e9b35b6186ea4fcb3debc24808f302c0bf7354b7afae86e339574ef8e5103a",
"c_equal": "c0f50c02db7d15a6392d2178e28df5c100e67fab7732f8e8474b888d218d0673",
"c_fix": "b91c6af7ea9740efb8cf196409b11d16fad52c51b732a356e5f844bd4e2f08e0",
"c_fix": "23e2471a9d6872e83028e0e219e330cef1930a4d89456a0fefa1bda8a7e3eb81",
"c_hv": "b5c44d60a4b03b9942fbcf3c706f797ce4dbddf9a67eb1c98d6aaf3234e9bc18",
"c_midpoint": "fd4efc26da1336f811a9016b02d7675af2a7c95cd1ffa2f091507b22434f5435",
"c_midpoint": "aaea530b8a04aa80efc99ed2cf42347a91c65b160286bae3e2882f5a0bd09a51",
"c_parallel": "aaa80258502af091b596511662aca392546dd0d7bf69cec4db98b6e0ae7591f0",
"c_perpendicular": "692ba7a77cd77108c33c101386b60b7c957ce7d2202937d9696d18dcf3d4cf8e",
"c_polygon": "defac40b4c6ef323494f49acbb94b896502f78f6a99e750975693c602164a44b",
"c_smooth": "dccf5effdb8977ac04e6c437c280c83ed0f117d81b64c57fae03f5f7f7f2b9c0",
"c_perpendicular": "0ccf0383176ace2d4ab30c2efe853815ca1df20f2537020f369f496d8bf124b5",
"c_polygon": "02c76b288b42d6be8eb77f502af95bb840557f43fed3def2b1a381f6ac920375",
"c_smooth": "87aeb504aa330a191a8680d9eab3e86844f6d7be0a9caa10fdf0a657e76f45ee",
"c_symmetry": "8646a7deb7447514bcc455562d8639f8b4c482df8c971fd2870c05965ce71f78",
"c_tangent": "a011f5d43d5e09f68de332c60c5acee737a3ebc94d107f521871225c3841a064",
"c_tangent": "ef8b1e502c2a939958b5e8de9ef2716442e32b2a394555eb7b51101c415b966e",
"cam": "a35342e36ca4deae55e98c54df630b5c2da591c06d979eb2b8ce6a13f6db66d0",
"camera": "fcd867877ea4984318dbff36a166b48682f26bb9d44e70383dde63d9a7ab87c8",
"canvas": "512d7721f0b8b14a46f22b9588b437b0cd8c2c425330d31b3b156c0d1334bcef",
"capture": "23ef84e50b45c06532b3e842b72f6da5498d0e6b8391e1bff55e84afa8359f37",
"center_of_mass": "24401b1a19cb45ddb028de0acf3c9815b47abecbde9e133f473adac3d27ae346",
"centerline": "a1889b9092ef5a7aa9592df994b6541b53ea9afb03b2c56c7678a61df335f4d7",
"centerline": "850283497bad77e233f934e8effc0d0d7b01ccde83fa66f6abb539a33a8a2c80",
"chain": "9cdc78486b5c4b5b2a378e09bbd41733eb167513f9f2bc4b1aad629dd6c4c753",
"chamfer": "487c90ae65e87b74d7ddee7d9ce9f088b63e9b5b85c2a390364855a2649f17e8",
"checkpoint": "a3bc0700a40b791b5f3bf176d820bdabd2cf94a526908609d50136c8d0918d6a",
"chevron": "61887736af4b1e6bef857c4d2e3846657824e84f1c8026a56525ddba2d2a8083",
"chevron_right": "d28a52dcc51fd9eb988f8f83e89d74370a31100049c910a886732a48ec1342ca",
"circle": "145544ff1cd133971f537dd247092126ef8d91a88fe31b795222e76840d0c4d0",
"circle2": "f1b6e8e2157279cfe1e608f913b377e44a70f7555f5010298138de8a887bf586",
"circle3": "aaf6b914e630688675abf4dcbf7d43096317881ced0e22520a4ef229013e0de2",
"circle_tt": "94996320b671c8c48f239b8c48f1b2f1cd5cc33a7d8ea1daaa61aa9203803cae",
"circle_ttt": "ac3c211a6fe17c3625060f695ecd93937730e2bc2dd57921bcbbcaa984b9badf",
"close": "5833954dfba3d70a9a3a7519f2796a7213899548b0345bd8fe8ea9a321d82be2",
"coil": "4c549fd63506457165f53a7940cc0f3dcacdbe075e935ea299edbf4985104411",
"combine": "f9d4211f0153779369c9da68b266c2c9a857d11bc033ff4a96ede794e470e38e",
"compare": "c24b6c18f18413ae6f3344aaa6e04b42fae8b63e7cd1552a81398462eb68eb96",
@@ -66,7 +70,7 @@
"configuration_table": "1a142e76b9e4cd47412a307cc0ad9b8c589bb97c8930fc445dd5bb8d74ecf707",
"conic": "808445fd697ff76f9074e7bbf877146e634995c295dce045604991fa4cec26a0",
"constrainer": "774cb158897eb91767541b5b1a25a46f3a731cbea0661006ce5472634ab6b0e3",
"construction": "ad962cbcd0aaef9167744abfd0bfe1d7ef55e48b4ff42f2b4b05f66204b58acb",
"construction": "f13e3851cdf7b30a7118802a1c17e8852fcb9893fd1d65c7098a5c3fc35c8396",
"contact_all": "6ad4d245592815a3e38e70ae324ad3971ab614daba7ca3cf8fcb64cb8e2be077",
"contact_new": "603410785fff408cc12dc72bef2f09885b694c5ddb2571c7819ae1301fb89dc6",
"contact_off": "54dc3bd2267ebb084d8dc7efee85e9f21d8c59a57ad37b96d78095bc75e73fa0",
@@ -81,8 +85,10 @@
"decal": "acc258e40b29ffb6b6007057f0b70f487995f573704c7ef4e4f08f209d52bddd",
"delete": "d14203f06b8b1b87328e7f198c7aa41595e9a66b5d8a7fbd2a3ec2e35cd99afd",
"delete_face": "dcbf8ab59bd37bd5a0cdd7a528b29c2d19daa5f3fc130ef5cff0c20034f061a4",
"dimension": "8246dde8b0a140d1f533c1e45f2e8b5af35c05d3504dccf50bd7e7726a93389e",
"dots": "aa38423c547594462f49cc355f3c9f5b41e28a7515f466a6a8cffda487ba1715",
"dimension": "a926b7c64bc8ca99d8d6dcbb81b95235bd3fc34fbed049aca8b01b4d1147c15d",
"disclosure_closed": "49a350c6f2efae78de0be96c524ebee1fd1291ea449a84224b88d37164a4e807",
"disclosure_open": "d13ce3f97e3e362a9904fa2bf3412f3f00e4136bc23538b92d41483961d280de",
"dots": "600f7ff209d93145fc938bc4f6a1e34473e6b9ee22a5b4718f133bc7da5ee58d",
"draft": "9c0fc361cacd0ad95ccd54794f4825dfc9901a83a3de752a588faec365d0ee09",
"draft_analysis": "eb39c0f90e9cf226da6307365a76015e900dd692dc3f0fdc8f02fe717226261f",
"drive": "cf9e924acb2531efd5a1b90915d59f0cb532c844b754fd3a0e376ef6f706c47d",
@@ -97,7 +103,7 @@
"export": "c8c2abf007e43e896479d78798e3976ba9eda809507c820bb38538f77db696ac",
"extend": "28c63df352c77d02feffe44cf09fb0fc1d48f7905aaabbfa6dba601dbb70f8e8",
"extrude": "9cc1538971c5d25467aaed23f739899d9d102fd07f86472ac3197c54ca2e1144",
"eye": "5d90a25f5d07e6e36a2a8faf5e2ed1425b2a787834ab2291b1b1c521057ec1c2",
"eye": "2449167c8410826dc1106b687c12a309d5bea8f7817b897943e80f182a163c2d",
"fastener": "fcff4e4be50907f712e527b373771afafd92e50096fc7a4b8aba5d86448e1e42",
"fillet": "11ec232dc111597211e3a4fe61b69616b5d28b4b1b2732052ddcd486cdfc6129",
"filter": "4d456a56416089574bd3732fa44c0c1b27fd2478b369448808a27f0144d15d8a",
@@ -109,15 +115,15 @@
"fold": "90344fb5e5799378117fbd79ef28a84f27fba5723af5414c4ea30a2ac3a4f21e",
"folder": "c19f5230df45a1bda1e92e89de38bba21c91e6eca0517eb15847a7712012d1e5",
"gear": "25a1ca48a587850da1104a46005a330873ed196246e3d04e12cf61f26ad2b0d5",
"graph": "25646bdf4d1a534c51e1897a8ae641a21f871ed70b24ff25652e1fe957c9fa00",
"ground": "b91c6af7ea9740efb8cf196409b11d16fad52c51b732a356e5f844bd4e2f08e0",
"graph": "7156caedd2dbfa2ae53da18b7a2204b8db66c45126859d36caf049a3fd4335a4",
"ground": "23e2471a9d6872e83028e0e219e330cef1930a4d89456a0fefa1bda8a7e3eb81",
"help": "8ce4060577483dbdb510be147d2827de50ac20029354008fca55abc0c7edf05d",
"hem": "f9b1aafbedcf3dc90c26be308814638e0095fbe31b0647092507343fa60af011",
"hole": "f82d47751f826e08682db847a270897474661bfe9f35b6d3678f49a203172563",
"home": "ab5f1c41d377687e4c5a808dfdccf2757b33dd43f934d44df43d6e9118176853",
"import": "e343540eb49fdb1e53c4d54b9e60ae513b3103df6c9133048bed878e58067455",
"include": "f5b80ad3e06e3b863fcfbe415ec912e4e09653dd519f9ac207819339fe34730f",
"infinite": "bff57a1c56d9b47131d7c8ff85f008a53c612c63706052e7e19d7aaea93cf5b1",
"infinite": "53fb781131078d2f8e71be23aa610d9f9d9c8bb79ba5f97fa397475e9ff8c3bd",
"inspect": "d2b4644b2d5f1aa50cb576b9265e275fa91201e296c2bc79865f810067d538e9",
"interference": "b3d729d0e822e8d95485c8ef50649847572def10d4c713fff0e073b8031c5f5d",
"intersect": "9ab4f6551848a2be546f55502a77075bed2d3994f89d91b721eab92bcee61d61",
@@ -125,7 +131,7 @@
"iso_analysis": "dacd34ac254ec4fbb6932350f62d4d92626d922a2bfc62d2caecdeb17303e896",
"iso_curve": "015ff2e26346ccaf149f75cf6a0652d9201c1b23a14e6bb62625acfa599dc002",
"joint": "72b71a608e713f6cf51a1cb84edb011d4ccbd8bfe41d917fa2a1d13345dc0b2b",
"joint_origin": "5b0761122eb4295ea220314e8db1efd1251e3482653d6cdcc47252d0268864a8",
"joint_origin": "e12529af1fce8c5ffdf746e71ce2f5db5eb170e53778469cea8fd30344291574",
"library": "ac0b1798e4c94340f9c854daa3829371c1a9f3ffea7679d955e6cfa139cafdc8",
"line": "cd1177ea659548f03d43c7fa7dcc209a0542b1ba4bd0153c5113576cb7a96a81",
"line_mid": "edb8ebc463479ae9878e54b930c124153ca880270b7cf6c0e4edba6336ba2854",
@@ -139,17 +145,20 @@
"min_radius": "92bd79b7d557aba6ddfccc76a4c6d0cfb579061b3c57172f9171d28af675711c",
"mirror": "56637068a319dc2972c5552b25e4f43151aa9bd5055c2fef08204dd7cc0829cf",
"moon": "3ba5adb92bf0f49e92d46682dbf6905e555aa6eebc8e2b3c5288f949cda29322",
"motion_link": "19698bbe15b7c2d2601208bbcdc26627d1aa7724aee017b7da8874ad87fb9d1f",
"motion_link": "27832f827146446557609336a9b9e48e649ed84e58c45bd9032a93abbd454104",
"move": "4132e270ada8d0c79b477095c84d656d6d354e200c1748ce7fc425f36d0b7031",
"nav_settings": "b93dd46d3a3d074e0d8834167f977dab417fbc1a721bc4db5e840732304e4193",
"new": "c64c78fd8445bc7b2aa8765d0507b9adf23e35cbf52828d3f1987edccce0389f",
"normal": "1148cef2d60a858b69406a2e2527d4ea126f45846de7cc2fb3a96640e9ee226f",
"normal": "3c000d707837d4e9c865aefa43c1e610bfce5a06a38510f82e4db11fc13ab31f",
"offset": "094607afcfdcb9c1410114d9f8d22ca367f8e32ab4c61f71affe44ad5d09adbe",
"offset_face": "83d39308ec9f3ead78329c62841c8899bbc76ce08e17d539016d18c91d9d4ea7",
"offset_one_side": "094607afcfdcb9c1410114d9f8d22ca367f8e32ab4c61f71affe44ad5d09adbe",
"offset_surface": "b501cfb56b8404cdb2275935d780ce7dbb67e87e29ade3782f0859c59070a2b1",
"offset_symmetric": "67f3489402af3afa055181bbdb2a32b9fe2e0d96dccbe0f28df087577978ac9c",
"offset_two_sides": "ef69f955c5bef15471e9d77a4498e9337bf938ad75dcbc1e95af2515b6fd13da",
"open": "f1880d25489753f7e40390902037bbd3195fbd099a5e9dd175797d832abff727",
"orbit": "4adcd400cdde909139386e78579433354f65f5a77a0594447bf6dda663b3b1b4",
"origin": "9b9360edc16ef9f059bff7d863f1bc0e3abac6acf559d76c4ea39dc374ab2844",
"origin": "636f9f41654a6da7a5a708bcddef85672f2d77bfec2267b0134e8c5a59768b40",
"pan": "2465eda7a39d4458d7818f2eb2b033992a68f80d9505bff538b41eb4511bb632",
"params": "d0d9d0b3e786880e59b985dc2e3a7d593cf3771484a8fd2924d69155195c230f",
"paste": "e1b3c089adafacb6f7b78b86ec7aba0876b189c81d01031045dfee2c47ae8edc",
@@ -164,22 +173,23 @@
"perspective": "5fa45ba9b75dbe9fdad9eae331ca7ff35c6ad018b8a1fc4c8ae7bcfc13ed0c3f",
"pipe": "b9ecf1ae9f6790e2ae6c677f290446f70eba1773a10247fe33d5283b360b0403",
"plane": "3e7fa0a9d57891f3c1ec03d7422bdb29353d3487deed12e8ec5737aeb33c2f3a",
"plane_along_path": "020e25969a7f4e7e3358003addee7df8b1991cde8401df0b4df8295b19497994",
"plane_along_path": "db4686ea02c3c3b8ad0d9eac203cc050c395cff6358358922a79c00e955e6852",
"plane_angle": "2f0914955663b393a377fda080bc03dc5a2133d42c506c1908c53b2fd609b156",
"plane_midplane": "66bd880821e7b122f130824313b7070d72d79168a52df6a126106b6eb5816474",
"plane_perpendicular": "90e1d9b8e75743821aa1082596d9c3f288ab3baddf087fbae3f2025481e58a6e",
"plane_perpendicular": "0055fb18ee39044bfc9f2ac53ba55fe73d8fe59e8f4cf225ee2c723a883d3085",
"plane_tangent": "c3872b9f1d0f0b2493a5488dfdad50049c3422c29023250088eeda9bbcc6e795",
"plane_three_points": "08e2dbaf606d165ef4f788869fc794a4fbbbb2ee86a60017b76e467af6da338a",
"plane_two_edges": "1588b8d4741382d9e8fec38212419ac1c05b1bc6036f276d3b5c62db66f8ebdb",
"plane_two_edges": "21de401ec5bab8ae37510da6fa0a6910325f858c17460da0041fd26897338d30",
"plastic_assign": "bbfdb2d2c07629c047e8e671c7c032654668a67b4b8a934f74cbb3d7a00cc5fe",
"plastic_rule": "51b8591d92907a3ffb76a47a0503d7ba86706d129dd990515a364dbf1ab8cb48",
"play": "d1c17ebc16e9a4ee5ae2a8cc37b515ffcc8c53a2ab17117959e9b21278df8def",
"plus": "4f324157bb2348ac9f09346b669b08d66ed96cb1c694f33db6f21134f18904b5",
"point": "fd19dbcd26e2a898267bab54b9d89779d97894209c1771317d7debef5a6f8f5d",
"point_along_path": "1fd58d45285fb5cabeddbd35bc8485af1b6d7f1414e302202b4e73de593d1b7a",
"point_along_path": "17abd16dec5f70e9f8449c927aa995cfed10fca552ec7cd8f51b209c52012595",
"point_center": "0ad755896f7d0154a854bda16d3059da78d1fae57962e13a3bb1b31e284b7e79",
"point_edge_plane": "548a6c1c944caabe44ab0318e85ae82f39c11f74fbedda35f57bc6615755f249",
"point_edge_plane": "a277bc0b87f2dd39eb613835d0f961b3372483e22053e6a522a06c535ad530c4",
"point_three_planes": "b33c7508ffa67b66a57b72c6ea7900e93ff16362a29d0b2688e0b1db9bfad041",
"point_two_edges": "35dcb67b36c8e41041cc836b01056731ab6f14c5e3b4bdd836fbee2a1dd897e3",
"point_two_edges": "da329241706e405b877cec20639dc0a78505ca7212c7c47ad1f1f34aa26f4666",
"point_vertex": "649bb63caf65a3fbc00a1222cc642294832b5e5d8c4d33f812e09a70684c4abe",
"polygon": "4919268cca7d21ce98dbf4ca01ee832350e793afe4213e257d3c46e94ea88ad1",
"polygon_circumscribed": "7e4ad16cc1f42c599287b186f26f4ff3c108736877a571a5513bfc72d0cbb23f",
@@ -213,17 +223,19 @@
"scale": "a66f69d9a1d46c8be30da0a15827bc7e38cba72d6271537763b888c2c7e2b3fa",
"screw": "5e30570785360d6a591ea22862a82f1cf62228385c1e96382536db8a453ac791",
"section": "cdd13f99f15f6d59bfa21c747810608d6afd00cb8207faf0337928c8521e7eae",
"select": "a0c716234ef1d599be9a7654d6b2911ebe4b2de479383ba33aeab14992443a2f",
"select": "fdf6431270006ca7409c60c37cfed59572de04229f104ec723e6010ffa2b7163",
"settings": "9193dd3a1007c43660d0d47b6499cb2571338b67949e1a99df112057b9bf2b8a",
"sheet": "774e30923ef6f37dd9c0b2bc3e45705e83b3a76700bf071cd41155a365d0daa4",
"sheet_rules": "b8c3a67c82be20fc00c109decc566e8755d70167e04e0a9fb92941f7b27aa993",
"sheet_rules": "5d3a2f37d3b4a4262b3ddbdae53d61f29e0f8e284dd54006da25a7acebfa1114",
"shell": "a1959ea0d0441a479bf654b6e549e2e44a0bfdc9cda4eb46fe1fbfd884de7e38",
"silhouette": "2a848aed2f247b1c0439f830a34a513c93bc5493c40044c821d493d863ad996e",
"sketch": "b52e5862b1709a9a7e3ca00d7436d126fbfc01b38080006fa4e17dc581dfa103",
"sketch_break": "430582243311f304dbab397ce58c8c9aef9c78ed88d3e921a57e584ddb95d94c",
"sketch_break": "67a5fab332616e6d6a9aeae3bedc04ee0768179143ba34b795bdd98cea04bb24",
"sketch_chamfer": "80ab27a9929f9cf0ab2f827757e007173d74ddca6c19af238931c40d4cd874b4",
"sketch_chamfer_angle": "b853f5e7c3b008c0d56ac445a9a28f713987c919ea284945113ad35e7665e2a7",
"sketch_chamfer_two_distances": "2b70d658e341f4388838f3c8c579ec5f4f3a7fddcfa2ce7a3f89fc3db6422d6e",
"sketch_chamfer_angle": "6750268d05e0b0fd74c2d0368bf4703b09da825def668dc3dfd05deef3ba9adb",
"sketch_chamfer_equal": "80ab27a9929f9cf0ab2f827757e007173d74ddca6c19af238931c40d4cd874b4",
"sketch_chamfer_two": "1f5ecf1283e2b021e067ed2e4e2e97aa4e1ad3c7c98d8609bb81c5f811988f78",
"sketch_chamfer_two_distances": "1f5ecf1283e2b021e067ed2e4e2e97aa4e1ad3c7c98d8609bb81c5f811988f78",
"sketch_circ_pattern": "c5ff904237a3072f858f66c2777febc4facd2fee630bbf979047c5685d9a05ce",
"sketch_dxf": "dddce44d4dd10f75a221208fcf431b36b3f0cafce6611fc695ac73afb4bf5789",
"sketch_fillet": "e8fa170e5f6899abf75788b9384d49f2b9d77ba9b644f79309284a9e65629fe1",
@@ -241,6 +253,7 @@
"spin": "a5bebeff8276dd12ab7e42ddf0051370b9063fb602607298acf22e77044434d1",
"spline": "a0dcb25614e5fe1a8f17bc3bddbb11b61cf82b461dd46443ab28cddd6e4e32e3",
"spline_cv": "353f6e411c0ce611f660cef2e4fc4f8f55edc2c82288de91948dc638cd2b5f15",
"split": "eb9619e74c6ee1b1989d35a2e31d2d33a7adac210c4cd424e5382902945ac68b",
"split_body": "eb9619e74c6ee1b1989d35a2e31d2d33a7adac210c4cd424e5382902945ac68b",
"split_face": "c8b89072118bb7a28b243d38e26b81a796781a631f30b3b1fc296ba23e4d908d",
"spun": "b4cfa093ffb43611ee124f4a39fb44c609deb9cde5e05e4dfbaf893fe197c27b",
@@ -249,7 +262,7 @@
"status_warning": "1ac4f0ccfac2c01f6f66cb42188f9ae053c4aef9c5bbd249f1fb09611eb4d825",
"stitch": "6c3205d64007b771ed78d6f8055867578439fb25d591563b32dbd3343c98732d",
"style": "6c2b04234b82ea4858c80190cc7208c60f549358253005afc35ab4e29506b578",
"suppress": "6c033a6672a3b6ca926e3d7a75a758c9db7e1144210cd360be043a9fdf999ede",
"suppress": "c54fbc01c12c0175d78cd63f692e8aaf73fc2cac79752b2a3ad821ac32734343",
"surface": "ba0e839ce0ee03e7ff95a2389a5ecb20bd80c713064d9b1bd5f461f4a80d1921",
"surface_blend": "0642f3b4af7d22f379a3c74f54c7b1c9eef0bdda19372ce4df73c0e56bdd7bbc",
"surface_boundary": "fb8501bae192e2416118cc2e88f544e0e29197f6720bccc541c967ca0848d8d3",
@@ -265,14 +278,14 @@
"surface_ruled": "c3f0202d3beaacef0368765ce771eb9e3b0e3e157f86564c72d81f0fad9a3cab",
"surface_stitch": "6c3205d64007b771ed78d6f8055867578439fb25d591563b32dbd3343c98732d",
"surface_sweep": "6d7f18c12b2e4c1dbe3632fe08239687f6f027b29b6dddbf8c8e2d9151c7baf3",
"surface_thicken": "676eb73a99d708214e8951ebe2c4d88d60de2d752c725d8106d12fbd812e461a",
"surface_thicken": "35aaa8e4fdbcd220a3a05165d5230a7ce576fbf5a6a5772d27162ab61bb71538",
"surface_trim": "ae269721b386bd1264184b9838df3240b5b6ba02b99c9993384ef21200fb7398",
"surface_unstitch": "bd8302fe95a89eb6f9bbf35ef0ee98c8a2186e160cff3d091f8cf2a0a75d4473",
"surface_untrim": "24de5d558519e0629280d11919ba5661c92ccdccdce03f8f3d83f2d7867c2173",
"svg": "7da77d53c84db18ce95737ad633c105d1009444b3192a5e0aae76873cfc9cc12",
"sweep": "6d7f18c12b2e4c1dbe3632fe08239687f6f027b29b6dddbf8c8e2d9151c7baf3",
"text": "9d31b72b16644681c8a86ac257511847ad69ce4351cf6960442222e94b168c4a",
"thicken": "676eb73a99d708214e8951ebe2c4d88d60de2d752c725d8106d12fbd812e461a",
"thicken": "35aaa8e4fdbcd220a3a05165d5230a7ce576fbf5a6a5772d27162ab61bb71538",
"thread": "158728c3dcf6623bb1584d162398bb12c28d18770e0d47f5e6328355ce31b37c",
"torus": "5ae13fc213582e0844118b910a3a26efd9e4b177d21500363fbae230803cb4fd",
"trace": "5f2ecc61d4591d3c3b0ecccd35296b8613fce7beb2ae214fb906160c31d89187",
@@ -284,7 +297,7 @@
"update": "c675e16df4c78589a00be4a34f98627500681672ee8a668e929aec50982da3cc",
"warning": "1ac4f0ccfac2c01f6f66cb42188f9ae053c4aef9c5bbd249f1fb09611eb4d825",
"web": "be5cd95bebcd0ebe257f9eb862112124429b381ef4a2f0849938fac7c9ccc619",
"ws_animate": "d36a0834ad989a6e16e2b0db493ad8867c590caedf0442c51eff19e934c887f8",
"ws_animate": "f34f187850789cc4f141b1ace114082be04edec8e7771d6730fec1cef19c7f64",
"ws_design": "7b1dd678a2d018892b965a78932c047bbfff6b4439d7bd9fe85c09a995f97581",
"ws_drawing": "61e99c7179f3448a31b8c76392334352ec3d5e39b0b92fe5244940146d0986cb",
"ws_render": "1bfe474d1742623ad2142129e4d6a40015e674813eb1db401d2e88e55b1f2a03",
@@ -292,6 +305,9 @@
"zoom": "89c121fa85df034d70bd9f06e20cbd54d163691d4df03b13a9ac5372e951aeda"
},
"notes": {
"pattern_flip": "This feels sparse. Improve the composition"
"construction": "Fix the bottom dot's position",
"infinite": "Make the infinity symbol use the 'Line art' colour",
"pattern_flip": "This feels sparse. Improve the composition",
"sheet_rules": "Make the top line also blue"
}
}
@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Connected agent</title>
<path d="M25 7L25 24M39 7L39 24" fill="none" stroke="currentColor" stroke-width="5" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M16 25L48 25L48 31Q48 41 38 41L36 41L36 47L28 47L28 41L26 41Q16 41 16 31Z" fill="currentColor" stroke="currentColor" stroke-width="3" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M32 47L32 57" fill="none" stroke="currentColor" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 558 B

@@ -0,0 +1,11 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Assistant</title>
<defs>
<mask id="assistant-sparkle" maskUnits="userSpaceOnUse" x="-32" y="-32" width="128" height="128">
<path d="M-32 -32L96 -32L96 96L-32 96Z" fill="white"/>
<path d="M28 18.5Q30.3 27.7 39.5 30Q30.3 32.3 28 41.5Q25.7 32.3 16.5 30Q25.7 27.7 28 18.5Z" fill="black"/>
<path d="M43.5 15.5Q44.6 19.9 49 21Q44.6 22.1 43.5 26.5Q42.4 22.1 38 21Q42.4 19.9 43.5 15.5Z" fill="black"/>
</mask>
</defs>
<path d="M15 11.5L49 11.5Q55 11.5 55 17.5L55 40.5Q55 46.5 49 46.5L30 46.5L18 55L18 46.5L15 46.5Q9 46.5 9 40.5L9 17.5Q9 11.5 15 11.5Z" fill="currentColor" stroke="currentColor" stroke-width="3" stroke-linejoin="round" stroke-linecap="round" mask="url(#assistant-sparkle)"/>
</svg>

After

Width:  |  Height:  |  Size: 792 B

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Chevron right</title>
<path d="M24 16L40 32L24 48" fill="none" stroke="currentColor" stroke-width="5" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 230 B

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Close</title>
<path d="M19 19L45 45M45 19L19 45" fill="none" stroke="currentColor" stroke-width="5" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 228 B

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Disclosure closed</title>
<path d="M24 17L46 32L24 47Z" fill="currentColor" stroke="currentColor" stroke-width="6" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 243 B

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Disclosure open</title>
<path d="M17 24L47 24L32 46Z" fill="currentColor" stroke="currentColor" stroke-width="6" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 241 B

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Export Tray</title>
<path d="M32 38L32 10M20 22L32 10L44 22M10 43L10 54L54 54L54 43" fill="none" stroke="currentColor" stroke-width="5.6" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 266 B

+5
View File
@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Eye</title>
<path d="M6 32C16 18 48 18 58 32C48 46 16 46 6 32Z" fill="none" stroke="currentColor" stroke-width="5" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M24 32A8 8 0 1 0 40 32A8 8 0 1 0 24 32Z" fill="currentColor"/>
</svg>

After

Width:  |  Height:  |  Size: 317 B

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Import Tray</title>
<path d="M32 10L32 38M20 26L32 38L44 26M10 43L10 54L54 54L54 43" fill="none" stroke="currentColor" stroke-width="5.6" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 266 B

+4
View File
@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Plus</title>
<path d="M32 14L32 50M14 32L50 32" fill="none" stroke="currentColor" stroke-width="5" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 227 B

@@ -0,0 +1,12 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Suppress</title>
<defs>
<mask id="suppress-slash" maskUnits="userSpaceOnUse" x="-32" y="-32" width="128" height="128">
<path d="M-32 -32L96 -32L96 96L-32 96Z" fill="white"/>
<path d="M13 51L51 13" fill="none" stroke="black" stroke-width="13" stroke-linejoin="round" stroke-linecap="round"/>
</mask>
</defs>
<path d="M6 32C16 18 48 18 58 32C48 46 16 46 6 32Z" fill="none" stroke="currentColor" stroke-width="5" stroke-linejoin="round" stroke-linecap="round" mask="url(#suppress-slash)"/>
<path d="M24 32A8 8 0 1 0 40 32A8 8 0 1 0 24 32Z" fill="currentColor" mask="url(#suppress-slash)"/>
<path d="M13 51L51 13" fill="none" stroke="currentColor" stroke-width="5" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 817 B

+1 -1
View File
@@ -4,5 +4,5 @@
<path d="M12 52L52 52" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M12 8L16.5 17L7.5 17Z" fill="#236ead"/>
<path d="M56 52L47 56.5L47 47.5Z" fill="#236ead"/>
<circle cx="12" cy="52" r="4" fill="#34465b"/>
<circle cx="12" cy="52" r="4" fill="currentColor"/>
</svg>

Before

Width:  |  Height:  |  Size: 489 B

After

Width:  |  Height:  |  Size: 494 B

@@ -6,5 +6,5 @@
<path d="M32 6L32 33" fill="none" stroke="#ffffff" stroke-width="6.45" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M32 6L32 33" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M32 27.6L36.68 30.3L36.68 35.7" fill="none" stroke="#ffffff" stroke-width="4.05" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M32 27.6L36.68 30.3L36.68 35.7" fill="none" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M32 27.6L36.68 30.3L36.68 35.7" fill="none" stroke="currentColor" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

+1 -1
View File
@@ -3,5 +3,5 @@
<path d="M7 32L23 32M41 32L57 32" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<circle cx="23" cy="32" r="3.07" fill="#236ead"/>
<circle cx="41" cy="32" r="3.07" fill="#236ead"/>
<path d="M32 22L32 42" fill="none" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M32 22L32 42" fill="none" stroke="currentColor" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 452 B

After

Width:  |  Height:  |  Size: 457 B

@@ -1,6 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Coincident</title>
<path d="M10.4 42.32L32 27.92" fill="none" stroke="#34465b" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M32 27.92L53.6 37.52" fill="none" stroke="#34465b" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M10.4 42.32L32 27.92" fill="none" stroke="currentColor" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M32 27.92L53.6 37.52" fill="none" stroke="currentColor" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M25.76 27.92A6.24 6.24 0 1 0 38.24 27.92A6.24 6.24 0 1 0 25.76 27.92Z" fill="#b9ddf8" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 541 B

After

Width:  |  Height:  |  Size: 551 B

+1 -1
View File
@@ -2,5 +2,5 @@
<title>Fixed</title>
<path d="M22.4 22.4A9.6 9.6 0 1 0 41.6 22.4A9.6 9.6 0 1 0 22.4 22.4Z" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M32 32L32 51.2" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M24.8 51.2L39.2 51.2" fill="none" stroke="#34465b" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M24.8 51.2L39.2 51.2" fill="none" stroke="currentColor" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 517 B

After

Width:  |  Height:  |  Size: 522 B

@@ -1,6 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Midpoint</title>
<path d="M10.4 39.2L53.6 24.8" fill="none" stroke="#34465b" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M10.4 39.2L53.6 24.8" fill="none" stroke="currentColor" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<circle cx="32" cy="32" r="7.3" fill="#ffffff"/>
<circle cx="32" cy="32" r="5.6" fill="#236ead"/>
</svg>

Before

Width:  |  Height:  |  Size: 327 B

After

Width:  |  Height:  |  Size: 332 B

@@ -2,5 +2,5 @@
<title>Perpendicular</title>
<path d="M12.8 48.8L51.2 48.8" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M32 48.8L32 15.2" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M32 39.2L41.6 39.2L41.6 48.8" fill="none" stroke="#34465b" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M32 39.2L41.6 39.2L41.6 48.8" fill="none" stroke="currentColor" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 496 B

After

Width:  |  Height:  |  Size: 501 B

@@ -1,6 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Regular polygon</title>
<path d="M52.4 32L42.2 49.67L21.8 49.67L11.6 32L21.8 14.33L42.2 14.33Z" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M32 32L52.4 32" fill="none" stroke="#34465b" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<circle cx="32" cy="32" r="3.4" fill="#34465b"/>
<path d="M32 32L52.4 32" fill="none" stroke="currentColor" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<circle cx="32" cy="32" r="3.4" fill="currentColor"/>
</svg>

Before

Width:  |  Height:  |  Size: 449 B

After

Width:  |  Height:  |  Size: 459 B

@@ -1,6 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Curvature</title>
<path d="M10.4 32L12.2 27.65L14 23.6L15.8 20.12L17.6 17.45L19.4 15.77L21.2 15.2L23 15.77L24.8 17.45L26.6 20.12L28.4 23.6L30.2 27.65L32 32" fill="none" stroke="#34465b" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M10.4 32L12.2 27.65L14 23.6L15.8 20.12L17.6 17.45L19.4 15.77L21.2 15.2L23 15.77L24.8 17.45L26.6 20.12L28.4 23.6L30.2 27.65L32 32" fill="none" stroke="currentColor" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M32 32L33.8 36.35L35.6 40.4L37.4 43.88L39.2 46.55L41 48.23L42.8 48.8L44.6 48.23L46.4 46.55L48.2 43.88L50 40.4L51.8 36.35L53.6 32" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<circle cx="32" cy="32" r="3.4" fill="#34465b"/>
<circle cx="32" cy="32" r="3.4" fill="currentColor"/>
</svg>

Before

Width:  |  Height:  |  Size: 624 B

After

Width:  |  Height:  |  Size: 634 B

@@ -1,5 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Tangent</title>
<path d="M17.6 32A14.4 14.4 0 1 0 46.4 32A14.4 14.4 0 1 0 17.6 32Z" fill="none" stroke="#34465b" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M17.6 32A14.4 14.4 0 1 0 46.4 32A14.4 14.4 0 1 0 17.6 32Z" fill="none" stroke="currentColor" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M10.4 17.6L53.6 17.6" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 392 B

After

Width:  |  Height:  |  Size: 397 B

@@ -1,9 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Centerline</title>
<path d="M10 32L23 32M41 32L54 32" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M11 53L23.41 40.59M40.59 23.41L53 11" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<circle cx="32" cy="32" r="3.6" fill="#236ead"/>
<circle cx="10" cy="32" r="5.8" fill="#ffffff"/>
<circle cx="10" cy="32" r="3.4" fill="#236ead"/>
<circle cx="54" cy="32" r="5.8" fill="#ffffff"/>
<circle cx="54" cy="32" r="3.4" fill="#236ead"/>
<circle cx="11" cy="53" r="5.8" fill="#ffffff"/>
<circle cx="11" cy="53" r="3.4" fill="#236ead"/>
<circle cx="53" cy="11" r="5.8" fill="#ffffff"/>
<circle cx="53" cy="11" r="3.4" fill="#236ead"/>
</svg>

Before

Width:  |  Height:  |  Size: 486 B

After

Width:  |  Height:  |  Size: 498 B

@@ -1,7 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Construction</title>
<path d="M13 51L19 45M25 39L31 33M37 27L43 21M49 15L51 13" fill="none" stroke="#6f879c" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M10 54L12 52" fill="none" stroke="#6f879c" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<circle cx="12" cy="52" r="3.07" fill="#6f879c"/>
<circle cx="52" cy="12" r="3.07" fill="#6f879c"/>
<path d="M17 47L23 41M29 35L35 29M41 23L47 17" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<circle cx="11" cy="53" r="3.07" fill="#236ead"/>
<circle cx="53" cy="11" r="3.07" fill="#236ead"/>
</svg>

Before

Width:  |  Height:  |  Size: 484 B

After

Width:  |  Height:  |  Size: 349 B

@@ -1,6 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Dimension</title>
<path d="M10 12L10 52M54 12L54 52" fill="none" stroke="#34465b" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M10 12L10 52M54 12L54 52" fill="none" stroke="currentColor" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M19 32L45 32" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M13 32L24 26L24 38Z" fill="#236ead"/>
<path d="M51 32L40 38L40 26Z" fill="#236ead"/>

Before

Width:  |  Height:  |  Size: 451 B

After

Width:  |  Height:  |  Size: 456 B

+3 -3
View File
@@ -1,6 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>More</title>
<circle cx="13" cy="32" r="5" fill="#34465b"/>
<circle cx="32" cy="32" r="5" fill="#34465b"/>
<circle cx="51" cy="32" r="5" fill="#34465b"/>
<circle cx="13" cy="32" r="5" fill="currentColor"/>
<circle cx="32" cy="32" r="5" fill="currentColor"/>
<circle cx="51" cy="32" r="5" fill="currentColor"/>
</svg>

Before

Width:  |  Height:  |  Size: 237 B

After

Width:  |  Height:  |  Size: 252 B

-8
View File
@@ -1,8 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Eye</title>
<path d="M4 32C16 14 48 14 60 32C48 50 16 50 4 32Z" fill="#e2e8ef"/>
<path d="M4 32C16 14 48 14 60 32C48 50 16 50 4 32Z" fill="none" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M22 32A10 10 0 1 0 42 32A10 10 0 1 0 22 32Z" fill="#328bd3"/>
<path d="M22 32A10 10 0 1 0 42 32A10 10 0 1 0 22 32Z" fill="none" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<circle cx="32" cy="32" r="4" fill="#34465b"/>
</svg>

Before

Width:  |  Height:  |  Size: 588 B

+1 -1
View File
@@ -1,6 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Dependency graph</title>
<path d="M16.5 17L47.5 32L16.5 47" fill="none" stroke="#34465b" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M16.5 17L47.5 32L16.5 47" fill="none" stroke="currentColor" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M8.6 12.44L16.5 17L16.5 26.12L8.6 21.56Z" fill="#b8c3cf"/>
<path d="M24.4 12.44L16.5 17L16.5 26.12L24.4 21.56Z" fill="#8e9faf"/>
<path d="M16.5 7.88L24.4 12.44L16.5 17L8.6 12.44Z" fill="#e2e8ef"/>

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

@@ -1,9 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Infinite line</title>
<path d="M32 32L15.66 48.34" fill="none" stroke="#34465b" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M10 54L13.18 44.45L19.55 50.82Z" fill="#34465b"/>
<path d="M32 32L48.34 15.66" fill="none" stroke="#34465b" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M54 10L50.82 19.55L44.45 13.18Z" fill="#34465b"/>
<path d="M32 32L15.66 48.34" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M10 54L13.18 44.45L19.55 50.82Z" fill="#236ead"/>
<path d="M32 32L48.34 15.66" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M54 10L50.82 19.55L44.45 13.18Z" fill="#236ead"/>
<path d="M32 32C26.86 22.57 20 23.43 20 32C20 40.57 26.86 41.43 32 32C37.14 22.57 44 23.43 44 32C44 40.57 37.14 41.43 32 32" fill="none" stroke="#ffffff" stroke-width="10.5" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M32 32C26.86 22.57 20 23.43 20 32C20 40.57 26.86 41.43 32 32C37.14 22.57 44 23.43 44 32C44 40.57 37.14 41.43 32 32" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M32 32C26.86 22.57 20 23.43 20 32C20 40.57 26.86 41.43 32 32C37.14 22.57 44 23.43 44 32C44 40.57 37.14 41.43 32 32" fill="none" stroke="currentColor" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 929 B

After

Width:  |  Height:  |  Size: 934 B

@@ -13,5 +13,5 @@
<path d="M26.03 34.5L17.85 53.09" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M15.03 59.5L13.97 47.02L24.95 51.85Z" fill="#236ead"/>
<circle cx="26.03" cy="34.5" r="9" fill="#ffffff"/>
<circle cx="26.03" cy="34.5" r="6.3" fill="#34465b"/>
<circle cx="26.03" cy="34.5" r="6.3" fill="currentColor"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

@@ -14,6 +14,6 @@
<path d="M44.97 12.22L40.5 14.72L41.55 8.4Z" fill="#236ead" />
<path d="M30.8 33.02L30.38 32.94L29.95 32.91L29.51 32.9L29.05 32.94L28.59 33.01L28.11 33.12L27.63 33.27L27.14 33.45L26.64 33.67L26.15 33.93L25.65 34.21L25.16 34.53L24.66 34.88L24.17 35.26L23.69 35.68L23.21 36.12L22.75 36.58L22.29 37.07L21.85 37.59L21.42 38.12L21 38.68L20.6 39.25L20.22 39.84L19.86 40.44L19.52 41.05L19.2 41.68L18.91 42.31L18.63 42.94L18.38 43.58L18.16 44.23L17.96 44.87L17.8 45.5L17.65 46.13L17.54 46.76L17.45 47.37L17.39 47.98L17.37 48.57L17.37 49.14L17.39 49.69L17.45 50.23L17.54 50.75L17.65 51.24L17.8 51.71L17.96 52.15L18.16 52.56L18.38 52.94L18.63 53.3L18.91 53.62" fill="none" stroke="#ffffff" stroke-width="4.8" stroke-linejoin="round" stroke-linecap="round" />
<path d="M20.23 55.13L15.18 54.22L20 50.01Z" fill="#ffffff" stroke="#ffffff" stroke-width="2" stroke-linejoin="round" stroke-linecap="round" />
<path d="M30.8 33.02L30.38 32.94L29.95 32.91L29.51 32.9L29.05 32.94L28.59 33.01L28.11 33.12L27.63 33.27L27.14 33.45L26.64 33.67L26.15 33.93L25.65 34.21L25.16 34.53L24.66 34.88L24.17 35.26L23.69 35.68L23.21 36.12L22.75 36.58L22.29 37.07L21.85 37.59L21.42 38.12L21 38.68L20.6 39.25L20.22 39.84L19.86 40.44L19.52 41.05L19.2 41.68L18.91 42.31L18.63 42.94L18.38 43.58L18.16 44.23L17.96 44.87L17.8 45.5L17.65 46.13L17.54 46.76L17.45 47.37L17.39 47.98L17.37 48.57L17.37 49.14L17.39 49.69L17.45 50.23L17.54 50.75L17.65 51.24L17.8 51.71L17.96 52.15L18.16 52.56L18.38 52.94L18.63 53.3L18.91 53.62" fill="none" stroke="#34465b" stroke-width="2.6" stroke-linejoin="round" stroke-linecap="round" />
<path d="M20.23 55.13L15.18 54.22L20 50.01Z" fill="#34465b" />
<path d="M30.8 33.02L30.38 32.94L29.95 32.91L29.51 32.9L29.05 32.94L28.59 33.01L28.11 33.12L27.63 33.27L27.14 33.45L26.64 33.67L26.15 33.93L25.65 34.21L25.16 34.53L24.66 34.88L24.17 35.26L23.69 35.68L23.21 36.12L22.75 36.58L22.29 37.07L21.85 37.59L21.42 38.12L21 38.68L20.6 39.25L20.22 39.84L19.86 40.44L19.52 41.05L19.2 41.68L18.91 42.31L18.63 42.94L18.38 43.58L18.16 44.23L17.96 44.87L17.8 45.5L17.65 46.13L17.54 46.76L17.45 47.37L17.39 47.98L17.37 48.57L17.37 49.14L17.39 49.69L17.45 50.23L17.54 50.75L17.65 51.24L17.8 51.71L17.96 52.15L18.16 52.56L18.38 52.94L18.63 53.3L18.91 53.62" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linejoin="round" stroke-linecap="round" />
<path d="M20.23 55.13L15.18 54.22L20 50.01Z" fill="currentColor" />
</svg>

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

+1 -1
View File
@@ -1,4 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Normal line</title>
<path d="M11 53L53 11" fill="none" stroke="#34465b" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M11 53L53 11" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 220 B

After

Width:  |  Height:  |  Size: 220 B

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Symmetric offset</title>
<path d="M8 56L8 40A32 32 0 0 1 40 8L56 8" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M20 56L20 40A20 20 0 0 1 40 20L56 20" fill="none" stroke="#8e9faf" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M32 56L32 40A8 8 0 0 1 40 32L56 32" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 537 B

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Two independent offset sides</title>
<path d="M8 56L8 40A32 32 0 0 1 40 8L56 8" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M24 56L24 40A16 16 0 0 1 40 24L56 24" fill="none" stroke="#8e9faf" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M34 56L34 40A6 6 0 0 1 40 34L56 34" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 549 B

+4 -4
View File
@@ -2,10 +2,10 @@
<title>Origin</title>
<path d="M32 39L32 19" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M32 11L37 20L27 20Z" fill="#236ead"/>
<path d="M32 39L49.09 48.97" fill="none" stroke="#34465b" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M56 53L45.71 52.78L50.75 44.15Z" fill="#34465b"/>
<path d="M32 39L14.91 48.97" fill="none" stroke="#34465b" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M8 53L13.25 44.15L18.29 52.78Z" fill="#34465b"/>
<path d="M32 39L49.09 48.97" fill="none" stroke="currentColor" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M56 53L45.71 52.78L50.75 44.15Z" fill="currentColor"/>
<path d="M32 39L14.91 48.97" fill="none" stroke="currentColor" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M8 53L13.25 44.15L18.29 52.78Z" fill="currentColor"/>
<path d="M26.3 35.71L32 39L32 45.58L26.3 42.29Z" fill="#b8c3cf"/>
<path d="M37.7 35.71L32 39L32 45.58L37.7 42.29Z" fill="#8e9faf"/>
<path d="M32 32.42L37.7 35.71L32 39L26.3 35.71Z" fill="#e2e8ef"/>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

@@ -1,6 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Plane along path</title>
<path d="M7 48C18 48 19 32 32 32C45 32 46 16 57 16" fill="none" stroke="#34465b" stroke-width="3.2" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M7 48C18 48 19 32 32 32C45 32 46 16 57 16" fill="none" stroke="currentColor" stroke-width="3.2" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M22 13L42 25L42 51L22 39Z" fill="#b9ddf8" stroke="#236ead" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round" stroke-dasharray="0.1 5"/>
<path d="M32 32C45 32 46 16 57 16" fill="none" stroke="#34465b" stroke-width="3.2" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M32 32C45 32 46 16 57 16" fill="none" stroke="currentColor" stroke-width="3.2" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 551 B

After

Width:  |  Height:  |  Size: 561 B

@@ -3,5 +3,5 @@
<path d="M28.71 27.48L56.68 43.63L35.29 55.98L7.32 39.84Z" fill="#b8c3cf" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M18.01 33.66L45.99 49.81L45.99 24.16L18.01 8.01Z" fill="#b9ddf8" stroke="#236ead" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round" stroke-dasharray="0.1 5"/>
<path d="M25.42 45.53L25.42 37.94L32 34.13" fill="none" stroke="#ffffff" stroke-width="4.05" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M25.42 45.53L25.42 37.94L32 34.13" fill="none" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M25.42 45.53L25.42 37.94L32 34.13" fill="none" stroke="currentColor" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 742 B

After

Width:  |  Height:  |  Size: 747 B

@@ -1,6 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Plane through two edges</title>
<path d="M25.9 17.92L56.39 35.52L38.1 46.08L7.61 28.48Z" fill="#b9ddf8" stroke="#236ead" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round" stroke-dasharray="0.1 5"/>
<path d="M18.28 17.92L56.39 39.92" fill="none" stroke="#34465b" stroke-width="3.2" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M7.61 24.08L45.72 46.08" fill="none" stroke="#34465b" stroke-width="3.2" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M18.28 17.92L56.39 39.92" fill="none" stroke="currentColor" stroke-width="3.2" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M7.61 24.08L45.72 46.08" fill="none" stroke="currentColor" stroke-width="3.2" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 561 B

After

Width:  |  Height:  |  Size: 571 B

@@ -1,6 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Point along path</title>
<path d="M8 48.8C16 48.8 17 19.8 32 19.8C47 19.8 48 44.8 56 44.8" fill="none" stroke="#34465b" stroke-width="3.2" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M8 48.8C16 48.8 17 19.8 32 19.8C47 19.8 48 44.8 56 44.8" fill="none" stroke="currentColor" stroke-width="3.2" stroke-linejoin="round" stroke-linecap="round"/>
<circle cx="32" cy="19.8" r="6.2" fill="#ffffff"/>
<circle cx="32" cy="19.8" r="4.5" fill="#236ead"/>
</svg>

Before

Width:  |  Height:  |  Size: 373 B

After

Width:  |  Height:  |  Size: 378 B

@@ -1,9 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Point at edge and plane</title>
<path d="M22 6.55L40 58.55" fill="none" stroke="#34465b" stroke-width="3.2" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M22 6.55L40 58.55" fill="none" stroke="currentColor" stroke-width="3.2" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M29.66 21.6L56.16 36.9L34.34 49.5L7.84 34.2Z" fill="#e2e8ef" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M22 6.55L32 35.44" fill="none" stroke="#ffffff" stroke-width="5.4" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M22 6.55L32 35.44" fill="none" stroke="#34465b" stroke-width="3.2" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M22 6.55L32 35.44" fill="none" stroke="currentColor" stroke-width="3.2" stroke-linejoin="round" stroke-linecap="round"/>
<circle cx="32" cy="35.44" r="6.2" fill="#ffffff"/>
<circle cx="32" cy="35.44" r="4.5" fill="#236ead"/>
</svg>

Before

Width:  |  Height:  |  Size: 756 B

After

Width:  |  Height:  |  Size: 766 B

@@ -1,6 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Point through two edges</title>
<path d="M9 39L55 25M15 13L49 51" fill="none" stroke="#34465b" stroke-width="3.2" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M9 39L55 25M15 13L49 51" fill="none" stroke="currentColor" stroke-width="3.2" stroke-linejoin="round" stroke-linecap="round"/>
<circle cx="32" cy="32" r="6.2" fill="#ffffff"/>
<circle cx="32" cy="32" r="4.5" fill="#236ead"/>
</svg>

Before

Width:  |  Height:  |  Size: 344 B

After

Width:  |  Height:  |  Size: 349 B

+1 -1
View File
@@ -1,4 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Select</title>
<path d="M18 7L18 50L28.5 40.5L35.5 56L43 52.5L36 37.5L50 37.5Z" fill="#34465b" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M18 7L18 50L28.5 40.5L35.5 56L43 52.5L36 37.5L50 37.5Z" fill="currentColor" stroke="currentColor" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 260 B

After

Width:  |  Height:  |  Size: 270 B

@@ -6,7 +6,7 @@
<path d="M27.98 26L58.77 43.77L58.77 46.86L36.02 60L5.23 42.23L5.23 39.14Z" fill="none" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M5.23 39.14L36.02 56.91L58.77 43.77" fill="none" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M36.02 56.91L36.02 60" fill="none" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M10 12L54 12" fill="none" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M10 12L54 12" fill="none" stroke="#236ead" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M14 12L14 20" fill="none" stroke="#236ead" stroke-width="2.97" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M22 12L22 17" fill="none" stroke="#236ead" stroke-width="2.97" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M30 12L30 20" fill="none" stroke="#236ead" stroke-width="2.97" stroke-linejoin="round" stroke-linecap="round"/>

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

@@ -2,7 +2,7 @@
<title>Distance and angle sketch chamfer</title>
<path d="M12 36L12 12L42 12" fill="none" stroke="#6f879c" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M12 54L12 36L42 12L54 12" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M12 25A11 11 0 0 1 20.59 29.13" fill="none" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M12 25A11 11 0 0 1 20.59 29.13" fill="none" stroke="currentColor" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<circle cx="12" cy="54" r="3.07" fill="#236ead"/>
<circle cx="54" cy="12" r="3.07" fill="#236ead"/>
</svg>

Before

Width:  |  Height:  |  Size: 628 B

After

Width:  |  Height:  |  Size: 633 B

@@ -2,7 +2,7 @@
<title>Two distance sketch chamfer</title>
<path d="M12 43L12 12L31 12" fill="none" stroke="#6f879c" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M12 54L12 43L31 12L54 12" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M18 9L18 15M9 25L15 25M9 31L15 31" fill="none" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M18 9L18 15M9 25L15 25M9 31L15 31" fill="none" stroke="currentColor" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<circle cx="12" cy="54" r="3.07" fill="#236ead"/>
<circle cx="54" cy="12" r="3.07" fill="#236ead"/>
</svg>

Before

Width:  |  Height:  |  Size: 625 B

After

Width:  |  Height:  |  Size: 630 B

@@ -1,8 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Suppress</title>
<path d="M4 32C16 14 48 14 60 32C48 50 16 50 4 32Z" fill="#e2e8ef"/>
<path d="M4 32C16 14 48 14 60 32C48 50 16 50 4 32Z" fill="none" stroke="#6f879c" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M23 32A9 9 0 1 0 41 32A9 9 0 1 0 23 32Z" fill="#b8c3cf"/>
<path d="M23 32A9 9 0 1 0 41 32A9 9 0 1 0 23 32Z" fill="none" stroke="#6f879c" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M10 54L54 10" fill="none" stroke="#34465b" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 659 B

+5 -5
View File
@@ -2,8 +2,8 @@
<title>Thicken</title>
<path d="M32 40L32 53" fill="none" stroke="#ffffff" stroke-width="6.45" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M32 60L27 51.5L37 51.5Z" fill="#ffffff" stroke="#ffffff" stroke-width="2.2" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M32 40L32 53" fill="none" stroke="#34465b" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M32 60L27 51.5L37 51.5Z" fill="#34465b"/>
<path d="M32 40L32 53" fill="none" stroke="currentColor" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M32 60L27 51.5L37 51.5Z" fill="currentColor"/>
<path d="M7.75 23L32 37L32 53L7.75 39Z" fill="#328bd3"/>
<path d="M56.25 23L32 37L32 53L56.25 39Z" fill="#236ead"/>
<path d="M32 9L56.25 23L32 37L7.75 23Z" fill="#b9ddf8"/>
@@ -11,9 +11,9 @@
<path d="M7.75 23L32 37L56.25 23" fill="none" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M32 37L32 53" fill="none" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M7.75 31L32 45L56.25 31" fill="none" stroke="#ffffff" stroke-width="4.75" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M7.75 31L32 45L56.25 31" fill="none" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M7.75 31L32 45L56.25 31" fill="none" stroke="currentColor" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M32 20.88L32 11" fill="none" stroke="#ffffff" stroke-width="6.45" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M32 4L37 12.5L27 12.5Z" fill="#ffffff" stroke="#ffffff" stroke-width="2.2" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M32 20.88L32 11" fill="none" stroke="#34465b" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M32 4L37 12.5L27 12.5Z" fill="#34465b"/>
<path d="M32 20.88L32 11" fill="none" stroke="currentColor" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M32 4L37 12.5L27 12.5Z" fill="currentColor"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

@@ -1,9 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Animate workspace</title>
<path d="M10.46 24.87L3.55 28.86L3.55 47.86L10.46 51.85M3.55 28.86L10.46 32.85" fill="none" stroke="#ffffff" stroke-width="7.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M10.46 24.87L3.55 28.86L3.55 47.86L10.46 51.85M3.55 28.86L10.46 32.85" fill="none" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M10.46 24.87L3.55 28.86L3.55 47.86L10.46 51.85M3.55 28.86L10.46 32.85" fill="none" stroke="currentColor" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M22.46 24.87L15.55 28.86L15.55 47.86L22.46 51.85M15.55 28.86L22.46 32.85" fill="none" stroke="#ffffff" stroke-width="7.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M22.46 24.87L15.55 28.86L15.55 47.86L22.46 51.85M15.55 28.86L22.46 32.85" fill="none" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M22.46 24.87L15.55 28.86L15.55 47.86L22.46 51.85M15.55 28.86L22.46 32.85" fill="none" stroke="currentColor" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M44 19.36L60.45 28.86L44 38.36L44 57.36L27.55 47.86L27.55 28.86Z" fill="#ffffff" stroke="#ffffff" stroke-width="7.55" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M27.55 28.86L44 38.36L44 57.36L27.55 47.86Z" fill="#b8c3cf"/>
<path d="M60.45 28.86L44 38.36L44 57.36L60.45 47.86Z" fill="#8e9faf"/>

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

+29 -2
View File
@@ -9,7 +9,7 @@
--out FILE Self-contained static review page (default target/icon-status.html)
Read assets/icons/ui/STYLE_GUIDE.md before editing artwork. SVGs are authoritative.
Every discovered non-final icon is Open; no inventory file controls discovery.
Every discovered non-final icon is Open; artwork, source and runtime requests are discovery sources.
"""
import sys, os, re, glob, html, json, hashlib, collections, tomllib, uuid, datetime
@@ -36,6 +36,7 @@ def art_rev():
paths += glob.glob(ROOT + '/crates/ui-egui/src/**/*.rs', recursive=True)
paths += glob.glob(HERE + '/*.py')
paths += [D + '/palette.toml', D + '/aliases.toml', __file__]
paths += glob.glob(D + '/requested_icons.txt')
for path in sorted(paths):
# Equal-length edits can share an mtime. Only the content identifies what
# the reviewer saw; file metadata must never authorize stale acceptance.
@@ -56,8 +57,31 @@ def ui_icon_names(source):
if icon: names.add(icon.group(1))
for table in re.finditer(r'for\s*\(i,\s*\(label,\s*icon,\s*tip\)\)\s*in\s*\[(.*?)\]\s*\.iter\(', source, re.S):
names.update(re.findall(r'\("[^"]*",\s*"([a-z][a-z0-9_]*)",\s*"[^"]*"\)', table.group(1)))
# System-window wrappers and declarative page lists own their icon arguments.
for call in re.finditer(r'\b(?:Window|Confirm)::new\([^;]*?,\s*"([a-z][a-z0-9_]*)"\s*,', source, re.S):
names.add(call.group(1))
for page in re.finditer(r'\bWindowPage\s*\{[^}]*?\bicon:\s*"([a-z][a-z0-9_]*)"', source, re.S):
names.add(page.group(1))
return names
def requested_icons():
"""Runtime ids recorded by the Harness audit; flags describe its last actual render."""
path = os.path.join(D, 'requested_icons.txt')
if not os.path.exists(path):
return {}
requested = {}
with open(path, encoding='utf-8') as manifest:
for number, line in enumerate(manifest, 1):
if not line.strip() or line.startswith('#'):
continue
fields = line.strip().split('\t')
if len(fields) != 2 or not re.fullmatch(r'[a-z][a-z0-9_]*', fields[0]) or fields[1] not in ('true', 'false'):
raise ValueError(f'{path}:{number}: expected icon_id<TAB>true|false')
if fields[0] in requested:
raise ValueError(f'{path}:{number}: duplicate requested icon {fields[0]}')
requested[fields[0]] = fields[1] == 'true'
return requested
def scan():
"""Everything derived from the repo: rows, summary."""
pal = tomllib.load(open(D + '/palette.toml', 'rb'))
@@ -86,6 +110,9 @@ def scan():
t = open(f, encoding='utf-8').read()
for name in ui_icon_names(t):
uses[name].append('ui:' + os.path.relpath(f, ROOT + '/crates/ui-egui/src').replace(os.sep, '/'))
runtime = requested_icons()
for name in runtime:
uses[name].append('ui:runtime audit')
mk = ROOT + '/assets/icons/ui/source/markers.rs'
if os.path.exists(mk):
for name in re.findall(r'=> "(c_[a-z_]+)"', open(mk, encoding='utf-8').read()): uses[name].append('ui:sketch constraint glyph')
@@ -95,7 +122,7 @@ def scan():
rows = []
for n in sorted(set(uses) | set(masters) | set(alias_raw)):
m, acc = alias_of(n)
if m in masters and n != 'placeholder': status = 'done'
if m in masters and n != 'placeholder' and not runtime.get(n, False): status = 'done'
else: status, m = 'placeholder', 'placeholder'
cmds = [u for u in uses.get(n, []) if not u.startswith('ui:')]
rows.append(dict(id=n, status=status, master=m, accent=acc, commands=len(cmds), example=cmds[0] if cmds else '',
+31 -1
View File
@@ -15,7 +15,7 @@ files = sorted(glob.glob(d + '/svg/*/*.svg'))
for path in Path(d).rglob('*'):
if not path.is_file(): continue
rel = path.relative_to(d).as_posix()
allowed = (rel in {'palette.toml', 'aliases.toml', 'review.json', 'references.toml'}
allowed = (rel in {'palette.toml', 'aliases.toml', 'review.json', 'references.toml', 'requested_icons.txt'}
or path.suffix == '.md'
or (rel.startswith('svg/') and path.suffix == '.svg')
or (path.parent in {Path(d) / 'tools', Path(d) / 'tools/author'} and path.suffix == '.py')
@@ -79,6 +79,36 @@ if os.path.exists(references):
if name not in names and name not in al: err(references, name + ': reference artwork is missing')
for message in icon_palette.validation_errors(pal, names | set(al)):
err(d + '/palette.toml', message)
# the review page lies its icons on the real app surfaces: its colours must be the app's tokens
def app_tokens():
src = open(os.path.join(d, '..', '..', '..', 'styles', 'ui', 'palettes.rs'), encoding='utf-8').read()
lo, hi = src.index('pub const fn light'), src.index('pub const fn dark')
light_src, dark_src = src[lo:hi], src[hi:]
def pick(block, name, dark):
pat = (r'\bt\.%s = Color32::from_rgb\((\d+), (\d+), (\d+)\)' if dark else r'\b%s: Color32::from_rgb\((\d+), (\d+), (\d+)\)') % name
m = re.search(pat, block)
return None if not m else '#%02x%02x%02x' % tuple(int(x) for x in m.groups())
out = {}
for name in ('toolbar', 'panel', 'border', 'text', 'text_dim', 'viewport_top'):
out[name] = (pick(light_src, name, False), pick(dark_src, name, True) or pick(light_src, name, False))
return out
css_path = os.path.join(d, 'tools', 'review', 'review.css')
if os.path.exists(css_path):
try:
tok = app_tokens()
css = open(css_path, encoding='utf-8').read()
want = {
':root{': {'--bg': tok['panel'][0], '--fg': tok['text'][0], '--card': tok['toolbar'][0], '--line': tok['border'][0], '--mut': tok['text_dim'][0]},
'body.dark{': {'--bg': tok['viewport_top'][1], '--fg': tok['text'][1], '--card': tok['toolbar'][1], '--line': tok['border'][1], '--mut': tok['text_dim'][1]},
}
for block, values in want.items():
line = next((l for l in css.split('\n') if l.startswith(block)), '')
for var, hexv in values.items():
m = re.search(re.escape(var) + r':(#[0-9a-fA-F]{6})', line)
if not m or m.group(1).lower() != hexv:
err(css_path, '%s%s is %s, the app token is %s (palettes.rs)' % (block, var, m.group(1) if m else 'missing', hexv))
except (OSError, ValueError) as e:
err(css_path, 'cannot compare with palettes.rs: %s' % e)
# an icon accepted as final on the review page must not change (un-accept it there to work on it again)
import json, review_state
rp = d + '/review.json'
+1 -1
View File
@@ -9,7 +9,7 @@ aliases = tomllib.load(open(d + '/aliases.toml', 'rb')).get('alias', {})
def themed(svg, theme, icon, family, size):
_, accent = icon_palette.resolve(palette, aliases, icon)
return icon_palette.themed(svg, palette, theme, accent, namespace='preview-' + family + '-' + icon + '-' + theme + '-' + str(size))
h = '<meta charset=utf-8><style>body{margin:0;font:10px sans-serif}.p{padding:8px}.light{background:#fff;color:#222}.dark{background:#1e2530;color:#ddd}.c{display:inline-block;width:92px;text-align:center;margin:3px 2px;vertical-align:top}.s{display:flex;gap:6px;justify-content:center;align-items:center;margin-top:2px}h3{margin:4px}</style>'
h = '<meta charset=utf-8><style>body{margin:0;font:10px sans-serif}.p{padding:8px}.light{background:#f6f7f9;color:#262a32}.dark{background:#272b34;color:#dee2e8}.c{display:inline-block;width:92px;text-align:center;margin:3px 2px;vertical-align:top}.s{display:flex;gap:6px;justify-content:center;align-items:center;margin-top:2px}h3{margin:4px}</style>'
for theme in ('light', 'dark'):
for fam in sorted(os.listdir(d + '/svg')):
h += '<div class="p %s"><h3>%s - %s</h3>' % (theme, fam, theme)
@@ -1,6 +1,6 @@
<!doctype html><html lang="en"><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
<title>SolidCraft icon style references</title>
<style>body{font:14px/1.5 system-ui;margin:24px auto;max-width:1100px;padding:0 20px;background:#f4f6f9;color:#34465b}h1{font-size:24px}h2{font-size:18px;margin-top:28px}.examples{display:flex;flex-wrap:wrap;gap:10px}article{display:flex;flex-wrap:wrap;width:180px;border:1px solid #d5dbe3;border-radius:8px;overflow:hidden;background:white}b{font-size:11px;width:100%;padding:7px 10px}.light,.dark{width:50%;display:flex;justify-content:center}.dark{background:#1e2530}svg{width:80px;height:80px;display:block;margin:4px auto}code{font-size:12px}a{color:#236ead}</style>
<style>body{font:14px/1.5 system-ui;margin:24px auto;max-width:1100px;padding:0 20px;background:#fafbfc;color:#262a32}h1{font-size:24px}h2{font-size:18px;margin-top:28px}.examples{display:flex;flex-wrap:wrap;gap:10px}article{display:flex;flex-wrap:wrap;width:180px;border:1px solid #d0d4dc;border-radius:8px;overflow:hidden;background:#f6f7f9}b{font-size:11px;width:100%;padding:7px 10px}.light,.dark{width:50%;display:flex;justify-content:center}.dark{background:#272b34}svg{width:80px;height:80px;display:block;margin:4px auto}code{font-size:12px}a{color:#236ead}</style>
<h1>Icon style references</h1><p><a href="/">Return to review</a>. The binding specification is <code>assets/icons/ui/STYLE_GUIDE.md</code>.
Read it before editing. These are original SolidCraft bases and finished visual references, shown directly from the current SVG masters.</p>
<p>64-unit grid · 2.55 outlines · 4.25 action strokes · round caps and joins · standard 30° isometric axes · top light, left medium, right dark.</p>
@@ -1,5 +1,6 @@
:root{--bg:#f4f6f9;--fg:#1d2733;--card:#fff;--line:#d5dbe3;--mut:#6b7a8a;--sz:48px}
body.dark{--bg:#1e2530;--fg:#dbe4ee;--card:#262f3c;--line:#3a4657;--mut:#8a9db0}
/* App surfaces (keep in sync with assets/styles/ui/palettes.rs; lint_icons.py checks): --card is the ribbon surface `toolbar` the icons lie on, --bg the page (`panel` in light, `viewport_top` in dark), --line `border`, --fg `text`, --mut `text_dim`. */
:root{--bg:#fafbfc;--fg:#262a32;--card:#f6f7f9;--line:#d0d4dc;--mut:#6e7480;--sz:48px}
body.dark{--bg:#2e3440;--fg:#dee2e8;--card:#272b34;--line:#3f4552;--mut:#949ba8}
body{font:12px system-ui,sans-serif;margin:0;background:var(--bg);color:var(--fg)}
header{position:sticky;top:0;background:var(--card);border-bottom:1px solid var(--line);padding:6px 12px;z-index:5;display:flex;flex-wrap:wrap;gap:4px 10px;align-items:center}
h1{font-size:14px;margin:0}.sum{color:var(--mut)}
+3 -22
View File
@@ -98,10 +98,6 @@ pub(crate) fn breadcrumb(p: &egui::Painter, c: egui::Pos2, col: Color32) {
p.line_segment([pos2(c.x + 1.5, c.y), pos2(c.x - 1.5, c.y + 3.0)], s);
}
pub(crate) fn caret(p: &egui::Painter, c: egui::Pos2, col: Color32) {
p.add(egui::Shape::convex_polygon(vec![pos2(c.x - 3.5, c.y - 2.0), pos2(c.x + 3.5, c.y - 2.0), pos2(c.x, c.y + 2.5)], col, Stroke::NONE));
}
pub(crate) fn brand_mark(p: &egui::Painter, c: Pos2) {
let s = 20.0 / 412.217;
let m = |pts: &[(f32, f32)]| -> Vec<egui::Pos2> { pts.iter().map(|&(x, y)| pos2(c.x + (x - 178.5) * s, c.y + (y - 206.1) * s)).collect() };
@@ -109,7 +105,9 @@ pub(crate) fn brand_mark(p: &egui::Painter, c: Pos2) {
let face = |col: Color32, pts: &[(f32, f32)]| {
p.add(egui::Shape::convex_polygon(m(pts), col, Stroke::NONE));
};
let [light, mid, dark, edge_col] = crate::theme::Tokens::get().logo;
// The faces are separated by gaps that show the application bar, not by white lines.
let [light, mid, dark, _] = crate::theme::Tokens::get().logo;
let edge_col = crate::theme::Tokens::get().app_bar;
face(light, &[(3.5, 105.9), (177.0, 5.8), (263.4, 55.6), (263.8, 156.0), (177.0, 406.4)]);
face(light, &[(177.0, 206.1), (263.8, 156.0), (350.5, 206.2), (350.5, 306.3), (177.0, 406.4)]);
face(mid, &[(3.5, 105.9), (177.0, 206.1), (177.0, 406.4)]);
@@ -148,20 +146,3 @@ pub(crate) fn cross(p: &egui::Painter, r: Rect, s: Stroke) {
p.line_segment([r.left_top(), r.right_bottom()], s);
p.line_segment([r.right_top(), r.left_bottom()], s);
}
pub(crate) fn radial_centre(p: &egui::Painter, c: Pos2, direction: Option<(egui::Vec2, bool)>, t: crate::theme::Tokens) {
p.circle(c, 15.0, t.panel.gamma_multiply(0.92), Stroke::new(1.0, t.border));
if let Some((d, enabled)) = direction {
p.line_segment([c + d * 6.0, c + d * 15.0], Stroke::new(3.0, if enabled { t.accent } else { t.border }));
} else {
p.circle_filled(c, 3.0, t.text_dim);
}
}
pub(crate) fn disclosure(p: &egui::Painter, ar: Rect, open: bool, ink: Color32) {
let pts = if open {
vec![pos2(ar.left() + 2.0, ar.top() + 4.0), pos2(ar.right() - 2.0, ar.top() + 4.0), pos2(ar.center().x, ar.bottom() - 3.0)]
} else {
vec![pos2(ar.left() + 4.0, ar.top() + 2.0), pos2(ar.right() - 3.0, ar.center().y), pos2(ar.left() + 4.0, ar.bottom() - 2.0)]
};
p.add(egui::Shape::convex_polygon(pts, ink, Stroke::NONE));
}
+1 -1
View File
@@ -49,7 +49,7 @@ pub(crate) fn snap(painter: &egui::Painter, c: Pos2, tip: Option<Pos2>, kind: Sn
// What it snaps to (hovered), or which component it is (picked): a small label.
if let Some(tag) = tag {
let t = Tokens::get();
let font = egui::FontId::proportional(11.5);
let font = egui::FontId::proportional(crate::theme::Tokens::get().font_caption);
let g = painter.layout_no_wrap(tag.to_string(), font, t.text);
let at = c + vec2(12.0, 10.0);
let r = egui::Rect::from_min_size(at, g.size()).expand2(vec2(5.0, 2.0));
+57 -8
View File
@@ -1,6 +1,6 @@
//! Navigation drawing definitions. Camera projection and picking remain in the UI widgets.
use crate::theme::Tokens;
use egui::{Align2, Color32, FontId, Pos2, Rect, Shape, Stroke, pos2, vec2};
use egui::{Color32, FontId, Pos2, Rect, Shape, Stroke, pos2, vec2};
use solidcraft_engine::geom::{Ring, Vec3};
pub(crate) struct Facet {
pub(crate) direction: Vec3,
@@ -120,7 +120,7 @@ pub(crate) fn paint_cube(painter: &egui::Painter, faces: &[Facet], hovered: Opti
}
if labels {
for face in faces {
face_text(painter, face, t.cube_text, 14.0 * scale);
face_text(painter, face, t.cube_text, t.font_heading * scale);
}
}
}
@@ -148,7 +148,8 @@ pub(crate) fn curved_arrow(painter: &egui::Painter, at: Pos2, clockwise: bool, h
arrow(painter, *tip, vec2(-end.sin(), end.cos()) * (sign * scale), hover, t);
}
}
pub(crate) const AXIS_RADIUS: f32 = 11.0;
/// The pointer target around a compass end: larger than the drawn disc, so it is easy to hit.
pub(crate) const AXIS_RADIUS: f32 = 15.0;
pub(crate) fn axis_tip(painter: &egui::Painter, c: Pos2, name: &str, d: &Vec3, at: Pos2, depth: f64, hovered: bool, t: Tokens) {
let col = if d.x != 0.0 {
t.triad[0]
@@ -158,9 +159,57 @@ pub(crate) fn axis_tip(painter: &egui::Painter, c: Pos2, name: &str, d: &Vec3, a
t.triad[2]
};
let positive = !name.starts_with('-');
let ink = if depth < -0.01 { col.gamma_multiply(0.55) } else { col };
painter.line_segment([c, at], Stroke::new(if positive { 2.0 } else { 1.0 }, ink));
painter.circle_filled(at, AXIS_RADIUS, if hovered { t.accent_soft } else { t.overlay });
painter.circle_stroke(at, AXIS_RADIUS, Stroke::new(if hovered { 2.0 } else { 1.0 }, if hovered { t.accent } else { ink }));
painter.text(at, Align2::CENTER_CENTER, name.to_uppercase(), FontId::proportional(11.0), ink);
let light = t.panel.r() > 128;
// Positive shafts are the full axis colour; negative ones are a dimmed tint (and any end
// pointing away from the viewer dims further).
let ink = if !positive || depth < -0.01 { col.gamma_multiply(0.55) } else { col };
let radius = if positive { 11.0 } else { 4.8 };
let direction = (at - c).normalized();
let width = 3.6;
// A negative shaft stops at the outer edge of its ring; a positive one runs under its disc.
let end = if positive { at - direction * radius } else { at - direction * (radius + 1.2) };
painter.line_segment([c, end], Stroke::new(width, ink));
// The round cap sits at the origin; the other end is covered by the disc or ring.
painter.circle_filled(c, width / 2.0, ink);
if positive {
painter.circle_filled(at, radius, col);
}
// Negative tips are hollow: neither the axis shaft nor a fill covers their
// centres, so the translucent compass background shows through. A hovered tip of either
// kind turns fully white.
if hovered {
// Same outer size as the resting tip: a ring's outline is half its stroke outside.
painter.circle_filled(at, if positive { radius } else { radius + 1.2 }, t.on_accent);
// On the light theme a white disc vanishes into the compass, so it carries an outline in
// the axis colour, drawn inside the disc so it does not grow.
if light && positive {
painter.circle_stroke(at, radius - 1.0, Stroke::new(2.0, col));
}
}
if positive {
// On a hovered (white) disc the letter takes the axis colour.
let letter = if hovered { col } else { t.on_accent };
let label = name.to_uppercase();
let galley = painter.layout_no_wrap(label, crate::fonts::bold(crate::theme::Tokens::get().font_compass), letter);
let origin = at - galley.size() / 2.0;
let ppp = painter.ctx().pixels_per_point().max(0.5);
let origin = pos2((origin.x * ppp).round() / ppp, (origin.y * ppp).round() / ppp);
painter.galley(origin, galley, letter);
}
if !positive {
painter.circle_stroke(
at,
radius,
Stroke::new(
2.4,
if !hovered {
ink
} else if light {
col
} else {
t.on_accent
},
),
);
}
}
+1 -1
View File
@@ -11,7 +11,7 @@ pub(crate) fn paint(painter: &egui::Painter, p: Pos2) {
[(0.0, 0.0), (0.0, 17.0), (4.5, 13.0), (7.5, 20.0), (10.5, 18.8), (7.5, 12.0), (13.0, 12.0)].iter().map(|(x, y)| p + vec2(*x, *y)).collect();
painter.add(Shape::convex_polygon(pts.clone(), ic.agent, Stroke::NONE));
painter.add(Shape::closed_line(pts, Stroke::new(1.5, ic.highlight)));
let label = painter.layout_no_wrap("Agent".into(), FontId::proportional(11.0), ic.highlight);
let label = painter.layout_no_wrap("Agent".into(), FontId::proportional(crate::theme::Tokens::get().font_small), ic.highlight);
let r = egui::Rect::from_min_size(p + vec2(15.0, 18.0), label.size() + vec2(10.0, 4.0));
painter.rect_filled(r, 4.0, ic.agent);
painter.galley(r.min + vec2(5.0, 2.0), label, ic.highlight);
+390 -39
View File
File diff suppressed because it is too large Load Diff
+259 -39
View File
@@ -3,11 +3,14 @@
"1. Open GitHub draft below, then paste the report copied to your clipboard.": "1. Åbn GitHub-kladden nedenfor, og indsæt rapporten fra udklipsholderen.",
"1. Open GitHub draft below. Your title and report will be filled in.": "1. Åbn GitHub-kladden nedenfor. Titel og rapport udfyldes automatisk.",
"2. Attach your screenshot: copy and paste it, or save and upload the PNG.": "2. Vedhæft dit skærmbillede: Kopiér og indsæt det, eller gem og upload PNG-filen.",
"2D": "2D",
"3. Click Submit new issue on GitHub to send your report.": "3. Klik på Submit new issue på GitHub for at sende rapporten.",
"A new configuration, a copy of the active one": "Opret en ny konfiguration som en kopi af den aktive",
"AI": "AI",
"ARRANGE": "ARRANGER",
"About SolidCraft": "Om SolidCraft",
"Acceleration": "Acceleration",
"Access": "Adgang",
"Actions": "Handlinger",
"Active": "Aktiv",
"Active rule": "Aktiv regel",
"Add": "Tilføj",
@@ -15,6 +18,7 @@
"Add Mid Radius": "Tilføj mellemradius",
"Add Row": "Tilføj række",
"Add condition": "Tilføj betingelse",
"Add or edit rule": "Tilføj eller redigér regel",
"Against": "Mod",
"Alignment": "Justering",
"All Sketch Points": "Alle skitsepunkter",
@@ -28,8 +32,11 @@
"Applied Radius": "Anvendt radius",
"Apply To": "Anvend på",
"Apply to selection": "Anvend på markering",
"Apply your appearance first. Exports assigned appearances and materials created in this design.": "Anvend først dit udseende. Eksporterer tildelte udseender og materialer oprettet i dette design.",
"Arc length": "Buelængde",
"Area": "Areal",
"As-Built": "Som bygget",
"Assign Rule": "Tildel regel",
"Attachment": "Tilknytning",
"BLEND SURFACE": "OVERGANGSFLADE",
"BOUNDARY SURFACE": "AFGRÆNSET OVERFLADE",
@@ -37,12 +44,21 @@
"Bend Line Position": "Bukkelinjens placering",
"Bend Position": "Bukningens placering",
"Bend Radius": "Bukkeradius",
"Bend Table": "Bukketabel",
"Bends": "Bukninger",
"Bodies": "Emner",
"Body": "Emne",
"Boundary": "Afgrænsning",
"Bounding box": "Omsluttende boks",
"CHECK": "KONTROLLÉR",
"COMPONENTS": "KOMPONENTER",
"CONFIGURATIONS": "KONFIGURATIONER",
"CONSTRAIN": "BIND",
"CONTACT": "KONTAKT",
"CONVERT": "KONVERTÉR",
"CSV or JSON: updates existing parameters, adds new ones": "CSV eller JSON: Opdaterer eksisterende parametre og tilføjer nye",
"Capture All Positions": "Registrér alle positioner",
"Capture Pos.": "Registrér pos.",
"Catch Direction": "Indgrebsretning",
"Cells": "Celler",
"Center X": "Centrum X",
@@ -53,15 +69,19 @@
"Check collisions": "Kontrollér kollisioner",
"Checked": "Kontrolleret",
"Choose a new plane. Sketch geometry and dimensions stay unchanged in its two-dimensional coordinates.": "Vælg et nyt plan. Skitsegeometri og mål bevarer deres todimensionelle koordinater.",
"Choose…": "Vælg…",
"Circular": "Cirkulær",
"Circumference": "Omkreds",
"Close draft": "Luk kladde",
"Collisions": "Kollisioner",
"Colour": "Farve",
"Comment": "Kommentar",
"Component1": "Komponent1",
"Config Table": "Konfig.tabel",
"Configuration": "Konfiguration",
"Configuration rules": "Konfigurationsregler",
"Construction": "Konstruktionsgeometri",
"Contact": "Kontakt",
"Contact Sets": "Kontaktsæt",
"Contact sets": "Kontaktsæt",
"Continuity": "Kontinuitet",
"Copy Error Message": "Kopiér fejlmeddelelse",
@@ -79,15 +99,17 @@
"Cube": "Terning",
"Current orthographic view (1:1 mm)": "Aktuel visning uden perspektiv (1:1 mm)",
"Current perspective view": "Aktuel perspektivvisning",
"Curv. Comb": "Krumningskam",
"Curvature (G2)": "Krumningskontinuitet (G2)",
"Curvature change (G3)": "Ændring i krumning (G3)",
"Customize Toolbar": "Tilpas værktøjslinje",
"Delete": "Slet",
"Delete all": "Slet alle",
"Delete rule": "Slet regel",
"Delete the row": "Slet rækken",
"Delete the set": "Slet sættet",
"Delete this parameter": "Slet denne parameter",
"Density": "Densitet",
"Dependents": "Afhængige",
"Designs you open or save appear here.": "Designs, du åbner eller gemmer, vises her.",
"Determined by To Object. Choose Angle to edit the angular extent.": "Bestemmes af Til objekt. Vælg Vinkel for at ændre vinkeludstrækningen.",
"Direction": "Retning",
@@ -99,6 +121,7 @@
"Drive Joint": "Bevæg led",
"Duration": "Varighed",
"Edges": "Kanter",
"Edit": "Redigér",
"Edit Profile Sketch": "Redigér profilskitse",
"Enable rule": "Aktivér regel",
"Enabled": "Aktiveret",
@@ -107,43 +130,63 @@
"Explode": "Adskil",
"Explode Text": "Opløs tekst",
"Export Animation…": "Eksportér animation…",
"Export CSV…": "Eksportér CSV…",
"Export Current View as DXF": "Eksportér aktuel visning som DXF",
"Export Current View as DXF…": "Eksportér aktuel visning som DXF…",
"Export DXF": "Eksportér DXF",
"Export DXF…": "Eksportér DXF…",
"Export JSON…": "Eksportér JSON…",
"Export hidden edges as dashed lines": "Eksportér skjulte kanter som stiplede linjer",
"Expression": "Udtryk",
"Extend to meet": "Forlæng, til de mødes",
"Extents": "Udstrækning",
"FILES": "FILER",
"FLAT": "FLAD",
"Face": "Flade",
"Face attachment lost": "Forbindelsen til fladen er mistet",
"Faces": "Flader",
"Faces / edges / vertices": "Flader / kanter / hjørnepunkter",
"Fastener": "Befæstigelse",
"Favorites": "Favoritter",
"Favourite": "Favorit",
"Feature": "Feature",
"Files from many CAD programs have Y up, so they open lying on their back here (Z up).": "Filer fra mange CAD-programmer har Y opad og åbnes derfor liggende på ryggen her (Z opad).",
"Filter": "Filter",
"Find in Browser": "Find i modeltræ",
"Flat": "Flad",
"Finish": "Afslut",
"Flat Pattern": "Flad afvikling",
"Flat size": "Bredde mellem flader",
"Follow camera (Look At sketches)": "Følg kamera (Se på skitser)",
"Follow the camera (Look At sketches)": "Følg kameraet (Se på skitser)",
"Font": "Skrifttype",
"From Bodies": "Fra emner",
"From edges": "Fra kanter",
"Full Length": "Fuld længde",
"Gap": "Spalte",
"Grid": "Gitter",
"HISTORY": "HISTORIK",
"Height": "Højde",
"Hidden edges": "Skjulte kanter",
"Holes": "Huller",
"Holes (edges on them)": "Huller (kanterne omkring dem)",
"Home view": "Hjemmevisning",
"Horiz./Vert.": "Vandr./Lodr.",
"IMAGES": "BILLEDER",
"Import…": "Importér…",
"In This Design": "I dette design",
"In place (surfaces)": "På stedet (overflader)",
"In plane": "I planet",
"Inside": "Indvendigt",
"Interferes": "Overlapper",
"Isocurve": "Isokurve",
"Issue title": "Titel på issue",
"JOINTS": "LED",
"Joint Origin1": "Lednulpunkt1",
"Keep (points on it)": "Behold (punkter på fladen)",
"Keep Tools": "Behold værktøjsemner",
"Keep of the other surface": "Behold fra den anden overflade",
"LIBRARY": "BIBLIOTEK",
"LINKS": "LINKS",
"Later": "Senere",
"Leave it": "Lad det være",
"Legacy face attachment. Rebinding to a certified face creates an exact attachment.": "Ældre fladetilknytning. Ny tilknytning til en certificeret flade giver en præcis tilknytning.",
@@ -151,47 +194,70 @@
"Length units": "Længdeenheder",
"Letter Spacing": "Tegnafstand",
"Library": "Bibliotek",
"Library file": "Biblioteksfil",
"Line Spacing": "Linjeafstand",
"Local library": "Lokalt bibliotek",
"Loop": "Løkke",
"MANAGE": "ADMINISTRÉR",
"MATERIALS": "MATERIALER",
"MODEL": "MODEL",
"MOTION": "BEVÆGELSE",
"Margin": "Margen",
"Mass": "Masse",
"Mass Center": "Massecentrum",
"Matched restrictions check the final values.": "Matchede begrænsninger kontrollerer de endelige værdier.",
"Material": "Materiale",
"Max": "Maks.",
"Mid Radius": "Mellemradius",
"Middle double-click": "Dobbeltklik med midterste museknap",
"Min": "Min.",
"Mitre": "Gering",
"Model Parameters": "Modelparametre",
"Modeled": "Modelleret",
"More toolbar panels": "Flere værktøjspaneler",
"Motion": "Bevægelse",
"Move / Copy": "Flyt / Kopiér",
"Move Type": "Flytningstype",
"Move down": "Flyt ned",
"Move earlier": "Flyt tidligere",
"Move later": "Flyt senere",
"Move up": "Flyt op",
"Name": "Navn",
"Nearest": "Nærmeste",
"New": "Ny",
"New Comp.": "Ny komp.",
"New Sketch": "Ny skitse",
"New rule…": "Ny regel…",
"New study": "Ny analyse",
"No crash reports.": "Ingen nedbrudsrapporter.",
"No matching appearance presets.": "Ingen matchende udseendeforudindstillinger.",
"No matching appearances": "Ingen matchende udseender",
"No visible bodies to export.": "Der er ingen synlige emner at eksportere.",
"None": "Ingen",
"Normal to sketch": "Vinkelret på skitse",
"Not available yet": "Endnu ikke tilgængelig",
"Notch": "Udskæring",
"OUTPUT": "OUTPUT",
"Opacity": "Opacitet",
"Open GitHub draft": "Åbn GitHub-kladde",
"Open local libraries in the desktop app. You can export this design's library here.": "Åbn lokale biblioteker i desktopappen. Du kan eksportere dette designs bibliotek her.",
"Open-source parametric 3D CAD in Rust. Licensed MIT OR Apache-2.0.": "Parametrisk 3D-CAD i Rust med åben kildekode. Licenseret under MIT OR Apache-2.0.",
"Operator": "Operator",
"Other side": "Anden side",
"Other surface (mutual trim)": "Anden overflade (trim begge)",
"Outside": "Udvendigt",
"Overall length": "Samlet længde",
"Parameter values": "Parameterværdier",
"Param. Graph": "Param.graf",
"Parameter": "Parameter",
"Parts": "Dele",
"Pass through": "Gå igennem",
"Path": "Bane",
"Pattern": "Mønster",
"Per Edge": "Pr. kant",
"Pick the tool bodies": "Vælg værktøjsemnerne",
"Picked": "Valgt",
"Picked point": "Valgt punkt",
"Place at the origin instead": "Placér ved nulpunktet i stedet",
"Plastic Rules": "Plastregler",
"Playback": "Afspilning",
"Plot": "Diagram",
"Position": "Position",
@@ -202,10 +268,11 @@
"Preview Row": "Forhåndsvis række",
"Preview leaves the current design unchanged.": "Forhåndsvisning ændrer ikke det aktuelle design.",
"Profiles/Curves": "Profiler/kurver",
"Projection": "Projektion",
"Quantity": "Antal",
"RECENT": "SENESTE",
"REFERENCE": "REFERENCE",
"RULED SURFACE": "REGELFLADE",
"RULES": "REGLER",
"Radius Fit": "Tilpasning af radius",
"Ratio": "Forhold",
"Reattach selected face": "Tilknyt den valgte flade igen",
@@ -223,6 +290,8 @@
"Remove this point": "Fjern dette punkt",
"Report a bug — review a screenshot and open a GitHub draft": "Rapportér en fejl — gennemgå et skærmbillede, og åbn en GitHub-kladde",
"Reset Toolbar": "Nulstil værktøjslinje",
"Resize Part": "Ændr størrelse på del",
"Restore View": "Gendan visning",
"Restore outer boundary": "Gendan ydre afgrænsning",
"Restore the inherited component or physical material appearance": "Gendan udseendet fra komponenten eller det fysiske materiale",
"Result": "Resultat",
@@ -231,18 +300,27 @@
"Reverse Direction": "Vend retning",
"Revert": "Tilbagefør",
"Revert All Positions": "Tilbagefør alle positioner",
"Revert Pos.": "Fortryd pos.",
"Revert Position": "Tilbagefør position",
"Review the report and screenshot before opening GitHub.": "Gennemgå rapport og skærmbillede, før du åbner GitHub.",
"Rim Edge": "Randkant",
"Roll History Marker Here": "Flyt tilbagerulningsmarkør hertil",
"Round": "Rund",
"Rule": "Regel",
"Rule name": "Regelnavn",
"Rules run from top to bottom. Matched drives change effective values.": "Regler køres oppefra og ned. Matchede drivere ændrer de effektive værdier.",
"SECTION": "SNIT",
"SURFACE QUALITY": "OVERFLADEKVALITET",
"Save Rule": "Gem regel",
"Save View": "Gem visning",
"Save the study as an animated GIF, seen as the viewport shows it": "Gem undersøgelsen som en animeret GIF med samme visning som i modelvinduet",
"Saved preview. Geometry is being checked before editing.": "Gemt forhåndsvisning. Geometrien kontrolleres før redigering.",
"Scale factor": "Skaleringsfaktor",
"Scene": "Scene",
"Search": "Søg",
"Search commands": "Søg efter kommandoer",
"Search this library": "Søg i dette bibliotek",
"Section": "Snit",
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "Vælg andet: Hold venstre museknap nede uden at flytte markøren på modellen (eller højreklik › Vælg andet…) for at vise alt under markøren. Klik igen samme sted for at vælge næste objekt. Dobbeltklik på den midterste museknap for at gå til Hjem (Indstillinger › Navigation).",
"Select bodies (a face picks its body)": "Vælg emner (en flade vælger sit emne)",
"Select bodies or faces instead": "Vælg emner eller flader i stedet",
@@ -250,6 +328,7 @@
"Semantic faces share their appearance across instances.": "Semantiske flader deler udseende på tværs af instanser.",
"Send your report on GitHub": "Send din rapport på GitHub",
"Shape": "Form",
"Sheet Rules": "Pladeregler",
"Show Agent Cursor": "Vis agentmarkør",
"Show in folder": "Vis i mappe",
"Show the pointer an AI agent moves (#35)": "Vis markøren, som en AI-agent bevæger (#35)",
@@ -263,6 +342,7 @@
"Sketch points": "Skitsepunkter",
"Slot width": "Slidsbredde",
"SolidCraft closed before these designs were saved. Their last autosave can be opened.": "SolidCraft lukkede, før disse designs blev gemt. Den seneste automatiske lagring kan åbnes.",
"Specialise": "Specialisér",
"Speed": "Hastighed",
"Stand it up": "Stil op",
"Stand it up: a quarter turn about X, as one Move feature you can undo.": "Stil op: En kvart omdrejning om X som én flyttefeature, der kan fortrydes.",
@@ -301,17 +381,19 @@
"Turn off section analysis before exporting the view.": "Slå snitanalyse fra, før visningen eksporteres.",
"Type": "Type",
"UNTRIM": "FJERN BESKÆRING",
"Unit": "Enhed",
"Updating the preview": "Opdaterer forhåndsvisningen",
"Use active author placement": "Brug den aktive komponents placering",
"Use fixed plane": "Brug fast plan",
"Use saved fixed plane": "Brug gemt fast plan",
"Use selected face as fixed plane": "Brug valgt flade som fast plan",
"User Parameters": "Brugerparametre",
"VIEW": "VISNING",
"VIEWS": "VISNINGER",
"Value": "Værdi",
"Velocity": "Hastighed",
"Vertices": "Hjørnepunkter",
"View navigator": "Visningsnavigator",
"Visible bodies only. Curves use line segments (0.01 mm tolerance).": "Kun synlige emner. Kurver består af linjestykker (tolerance: 0,01 mm).",
"Volume": "Volumen",
"Wall": "Væg",
"Wall Location": "Vægplacering",
@@ -319,6 +401,13 @@
"When": "Når",
"Width": "Bredde",
"Will fail": "Vil fejle",
"X angle": "X-vinkel",
"X distance": "X-afstand",
"Y angle": "Y-vinkel",
"Y distance": "Y-afstand",
"Z angle": "Z-vinkel",
"Z distance": "Z-afstand",
"Zebra": "Zebra",
"angle, height": "vinkel, højde",
"appearance.dielectric_ior": "Dielektrisk brydningsindeks",
"appearance.metallic": "Metallisk",
@@ -327,7 +416,13 @@
"appearance.roughness": "Ruhed",
"appearance.surface_response": "Overfladens reaktion på lys",
"browser.body_name": "Emne{number}",
"browser.calibrate_title": "Kalibrér: {name}",
"browser.capture_many": "{count} komponenter er flyttet, men deres positioner er ikke registreret.",
"browser.capture_note": "Registrér beholder den nye position; Fortryd sætter den tilbage.",
"browser.capture_one": "En komponent er flyttet, men dens position er ikke registreret.",
"browser.component_name": "Komponent{number}",
"browser.edit_canvas_title": "Redigér lærred: {name}",
"browser.move_title": "Flyt: {name}",
"browser.parameters": "Parametre ({count})",
"browser.precision": "Præcision: {precision}",
"browser.sketch_name": "Skitse{number}",
@@ -455,6 +550,7 @@
"cmd.joint_drive": "Bevæg led",
"cmd.joint_edit": "Redigér led",
"cmd.joint_gear": "Tandhjul",
"cmd.joint_inspect": "Undersøg led",
"cmd.joint_limits": "Bevægelsesgrænser for led",
"cmd.joint_list": "Vis led",
"cmd.joint_motion_link": "Bevægelseskobling",
@@ -613,6 +709,7 @@
"cmd.sketch_spline_fit_point": "Spline gennem punkter",
"cmd.sketch_spun_profile": "Omdrejningsprofil",
"cmd.sketch_toggle_driven": "Skift mellem styrende og afledt mål",
"cmd.sketch_trim_path": "Trim langs bane",
"cmd.solid_align": "Justér",
"cmd.solid_boundary_fill": "Afgrænset udfyldning",
"cmd.solid_bounding_solid": "Omsluttende emne",
@@ -663,7 +760,15 @@
"cmd.view_navigate": "Navigér i visning",
"cmd.view_rename": "Omdøb navngiven visning",
"cmd.view_save": "Ny navngiven visning",
"command name": "kommandonavn",
"color.copy": "Klik for at kopiere farveværdier",
"color.edit": "Klik for at redigere farve",
"color.gamma": "Viser farveværdier i 0-255 gammarum",
"color.hue": "Farvetone",
"color.linear": "Viser farveværdier i 0-1 lineært rum",
"color.saturation_value": "Mætning og værdi",
"color.selected": "Valgt farve",
"contact.components_count": "{count} komponenter",
"contact.enabled_label": "aktiveret",
"coupling.gear_1": "Tandhjul 1",
"coupling.gear_1_pick": "Tandhjul 1 (valgfrit)",
"coupling.gear_2": "Tandhjul 2",
@@ -680,6 +785,21 @@
"coupling.rack": "Tandstang",
"coupling.radius_1": "Radius 1",
"coupling.radius_2": "Radius 2",
"crash.announce": "SolidCraft blev lukket uventet eller stødte på en intern fejl sidste gang. En nedbrudsrapport er gemt (Hjælp > Nedbrudsrapporter…).",
"crash.copy_folder": "Kopiér mappesti",
"crash.copy_path": "Kopiér sti",
"crash.deleted": "Slettede {count} rapport(er).",
"crash.detected_time": "Registreret {time} UTC",
"crash.intro": "Nedbrudsrapporter bliver på denne computer og sendes aldrig nogen steder hen. Rapportér… åbner en kladde, som du gennemgår, før noget deles.",
"crash.kind_command": "Intern fejl i en kommando",
"crash.kind_internal": "Intern fejl",
"crash.kind_kernel": "Kernefejl (genoprettet)",
"crash.kind_unclean": "Lukket uventet",
"crash.previous_session": "En tidligere SolidCraft-session sluttede uventet eller stødte på en intern fejl. Rapporter fra uventede afslutninger registreres ved opstart. Se Hjælp > Nedbrudsrapporter… for detaljer.",
"crash.replayable": "kan afspilles",
"crash.report": "Rapportér…",
"crash.unclean_explanation": "Registreret ved en senere opstart; det nøjagtige afslutningstidspunkt blev ikke noteret. Et systemnedlukning eller tvunget stop kan også give en uventet afslutning.",
"delete.and_more": "… og {count} mere",
"dialog.bodies_count": "Antal emner: {count}",
"dialog.clearance_invalid": "afstanden skal være 0 eller større",
"dialog.compute": "Beregn",
@@ -687,6 +807,8 @@
"dialog.computing": "Beregner… {seconds} s",
"dialog.could_not_be_checked": "kunne ikke kontrolleres",
"dialog.custom_axis": "↖ Brugerdefineret akse",
"dialog.dimension_insert_hint": "Klik på et andet styrende mål for at indsætte dets navn",
"dialog.dimension_name": "Mål: {name}",
"dialog.edit_title": "REDIGÉR {title}",
"dialog.select": "↖ Vælg",
"dialog.selected_count": "↖ Valgte: {count}",
@@ -710,9 +832,30 @@
"dxf.progress.write": "Skriver DXF (5/5)",
"dxf.tolerance": "Kurvetolerance",
"dxf.tolerance_help": "Kun synlige emner. Kurver består af rette linjestykker. En mindre tolerance giver finere kurver og kræver mere tid og hukommelse.",
"expression, e.g. 25 mm": "udtryk, f.eks. 25 mm",
"file_load.built": "Bygget indtil videre: {built} af {total} features.",
"file_load.converting": "Konverterer solid {done} af {total}",
"file_load.inserting": "Indsætter geometri",
"file_load.opening": "Åbner design",
"file_load.preparing_thumbnail": "Forbereder miniaturebillede...",
"file_load.preparing_view": "Forbereder visningen...",
"file_load.preview_note": "Gemt forhåndsvisning. Geometrien kontrolleres, før du kan redigere.",
"file_load.reading": "Læser filen...",
"file_load.reading_percent": "Læser filen... {percent}%",
"file_load.rebuilding": "Genopbygger feature {done} af {total}",
"file_load.recovering": "Genopretter design",
"file_load.stopping": "Venter på, at den forrige handling stopper...",
"file_load.waiting": "Venter på filhandling",
"fits the face, e.g. M8": "passer til fladen, f.eks. M8",
"from the rule": "fra reglen",
"help.apache": "Apache-licens 2.0",
"help.attributions": "Tilskrivninger (ATTRIBUTION.md)",
"help.build": "Version {version} · commit {commit} · bygget {date}",
"help.font_details": "Brugerfladen bruger egui's indbyggede skrifttyper (Ubuntu Light og Hack, under Ubuntu Font Licence og MIT/Bitstream Vera-licensen; Noto Emoji og emoji-ikonskrifttypen, under OFL og MIT). Skitsetekst bruger Liberation Sans (SIL OFL 1.1). Crafting Apps' valgfrie skrifttypepakke, craft-fonts (BIZ UDPGothic, Shippori Mincho, BIZ UDMincho, Noto Sans CJK SC, Noto Sans Arabic; SIL OFL 1.1), er ikke inkluderet i SolidCraft-builds.",
"help.fonts": "Skrifttyper",
"help.licences": "Licenser",
"help.mit": "MIT-licens",
"help.notice": "NOTICE",
"help.third_party": "Tredjepartslicenser",
"home.documentation": "Dokumentation",
"home.in_this_browser": "i denne webbrowser",
"home.missing": "mangler",
@@ -738,9 +881,30 @@
"install.menu": "Installér app…",
"install.steps_intro": "Safari installerer webapps via sine egne menuer:",
"install.title": "Installér SolidCraft",
"issue.body_label": "Rapporttekst",
"issue.note_image_copied": "Indsæt billedet i GitHub-kladden, eller gem PNG-filen, hvis billedkopiering ikke er tilgængelig.",
"issue.note_opened": "Gennemgå GitHub-kladden, og vedhæft skærmbilledet. GitHubs knap Submit new issue udgiver den.",
"issue.note_opened_copied": "Rapport kopieret. Indsæt den i GitHub-kladden, vedhæft skærmbilledet, og klik derefter på Submit new issue på GitHub.",
"issue.note_removed": "Skærmbillede fjernet fra denne kladde.",
"issue.note_report_copied": "Rapport kopieret. Indsæt den i GitHub-kladden, hvis det er nødvendigt.",
"issue.note_save_failed": "Skærmbilledet kunne ikke gemmes: {error}",
"issue.note_saved": "Skærmbillede gemt. Vedhæft PNG-filen i GitHub-kladden.",
"issue.save_screenshot": "Gem skærmbillede…",
"joint.from_bodies_hint": "Hvert valgt emne flyttes til sin egen nye komponent og bliver på sin plads.",
"joint.one_body": "Begge valg er på {body}. Vælg det andet på et andet emne.",
"joint.one_component": "Disse emner tilhører samme komponent: {bodies}. Et led bevæger én komponent i forhold til en anden, så hvert emne skal have sin egen komponent.",
"keymap.command_id": "Kommando-ID",
"keymap.empty": "Ingen matchende kommandoer",
"keymap.use_anyway": "Brug {key} her alligevel",
"manage.appearance_hint": "Vælg et udseende til markeringen, eller træk en farveprøve over på et emne eller en flade.",
"manage.assign_hint": "Vælg emner, og vælg deres plastdesignregel.",
"manage.config_hint": "Redigér konfigurationsrækker, kolonner og regler, og aktivér eller forhåndsvis derefter en række.",
"manage.empty": "Ingen matchende rækker.",
"manage.material_hint": "Vælg emner, og vælg deres fysiske materiale.",
"manage.parameters_hint": "Redigér udtryk og enheder. Ændringer anvendes ved Enter eller når du forlader et felt.",
"manage.rules_hint": "Vælg en regel at redigere, eller opret en ny regel. Bekræft for at gemme reglen.",
"measure.at": "Ved",
"measure.item": "Element {number}",
"messages.ago_h": "For {n} timer siden",
"messages.ago_min": "For {n} min. siden",
"messages.ago_s": "For {n} sek. siden",
@@ -752,8 +916,16 @@
"messages.title": "Beskeder",
"motion.checked_to": "Kun kontrolleret til trin {step}: Kontrollen nåede sin beregningsgrænse",
"motion.collision": "{a} · {b}: trin {steps}",
"motion.delete_key": "Slet animationspunkt",
"motion.duration_hint": "sekunder (tomt: pr. trin)",
"motion.key_here": "Sæt punkt her",
"motion.key_here_hint": "Tilføj et animationspunkt i dette trin med ledets aktuelle værdi",
"motion.no_collisions": "Ingen kollisioner under analysen",
"motion.step_back": "Trin tilbage",
"motion.step_forward": "Trin frem",
"motion.to_end": "Til slutningen",
"motion.to_start": "Til starten",
"motion.track_name": "Spor {number}",
"nav.touchpad.help": "Hold Shift nede, og bevæg markøren for at panorere; hold Alt/Option nede, og bevæg den for at rotere visningen. Ingen museknap behøver at være trykket ned. Lodret rulning zoomer.",
"nav.touchpad.label": "Pegefelt",
"nav.touchpad.orbit": "Rotér visning (hold Alt/Option nede og bevæg markøren; ingen museknap)",
@@ -762,13 +934,13 @@
"nav.touchpad.zoom": "Zoom (lodret rulning eller knibebevægelse)",
"needs two joints": "kræver to led",
"new configuration": "ny konfiguration",
"new name": "nyt navn",
"new parameter": "ny parameter",
"no hole": "intet hul",
"no interference": "intet overlap",
"no joint that moves yet": "endnu intet bevægeligt led",
"no picture": "intet billede",
"none": "ingen",
"parts.automatic_name": "automatisk",
"pattern.alignment_2": "Justering 2",
"pattern.alignment_hint": "Antal omfatter originalen. Centrér fordeler kopierne ligeligt; Vend vælger siden med den ekstra kopi ved et lige samlet antal.",
"pattern.direction_1": "Retning 1",
@@ -776,12 +948,19 @@
"pattern.flip_direction": "Vend retning",
"pick a hole to seat it, or OK places it at the origin": "vælg et hul til placeringen, eller tryk OK for at placere ved nulpunktet",
"pick one or two vertices, edges or faces": "vælg et eller to hjørnepunkter, kanter eller flader",
"prefs.cpu": "CPU (software)",
"prefs.minutes_suffix": " min",
"properties.title": "Egenskaber: {name}",
"recovery.days_ago": "for {count} dage siden",
"recovery.detail": "{file} · {features} features · autogemt {when}",
"recovery.hours_ago": "for {count} t siden",
"recovery.just_now": "lige nu",
"recovery.min_ago": "for {count} min siden",
"recovery.never_saved": "aldrig gemt",
"render.background": "Scenebaggrund",
"render.fit": "Tilpas til produkt",
"render.image_cancel": "Annullér rendering",
"render.image_cancelled": "Rendering annulleret",
"render.image_cancelling": "Annullerer",
"render.image_check": "Kontrollér, om billedet kan renderes",
"render.image_completed": "Billedet er klar til eksport",
"render.image_encoding": "Gemmer som PNG",
"render.image_export": "Eksportér PNG",
@@ -828,6 +1007,19 @@
"sample.plate.name": "Eksempelplade",
"sample.vents.about": "Slidser i et rektangulært mønster: Rækker, kolonner og deling er parametre",
"sample.vents.name": "Ventilationsplade",
"search.command_keys": "Enter kør Shift+Enter kør og hold åben Esc luk",
"search.commands": "Kommandoer",
"search.design": "I dette design",
"search.feature": "Feature",
"search.invalid": "Ugyldig",
"search.keys": "↑ ↓ vælg Enter kør Tab udfyld navn Esc luk",
"search.no_match": "Ingen kommando matcher '{query}'",
"search.parameter": "Parameter",
"search.recent": "Seneste",
"search.run": "Kør kommando",
"search.suggest": "Mente du {label}?",
"search.try": "Skriv et kommandonavn, en feature i dette design eller en parameter. Du kører en kommando med værdier ved at skrive dens id og et JSON-objekt: {example}",
"search.valid": "Gyldig",
"settings.display_quality_note": "Finere niveauer viser krumme flader og kanter mere jævnt og tager længere tid at opbygge mesh til. Kun modelvisningen ændres, aldrig designet eller eksporten.",
"settings.frame_rate_note": "Viser billeder pr. sekund nederst til højre i modelvisningen. Når dette er slået til, gentegnes visningen løbende.",
"settings.measure_axis_distances": "Vis X/Y/Z-afstande",
@@ -838,17 +1030,30 @@
"settings.reset_intro": "Disse indstillinger virker ikke på denne computer og ville forhindre SolidCraft i at starte. De er derfor ændret:",
"settings.reset_title": "Indstillinger blev ændret",
"settings.supersampling_note": "Tegner visningen med højere opløsning og nedskalerer den: Jævnere kanter, linjer og gitter på skærme under 4K. Det kræver GPU-tid: ca. 2,25× så mange pixels ved 1,5× og 4× ved 2×.",
"shortcut.json_error": "parametre skal være JSON: {error}",
"shortcut.no_command": "ingen kommando `{id}`; teksten før JSON skal være et kommando-id",
"shortcut.pin": "Fastgør øverst i S-boksen",
"shortcut.unpin": "Frigør",
"sketch.degrees_of_freedom": "Frihedsgrader: {count}",
"sketch.dof_left": "{count} frihedsgrader tilbage",
"sketch.dof_left_one": "{count} frihedsgrad tilbage",
"sketch.draw_curves": "Tegn som {linetype}",
"sketch.make_selection": "Konvertér markeringen til {linetype}",
"sketch.open_ends": "Åbne ender: {count}",
"sketch.untied_ends": "Ender uden binding: {count}",
"sketch_panel.attached": "Tilknyttet {body} / {path}",
"sketch_panel.author": "Forfatterplacering: {path}",
"sketch_panel.dimension_hint": "Angiv en værdi eller et udtryk, og tryk derefter på OK.",
"sketch_panel.move_hint": "Angiv afstande og en vinkel, og tryk derefter på OK.",
"sketch_panel.redefine_title": "{command}: {name}",
"sketch_panel.sheet_attachment": "Tilknyttet en plan flade på et pladeemne; den følger panelet, når pladen ændres.",
"sketch_panel.text_hint": "Redigér teksten og dens placering, og tryk derefter på OK.",
"sketch_panel.use_plane": "Brug planet {plane}",
"sketch_text.align_center": "Centreret",
"sketch_text.align_left": "Venstre",
"sketch_text.align_right": "Højre",
"sketch_text.bold": "Fed",
"sketch_text.builtin_font": "{family} (indbygget)",
"sketch_text.font_search": "Søg efter skrifttyper",
"sketch_text.italic": "Kursiv",
"sketch_text.loading_fonts": "Søger efter installerede skrifttyper…",
"sketch_text.on_path": "Teksten følger sin bane: Kurven bestemmer placeringen",
@@ -869,6 +1074,7 @@
"timeline.ungroup": "Fjern featuremapper",
"to the face": "til fladen",
"to the next face": "til næste flade",
"toolbar.customize_note": "Fanen {tab}. Markér en kommando for at give den en værktøjslinjeknap. Du kan også trække en række fra et panels rullemenu over på værktøjslinjen, trække en knap af værktøjslinjen for at fjerne den eller bruge nålen på en rullemenurække.",
"toolbar.image": "BILLEDE",
"toolbar.render": "RENDERING",
"toolbar.render_scene": "SCENE",
@@ -877,11 +1083,11 @@
"trace.stopped_by_loop": "Slutter ved prøve {sample}: Løkken gennem {joint} kan ikke lukkes yderligere",
"ui.1_degree_of_freedom": "1 frihedsgrad",
"ui.3d_mouse_speed": "3D-musens hastighed",
"ui.3d_mouse_windows": "3D-mus",
"ui.3d_sketch": "3D-skitse",
"ui.about": "Om",
"ui.activate": "Aktivér",
"ui.active_component": "Aktiv komponent",
"ui.add_configuration": "Tilføj konfiguration",
"ui.add_parameter": "Tilføj parameter",
"ui.agent_chat": "Agentchat",
"ui.agent_chat_add": "Vedhæft filer",
"ui.agent_chat_add_file": "Vælg en fil…",
@@ -923,6 +1129,7 @@
"ui.agent_chat_file_error": "{name}: {error}",
"ui.agent_chat_file_heic": "{name}: HEIC-billeder kan ikke læses her; eksportér først som JPEG eller PNG",
"ui.agent_chat_file_importing": "Importerer i designet…",
"ui.agent_chat_file_kinds": "{name}: Billeder (PNG, JPEG, GIF, WebP, BMP, TIFF), tekstfiler (også SVG) og CAD-filer (STEP, IGES, FreeCAD, Rhino, STL, 3MF, OBJ, glTF) kan vedhæftes",
"ui.agent_chat_file_loading": "Læser filen…",
"ui.agent_chat_file_not_a_file": "{name}: Ikke en fil",
"ui.agent_chat_file_not_a_picture": "{name}: Ikke et billede",
@@ -945,7 +1152,7 @@
"ui.agent_chat_model_tip": "Modellen, som agenten bruger. Dit valg huskes for denne agent.",
"ui.agent_chat_new": "Ny chat",
"ui.agent_chat_new_confirm": "Start en ny chat? Den aktuelle chat ryddes.",
"ui.agent_chat_new_yes": "Ny chat",
"ui.agent_chat_next_picture": "Næste billede",
"ui.agent_chat_no_cli": "solidcraft-cli blev ikke fundet ved siden af appen, så agenten kan ikke få adgang til designet.",
"ui.agent_chat_no_control": "Agentchattens endepunkt startede ikke, så agenten kan ikke få adgang til designet.",
"ui.agent_chat_no_images": "Denne agent kan ikke modtage billeder",
@@ -961,6 +1168,7 @@
"ui.agent_chat_placeholder": "Bed agenten om at ændre designet…",
"ui.agent_chat_placeholder_busy": "Sæt en besked i kø til agenten er færdig (Ctrl+Enter: send nu)",
"ui.agent_chat_pop_out": "Åbn i eget vindue, som du kan flytte til en anden skærm",
"ui.agent_chat_previous_picture": "Forrige billede",
"ui.agent_chat_privacy": "Intet sendes andre steder end til den agent, du kører, under din egen konto.",
"ui.agent_chat_privacy_note": "SolidCraft sender intet nogen steder: Agenten, du kører, kommunikerer med sin egen tjeneste under din egen konto. SolidCraft ser eller gemmer aldrig en nøgle eller adgangskode.",
"ui.agent_chat_queue": "Sæt i kø",
@@ -1016,6 +1224,7 @@
"ui.ai_bad_url": "Angiv en URL, der begynder med http:// eller https://",
"ui.ai_base_url": "Basis-URL",
"ui.ai_button": "AI",
"ui.ai_command_hint": "my-agent --acp",
"ui.ai_copy_suffix": "kopi",
"ui.ai_custom_url": "Brugerdefineret URL",
"ui.ai_default": "Standard",
@@ -1036,8 +1245,10 @@
"ui.ai_mode_edits_note": "Modelleringskommandoer kører uden at spørge; lagring, åbning, eksport og andre filhandlinger eller destruktive handlinger kræver stadig godkendelse.",
"ui.ai_mode_global": "Som angivet ovenfor",
"ui.ai_model": "Model",
"ui.ai_model_hint": "qwen3:14b",
"ui.ai_modes_scope": "Disse indstillinger gælder for SolidCrafts værktøjer. Agentens egne værktøjer (kommandoskal og filredigering) følger dens egen tilstand, som du vælger øverst i chatpanelet, hvis agenten har flere tilstande.",
"ui.ai_name": "Navn",
"ui.ai_name_hint": "Min agent",
"ui.ai_need_command": "Angiv kommandoen, der starter agenten",
"ui.ai_need_model": "Angiv modelnavnet",
"ui.ai_no_answer": "Agenten svarede ikke i tide",
@@ -1068,6 +1279,7 @@
"ui.ai_toolbar_button": "Vis knap på værktøjslinjen",
"ui.ai_toolbar_button_note": "AI-knappen i højre ende af værktøjslinjen. Ctrl+Shift+A og menuen Vis åbner stadig panelet.",
"ui.ai_undo_tip": "Fortryd alt, agenten ændrede for denne besked, som ét trin",
"ui.ai_url_hint": "https://example.com/v1",
"ui.all": "Alle",
"ui.ambient_occlusion": "Ambient occlusion",
"ui.ambient_occlusion_help": "Bløde skygger, hvor uigennemsigtige overflader mødes. Kræver GPU-visning.",
@@ -1081,7 +1293,8 @@
"ui.anti_aliasing": "Kantudjævning",
"ui.apply": "Anvend",
"ui.arc": "Bue",
"ui.assemble": "Samling",
"ui.assistant": "Assistent",
"ui.assistant_waiting": "{count} godkendelser venter",
"ui.autosave_every": "Gem automatisk hver",
"ui.axes": "Akser",
"ui.axis": "Akse",
@@ -1092,6 +1305,11 @@
"ui.bodies_all_if_none": "Emner (alle, hvis ingen er valgt)",
"ui.box": "Boks",
"ui.break": "Bryd",
"ui.browser_bodies_one": "{count} emne",
"ui.browser_bodies_other": "{count} emner",
"ui.browser_collapse": "Skjul modeltræet",
"ui.browser_expand": "Udvid modeltræet",
"ui.browser_title": "Modeltræ",
"ui.calibrate": "Kalibrér",
"ui.cancel": "Annullér",
"ui.canvases": "Referencebilleder",
@@ -1101,7 +1319,6 @@
"ui.chamfer": "Affasning",
"ui.chamfer_type": "Affasningstype",
"ui.change_workspace": "Skift arbejdsområde",
"ui.chord_length": "Kordelængde",
"ui.circle": "Cirkel",
"ui.circular_pattern": "Cirkulært mønster",
"ui.clear": "Ryd",
@@ -1134,11 +1351,9 @@
"ui.command_done": "{command} udført",
"ui.command_palette_hint": "Skriv en kommando, f.eks. {example} — eller tryk S for at søge",
"ui.concentric": "Koncentrisk",
"ui.configurations": "Konfigurationer",
"ui.conic_curve": "Keglesnitskurve",
"ui.constant": "Konstant",
"ui.constraints_conflict": "Bindinger er i konflikt",
"ui.construct": "Konstruktion",
"ui.construction": "Konstruktionsgeometri",
"ui.context_menu": "Genvejsmenu",
"ui.continuity_g0": "G0-knæk",
@@ -1164,7 +1379,6 @@
"ui.cylinder": "Cylinder",
"ui.dark": "Mørk",
"ui.default_modelling_orientation": "Standardorientering for modellering",
"ui.degrees_of_freedom": "frihedsgrader",
"ui.delete": "Slet",
"ui.depth": "Dybde",
"ui.design": "Design",
@@ -1206,7 +1420,6 @@
"ui.equivalent_radius": "Ækvivalent radius",
"ui.expand": "Fold ud",
"ui.exportDxf": "Eksportér DXF…",
"ui.export_ellipsis": "Eksportér...",
"ui.export_ellipsis_keymap": "Eksportér…",
"ui.export_keymap_tip": "Gem tastaturgenveje, favoritter i S-menuen og værktøjslinjen i en fil",
"ui.extend": "Forlæng",
@@ -1230,6 +1443,7 @@
"ui.frame_rate": "Billedfrekvens",
"ui.front": "Forfra",
"ui.fully_constrained": "Fuldt bundet",
"ui.function": "Funktion",
"ui.general": "Generelt",
"ui.graphics": "Grafik",
"ui.graphics_custom": "Brugerdefineret",
@@ -1296,7 +1510,6 @@
"ui.heading_sheet_metal": "PLADE",
"ui.heading_shell": "UDHULNING",
"ui.heading_sketch": "SKITSE",
"ui.heading_sketch_palette": "SKITSEPALETTE",
"ui.heading_sketch_scale": "SKITSESKALERING",
"ui.heading_solid": "MASSIVT EMNE",
"ui.heading_sphere": "KUGLE",
@@ -1315,6 +1528,7 @@
"ui.hideOrigin": "Skjul nulpunkt",
"ui.hidePlanes": "Skjul planer",
"ui.hideProfile": "Skjul profil",
"ui.history_title": "Historik",
"ui.hole": "Hul",
"ui.hole_tap_type": "Hullets gevindtype",
"ui.hole_type": "Hultype",
@@ -1334,7 +1548,6 @@
"ui.length_decimals": "Længdedecimaler",
"ui.light": "Lys",
"ui.line": "Linje",
"ui.linetype": "Linjetype",
"ui.lock": "Lås",
"ui.loft": "Loft",
"ui.look_at": "Se på",
@@ -1368,10 +1581,8 @@
"ui.mesh_format_3mf": "3MF",
"ui.mesh_format_3mf_tagged": "3MF med flademærker",
"ui.mesh_format_stl": "STL",
"ui.midpoint": "Midtpunkt",
"ui.mirror": "Spejling",
"ui.mirror_line": "Spejlingslinje",
"ui.modify": "Redigér",
"ui.move": "Flyt",
"ui.multiple": "Flere",
"ui.named_views": "Navngivne visninger",
@@ -1392,7 +1603,6 @@
"ui.offset_plane": "Forskudt plan",
"ui.one_side": "Én side",
"ui.open": "Åbn",
"ui.open_ellipsis": "Åbn...",
"ui.open_ends_tip": "Berører intet",
"ui.open_preferences": "Åbn indstillinger",
"ui.open_recent": "Åbn seneste",
@@ -1402,8 +1612,9 @@
"ui.orbit_around": "Rotér omkring",
"ui.orientation": "Orientering",
"ui.origin": "Nulpunkt",
"ui.palette_points": "Punkter",
"ui.palette_profile": "Profil",
"ui.palette_construction_mode": "Konstruktionstilstand (X)",
"ui.palette_more": "Mere",
"ui.palette_sketch_options": "Skitseindstillinger",
"ui.pan": "Panorér",
"ui.pan_zoom_orbit": "Panorér, zoom, rotér visning",
"ui.parallel": "Parallel",
@@ -1444,7 +1655,6 @@
"ui.quit": "Afslut",
"ui.radial_context_menu": "Radial genvejsmenu",
"ui.radius": "Radius",
"ui.radius_type": "Radiustype",
"ui.rail": "Styrekurve",
"ui.rail_fit": "Tilpasning til styrekurve",
"ui.reachable": "Kan nås",
@@ -1471,10 +1681,12 @@
"ui.reverse_horizontal_pan_tip": "Vandret rulning med to fingre og tommelfingerhjulet panorerer i den modsatte retning",
"ui.reverse_zoom_direction": "Vend zoomretning",
"ui.revolve": "Omdrejning",
"ui.ribbon_exit": "Afslut",
"ui.ribbon_pin_footer": "Fastgjorte kommandoer vises på båndet. Hold musen over en række for at fastgøre eller frigøre den.",
"ui.ribbon_workspace": "Arbejdsområde",
"ui.right": "Fra højre",
"ui.save": "Gem",
"ui.save_as": "Gem som",
"ui.save_as_ellipsis": "Gem som...",
"ui.save_mesh_no_slicer": "Ingen slicer fundet: Vælg Brugerdefineret…, og vælg dit slicerprogram, eller slå Send til slicer fra",
"ui.save_mesh_pick_bodies": "Vælg de emner, der skal gemmes",
"ui.save_mesh_temp_failed": "Kunne ikke oprette {folder}: {error}",
@@ -1487,7 +1699,6 @@
"ui.select": "Vælg",
"ui.selectOther": "Vælg andet…",
"ui.selectTangentChain": "Vælg tangentkæde",
"ui.select_other_hint": "Vælg andet: Hold venstre museknap nede uden at flytte markøren på modellen (eller højreklik › Vælg andet…) for at vise alt under markøren. Klik igen samme sted for at vælge næste objekt.",
"ui.selection": "Markering",
"ui.selection_modifiers": "Taster ved markering",
"ui.selection_shift_add_only": "Shift+klik tilføjer uden at fjerne",
@@ -1507,7 +1718,6 @@
"ui.show_constraints": "Bindinger",
"ui.show_construction_geometries": "Konstruktionsgeometri",
"ui.show_dimensions": "Mål",
"ui.show_points": "Vis punkter",
"ui.show_profile": "Vis profil",
"ui.show_projected_geometries": "Projiceret geometri",
"ui.simple": "Enkel",
@@ -1518,7 +1728,6 @@
"ui.sketch_grid": "Skitsegitter",
"ui.sketch_modifiers": "Taster i skitser",
"ui.sketch_scale": "Skitseskalering",
"ui.sketch_status": "Skitsestatus",
"ui.sketches": "Skitser",
"ui.slice": "Lagdeling",
"ui.slicer": "Slicer",
@@ -1564,7 +1773,7 @@
"ui.stand_up_tip": "Når en STEP- eller IGES-fil åbnes med Z opad, tilbydes en kvart omdrejning om X",
"ui.start": "Start",
"ui.start_over": "Start forfra",
"ui.start_radius": "Startradius",
"ui.start_page": "Startside",
"ui.stretch": "Stræk",
"ui.supersampling": "Supersampling (SSAA)",
"ui.supersampling_adaptive": "Kun når visningen står stille",
@@ -1614,13 +1823,24 @@
"ui.untitled": "Uden titel",
"ui.up_axis_note": "Hvilken akse der peger opad i visningerne (aksevisning, Hjem, Se på). Designs ændres ikke.",
"ui.updateToCurrentView": "Opdatér til aktuel visning",
"ui.variable": "Variabel",
"ui.view_centre": "Visningens centrum",
"ui.visual_shaded_hidden_edges": "Skyggelagt med skjulte kanter",
"ui.visual_shaded_with_edges": "Skyggelagt med kanter",
"ui.visual_style": "Visuel stil",
"ui.visual_wireframe": "Trådmodel",
"ui.workspace": "arbejdsområde",
"ui.workspace_animation_description": "Eksploderede visninger, bevægelsesstudier og storyboards",
"ui.workspace_available": "Tilgængelig",
"ui.workspace_current": "Aktuel",
"ui.workspace_design_description": "Skitsér, modellér, saml og undersøg",
"ui.workspace_design_tabs": "Model · Overflade · Saml · Indsæt · Undersøg · Administrér · Visning",
"ui.workspace_drawing_description": "2D-tegninger med visninger, mål og tegningshoveder",
"ui.workspace_footer": "Alle arbejdsområder arbejder på det samme design, den samme historik og den samme markering; kun båndets faner ændres. Skifteren er låst, mens en skitse eller feature redigeres.",
"ui.workspace_manufacture_description": "Slicing, værktøjsbaner og opsætning",
"ui.workspace_planned": "Planlagt",
"ui.workspace_render_description": "Produktforhåndsvisning, scene-indstillinger og renderede billeder",
"ui.workspace_render_tabs": "Scene · Billede",
"ui.workspace_simulation_description": "Belastninger, bindinger og resultater",
"ui.x_distance": "X-afstand",
"ui.y_distance": "Y-afstand",
"ui.y_up": "Y opad",
@@ -1630,7 +1850,6 @@
"ui.zoom_to_the_cursor": "Zoom mod markøren",
"update.available": "SolidCraft {version} er tilgængelig",
"update.button": "Opdatér",
"update.button_tip": "SolidCraft {version} er tilgængelig",
"update.button_version": "Opdatér til {version}",
"update.check_automatically": "Søg automatisk efter opdateringer",
"update.check_now": "Søg nu",
@@ -1662,5 +1881,6 @@
"update.title": "Opdatering tilgængelig",
"update.up_to_date": "SolidCraft er opdateret.",
"update.update_now": "Opdatér nu",
"update.you_have": "Du har {version}."
"update.you_have": "Du har {version}.",
"↑ ↓ choose · Enter / Tab complete · Esc dismiss": "↑ ↓ vælg · Enter / Tab udfyld · Esc luk"
}
+613 -31
View File
File diff suppressed because it is too large Load Diff
+259 -41
View File
@@ -3,11 +3,14 @@
"1. Open GitHub draft below, then paste the report copied to your clipboard.": "1. Open GitHub draft below, then paste the report copied to your clipboard.",
"1. Open GitHub draft below. Your title and report will be filled in.": "1. Open GitHub draft below. Your title and report will be filled in.",
"2. Attach your screenshot: copy and paste it, or save and upload the PNG.": "2. Attach your screenshot: copy and paste it, or save and upload the PNG.",
"2D": "2D",
"3. Click Submit new issue on GitHub to send your report.": "3. Click Submit new issue on GitHub to send your report.",
"A new configuration, a copy of the active one": "A new configuration, a copy of the active one",
"AI": "AI",
"ARRANGE": "ARRANGE",
"About SolidCraft": "About SolidCraft",
"Acceleration": "Acceleration",
"Access": "Access",
"Actions": "Actions",
"Active": "Active",
"Active rule": "Active rule",
"Add": "Add",
@@ -15,6 +18,7 @@
"Add Mid Radius": "Add Mid Radius",
"Add Row": "Add Row",
"Add condition": "Add condition",
"Add or edit rule": "Add or edit rule",
"Against": "Against",
"Alignment": "Alignment",
"All Sketch Points": "All Sketch Points",
@@ -28,8 +32,11 @@
"Applied Radius": "Applied Radius",
"Apply To": "Apply To",
"Apply to selection": "Apply to selection",
"Apply your appearance first. Exports assigned appearances and materials created in this design.": "Apply your appearance first. Exports assigned appearances and materials created in this design.",
"Arc length": "Arc length",
"Area": "Area",
"As-Built": "As-Built",
"Assign Rule": "Assign Rule",
"Attachment": "Attachment",
"BLEND SURFACE": "BLEND SURFACE",
"BOUNDARY SURFACE": "BOUNDARY SURFACE",
@@ -37,12 +44,21 @@
"Bend Line Position": "Bend Line Position",
"Bend Position": "Bend Position",
"Bend Radius": "Bend Radius",
"Bend Table": "Bend Table",
"Bends": "Bends",
"Bodies": "Bodies",
"Body": "Body",
"Boundary": "Boundary",
"Bounding box": "Bounding box",
"CHECK": "CHECK",
"COMPONENTS": "COMPONENTS",
"CONFIGURATIONS": "CONFIGURATIONS",
"CONSTRAIN": "CONSTRAIN",
"CONTACT": "CONTACT",
"CONVERT": "CONVERT",
"CSV or JSON: updates existing parameters, adds new ones": "CSV or JSON: updates existing parameters, adds new ones",
"Capture All Positions": "Capture All Positions",
"Capture Pos.": "Capture Pos.",
"Catch Direction": "Catch Direction",
"Cells": "Cells",
"Center X": "Center X",
@@ -53,15 +69,19 @@
"Check collisions": "Check collisions",
"Checked": "Checked",
"Choose a new plane. Sketch geometry and dimensions stay unchanged in its two-dimensional coordinates.": "Choose a new plane. Sketch geometry and dimensions stay unchanged in its two-dimensional coordinates.",
"Choose…": "Choose…",
"Circular": "Circular",
"Circumference": "Circumference",
"Close draft": "Close draft",
"Collisions": "Collisions",
"Colour": "Colour",
"Comment": "Comment",
"Component1": "Component1",
"Config Table": "Config Table",
"Configuration": "Configuration",
"Configuration rules": "Configuration rules",
"Construction": "Construction",
"Contact": "Contact",
"Contact Sets": "Contact Sets",
"Contact sets": "Contact sets",
"Continuity": "Continuity",
"Copy Error Message": "Copy Error Message",
@@ -79,15 +99,17 @@
"Cube": "Cube",
"Current orthographic view (1:1 mm)": "Current orthographic view (1:1 mm)",
"Current perspective view": "Current perspective view",
"Curv. Comb": "Curv. Comb",
"Curvature (G2)": "Curvature (G2)",
"Curvature change (G3)": "Curvature change (G3)",
"Customize Toolbar": "Customize Toolbar",
"Delete": "Delete",
"Delete all": "Delete all",
"Delete rule": "Delete rule",
"Delete the row": "Delete the row",
"Delete the set": "Delete the set",
"Delete this parameter": "Delete this parameter",
"Density": "Density",
"Dependents": "Dependents",
"Designs you open or save appear here.": "Designs you open or save appear here.",
"Determined by To Object. Choose Angle to edit the angular extent.": "Determined by To Object. Choose Angle to edit the angular extent.",
"Direction": "Direction",
@@ -99,6 +121,7 @@
"Drive Joint": "Drive Joint",
"Duration": "Duration",
"Edges": "Edges",
"Edit": "Edit",
"Edit Profile Sketch": "Edit Profile Sketch",
"Enable rule": "Enable rule",
"Enabled": "Enabled",
@@ -107,43 +130,63 @@
"Explode": "Explode",
"Explode Text": "Explode Text",
"Export Animation…": "Export Animation…",
"Export CSV…": "Export CSV…",
"Export Current View as DXF": "Export Current View as DXF",
"Export Current View as DXF…": "Export Current View as DXF…",
"Export DXF": "Export DXF",
"Export DXF…": "Export DXF…",
"Export JSON…": "Export JSON…",
"Export hidden edges as dashed lines": "Export hidden edges as dashed lines",
"Expression": "Expression",
"Extend to meet": "Extend to meet",
"Extents": "Extents",
"FILES": "FILES",
"FLAT": "FLAT",
"Face": "Face",
"Face attachment lost": "Face attachment lost",
"Faces": "Faces",
"Faces / edges / vertices": "Faces / edges / vertices",
"Fastener": "Fastener",
"Favorites": "Favorites",
"Favourite": "Favourite",
"Feature": "Feature",
"Files from many CAD programs have Y up, so they open lying on their back here (Z up).": "Files from many CAD programs have Y up, so they open lying on their back here (Z up).",
"Filter": "Filter",
"Find in Browser": "Find in Browser",
"Flat": "Flat",
"Finish": "Finish",
"Flat Pattern": "Flat Pattern",
"Flat size": "Flat size",
"Follow camera (Look At sketches)": "Follow camera (Look At sketches)",
"Follow the camera (Look At sketches)": "Follow the camera (Look At sketches)",
"Font": "Font",
"From Bodies": "From Bodies",
"From edges": "From edges",
"Full Length": "Full Length",
"Gap": "Gap",
"Grid": "Grid",
"HISTORY": "HISTORY",
"Height": "Height",
"Hidden edges": "Hidden edges",
"Holes": "Holes",
"Holes (edges on them)": "Holes (edges on them)",
"Home view": "Home view",
"Horiz./Vert.": "Horiz./Vert.",
"IMAGES": "IMAGES",
"Import…": "Import…",
"In This Design": "In This Design",
"In place (surfaces)": "In place (surfaces)",
"In plane": "In plane",
"Inside": "Inside",
"Interferes": "Interferes",
"Isocurve": "Isocurve",
"Issue title": "Issue title",
"JOINTS": "JOINTS",
"Joint Origin1": "Joint Origin1",
"Keep (points on it)": "Keep (points on it)",
"Keep Tools": "Keep Tools",
"Keep of the other surface": "Keep of the other surface",
"LIBRARY": "LIBRARY",
"LINKS": "LINKS",
"Later": "Later",
"Leave it": "Leave it",
"Legacy face attachment. Rebinding to a certified face creates an exact attachment.": "Legacy face attachment. Rebinding to a certified face creates an exact attachment.",
@@ -151,47 +194,70 @@
"Length units": "Length units",
"Letter Spacing": "Letter Spacing",
"Library": "Library",
"Library file": "Library file",
"Line Spacing": "Line Spacing",
"Local library": "Local library",
"Loop": "Loop",
"MANAGE": "MANAGE",
"MATERIALS": "MATERIALS",
"MODEL": "MODEL",
"MOTION": "MOTION",
"Margin": "Margin",
"Mass": "Mass",
"Mass Center": "Mass Center",
"Matched restrictions check the final values.": "Matched restrictions check the final values.",
"Material": "Material",
"Max": "Max",
"Mid Radius": "Mid Radius",
"Middle double-click": "Middle double-click",
"Min": "Min",
"Mitre": "Mitre",
"Model Parameters": "Model Parameters",
"Modeled": "Modeled",
"More toolbar panels": "More toolbar panels",
"Motion": "Motion",
"Move / Copy": "Move / Copy",
"Move Type": "Move Type",
"Move down": "Move down",
"Move earlier": "Move earlier",
"Move later": "Move later",
"Move up": "Move up",
"Name": "Name",
"Nearest": "Nearest",
"New": "New",
"New Comp.": "New Comp.",
"New Sketch": "New Sketch",
"New rule…": "New rule…",
"New study": "New study",
"No crash reports.": "No crash reports.",
"No matching appearance presets.": "No matching appearance presets.",
"No matching appearances": "No matching appearances",
"No visible bodies to export.": "No visible bodies to export.",
"None": "None",
"Normal to sketch": "Normal to sketch",
"Not available yet": "Not available yet",
"Notch": "Notch",
"OUTPUT": "OUTPUT",
"Opacity": "Opacity",
"Open GitHub draft": "Open GitHub draft",
"Open local libraries in the desktop app. You can export this design's library here.": "Open local libraries in the desktop app. You can export this design's library here.",
"Open-source parametric 3D CAD in Rust. Licensed MIT OR Apache-2.0.": "Open-source parametric 3D CAD in Rust. Licensed MIT OR Apache-2.0.",
"Operator": "Operator",
"Other side": "Other side",
"Other surface (mutual trim)": "Other surface (mutual trim)",
"Outside": "Outside",
"Overall length": "Overall length",
"Parameter values": "Parameter values",
"Param. Graph": "Param. Graph",
"Parameter": "Parameter",
"Parts": "Parts",
"Pass through": "Pass through",
"Path": "Path",
"Pattern": "Pattern",
"Per Edge": "Per Edge",
"Pick the tool bodies": "Pick the tool bodies",
"Picked": "Picked",
"Picked point": "Picked point",
"Place at the origin instead": "Place at the origin instead",
"Plastic Rules": "Plastic Rules",
"Playback": "Playback",
"Plot": "Plot",
"Position": "Position",
@@ -202,10 +268,11 @@
"Preview Row": "Preview Row",
"Preview leaves the current design unchanged.": "Preview leaves the current design unchanged.",
"Profiles/Curves": "Profiles/Curves",
"Projection": "Projection",
"Quantity": "Quantity",
"RECENT": "RECENT",
"REFERENCE": "REFERENCE",
"RULED SURFACE": "RULED SURFACE",
"RULES": "RULES",
"Radius Fit": "Radius Fit",
"Ratio": "Ratio",
"Reattach selected face": "Reattach selected face",
@@ -223,6 +290,8 @@
"Remove this point": "Remove this point",
"Report a bug — review a screenshot and open a GitHub draft": "Report a bug — review a screenshot and open a GitHub draft",
"Reset Toolbar": "Reset Toolbar",
"Resize Part": "Resize Part",
"Restore View": "Restore View",
"Restore outer boundary": "Restore outer boundary",
"Restore the inherited component or physical material appearance": "Restore the inherited component or physical material appearance",
"Result": "Result",
@@ -231,18 +300,27 @@
"Reverse Direction": "Reverse Direction",
"Revert": "Revert",
"Revert All Positions": "Revert All Positions",
"Revert Pos.": "Revert Pos.",
"Revert Position": "Revert Position",
"Review the report and screenshot before opening GitHub.": "Review the report and screenshot before opening GitHub.",
"Rim Edge": "Rim Edge",
"Roll History Marker Here": "Roll History Marker Here",
"Round": "Round",
"Rule": "Rule",
"Rule name": "Rule name",
"Rules run from top to bottom. Matched drives change effective values.": "Rules run from top to bottom. Matched drives change effective values.",
"SECTION": "SECTION",
"SURFACE QUALITY": "SURFACE QUALITY",
"Save Rule": "Save Rule",
"Save View": "Save View",
"Save the study as an animated GIF, seen as the viewport shows it": "Save the study as an animated GIF, seen as the viewport shows it",
"Saved preview. Geometry is being checked before editing.": "Saved preview. Geometry is being checked before editing.",
"Scale factor": "Scale factor",
"Scene": "Scene",
"Search": "Search",
"Search commands": "Search commands",
"Search this library": "Search this library",
"Section": "Section",
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).",
"Select bodies (a face picks its body)": "Select bodies (a face picks its body)",
"Select bodies or faces instead": "Select bodies or faces instead",
@@ -250,6 +328,7 @@
"Semantic faces share their appearance across instances.": "Semantic faces share their appearance across instances.",
"Send your report on GitHub": "Send your report on GitHub",
"Shape": "Shape",
"Sheet Rules": "Sheet Rules",
"Show Agent Cursor": "Show Agent Cursor",
"Show in folder": "Show in folder",
"Show the pointer an AI agent moves (#35)": "Show the pointer an AI agent moves (#35)",
@@ -263,6 +342,7 @@
"Sketch points": "Sketch points",
"Slot width": "Slot width",
"SolidCraft closed before these designs were saved. Their last autosave can be opened.": "SolidCraft closed before these designs were saved. Their last autosave can be opened.",
"Specialise": "Specialise",
"Speed": "Speed",
"Stand it up": "Stand it up",
"Stand it up: a quarter turn about X, as one Move feature you can undo.": "Stand it up: a quarter turn about X, as one Move feature you can undo.",
@@ -301,17 +381,19 @@
"Turn off section analysis before exporting the view.": "Turn off section analysis before exporting the view.",
"Type": "Type",
"UNTRIM": "UNTRIM",
"Unit": "Unit",
"Updating the preview": "Updating the preview",
"Use active author placement": "Use active author placement",
"Use fixed plane": "Use fixed plane",
"Use saved fixed plane": "Use saved fixed plane",
"Use selected face as fixed plane": "Use selected face as fixed plane",
"User Parameters": "User Parameters",
"VIEW": "VIEW",
"VIEWS": "VIEWS",
"Value": "Value",
"Velocity": "Velocity",
"Vertices": "Vertices",
"View navigator": "View navigator",
"Visible bodies only. Curves use line segments (0.01 mm tolerance).": "Visible bodies only. Curves use line segments (0.01 mm tolerance).",
"Volume": "Volume",
"Wall": "Wall",
"Wall Location": "Wall Location",
@@ -319,6 +401,13 @@
"When": "When",
"Width": "Width",
"Will fail": "Will fail",
"X angle": "X angle",
"X distance": "X distance",
"Y angle": "Y angle",
"Y distance": "Y distance",
"Z angle": "Z angle",
"Z distance": "Z distance",
"Zebra": "Zebra",
"angle, height": "angle, height",
"appearance.dielectric_ior": "Dielectric IOR",
"appearance.metallic": "Metallic",
@@ -327,7 +416,13 @@
"appearance.roughness": "Roughness",
"appearance.surface_response": "Surface response",
"browser.body_name": "Body{number}",
"browser.calibrate_title": "Calibrate: {name}",
"browser.capture_many": "{count} components were moved but their positions aren't captured.",
"browser.capture_note": "Capture keeps the new position; Revert puts it back.",
"browser.capture_one": "A component was moved but its position isn't captured.",
"browser.component_name": "Component{number}",
"browser.edit_canvas_title": "Edit Canvas: {name}",
"browser.move_title": "Move: {name}",
"browser.parameters": "Parameters ({count})",
"browser.precision": "Precision: {precision}",
"browser.sketch_name": "Sketch{number}",
@@ -665,7 +760,15 @@
"cmd.view_navigate": "Navigate View",
"cmd.view_rename": "Rename Named View",
"cmd.view_save": "New Named View",
"command name": "command name",
"color.copy": "Click to copy color values",
"color.edit": "Click to edit color",
"color.gamma": "Showing color values in 0-255 gamma space",
"color.hue": "Hue",
"color.linear": "Showing color values in 0-1 linear space",
"color.saturation_value": "Saturation and value",
"color.selected": "Selected color",
"contact.components_count": "{count} components",
"contact.enabled_label": "enabled",
"coupling.gear_1": "Gear 1",
"coupling.gear_1_pick": "Gear 1 (optional)",
"coupling.gear_2": "Gear 2",
@@ -682,6 +785,21 @@
"coupling.rack": "Rack",
"coupling.radius_1": "Radius 1",
"coupling.radius_2": "Radius 2",
"crash.announce": "SolidCraft closed unexpectedly or hit an internal error last time. A crash report was saved (Help > Crash Reports…).",
"crash.copy_folder": "Copy folder path",
"crash.copy_path": "Copy path",
"crash.deleted": "Deleted {count} report(s).",
"crash.detected_time": "Detected {time} UTC",
"crash.intro": "Crash reports stay on this computer and are never sent anywhere. Report… opens a draft you review before anything is shared.",
"crash.kind_command": "Internal error in a command",
"crash.kind_internal": "Internal error",
"crash.kind_kernel": "Kernel error (recovered)",
"crash.kind_unclean": "Closed unexpectedly",
"crash.previous_session": "An earlier SolidCraft session ended unexpectedly or hit an internal error. Reports from unclean exits are detected at startup. See Help > Crash Reports… for details.",
"crash.replayable": "replayable",
"crash.report": "Report…",
"crash.unclean_explanation": "Detected on a later launch; the exact exit time was not recorded. A system shutdown or forced stop can also cause an unclean exit.",
"delete.and_more": "… and {count} more",
"dialog.bodies_count": "{count} bodies",
"dialog.clearance_invalid": "the clearance must be a length of 0 or more",
"dialog.compute": "Compute",
@@ -689,6 +807,8 @@
"dialog.computing": "Computing… {seconds} s",
"dialog.could_not_be_checked": "could not be checked",
"dialog.custom_axis": "↖ Custom Axis",
"dialog.dimension_insert_hint": "Click another driving dimension to insert its name",
"dialog.dimension_name": "Dimension: {name}",
"dialog.edit_title": "EDIT {title}",
"dialog.select": "↖ Select",
"dialog.selected_count": "↖ {count} selected",
@@ -712,9 +832,30 @@
"dxf.progress.write": "Writing DXF (5/5)",
"dxf.tolerance": "Curve tolerance",
"dxf.tolerance_help": "Visible bodies only. Curves use straight segments. Smaller tolerance gives finer curves and uses more time and memory.",
"expression, e.g. 25 mm": "expression, e.g. 25 mm",
"file_load.built": "Built so far: {built} of {total} features.",
"file_load.converting": "Converting solid {done} of {total}",
"file_load.inserting": "Inserting geometry",
"file_load.opening": "Opening design",
"file_load.preparing_thumbnail": "Preparing the thumbnail...",
"file_load.preparing_view": "Preparing the view...",
"file_load.preview_note": "Saved preview. Geometry is being checked before editing.",
"file_load.reading": "Reading the file...",
"file_load.reading_percent": "Reading the file... {percent}%",
"file_load.rebuilding": "Rebuilding feature {done} of {total}",
"file_load.recovering": "Recovering design",
"file_load.stopping": "Waiting for the previous operation to stop...",
"file_load.waiting": "Waiting for file operation",
"fits the face, e.g. M8": "fits the face, e.g. M8",
"from the rule": "from the rule",
"help.apache": "Apache licence 2.0",
"help.attributions": "Attributions (ATTRIBUTION.md)",
"help.build": "Version {version} · commit {commit} · built {date}",
"help.font_details": "The interface uses egui's built-in fonts (Ubuntu Light and Hack, under the Ubuntu Font Licence and the MIT/Bitstream Vera licence; Noto Emoji and the emoji icon font, under the OFL and MIT). Sketch text uses Liberation Sans (SIL OFL 1.1). The Crafting Apps' optional font pack, craft-fonts (BIZ UDPGothic, Shippori Mincho, BIZ UDMincho, Noto Sans CJK SC, Noto Sans Arabic; SIL OFL 1.1), is not bundled in SolidCraft builds.",
"help.fonts": "Fonts",
"help.licences": "Licences",
"help.mit": "MIT licence",
"help.notice": "NOTICE",
"help.third_party": "Third-party licences",
"home.documentation": "Documentation",
"home.in_this_browser": "in this browser",
"home.missing": "missing",
@@ -740,9 +881,30 @@
"install.menu": "Install app…",
"install.steps_intro": "Safari installs web apps from its own menus:",
"install.title": "Install SolidCraft",
"issue.body_label": "Report text",
"issue.note_image_copied": "Paste the image into the GitHub draft, or save the PNG if image copying is unavailable.",
"issue.note_opened": "Review the GitHub draft and attach the screenshot. GitHub's Submit new issue button publishes it.",
"issue.note_opened_copied": "Report copied. Paste into the GitHub draft, attach the screenshot, then click Submit new issue on GitHub.",
"issue.note_removed": "Screenshot removed from this draft.",
"issue.note_report_copied": "Report copied. Paste it into the GitHub draft if needed.",
"issue.note_save_failed": "Could not save screenshot: {error}",
"issue.note_saved": "Screenshot saved. Attach the PNG in the GitHub draft.",
"issue.save_screenshot": "Save screenshot…",
"joint.from_bodies_hint": "Each picked body moves into a new component of its own and stays where it is.",
"joint.one_body": "Both picks are on {body}. Pick the second one on another part.",
"joint.one_component": "These parts are bodies of one component: {bodies}. A joint moves one component against another, so each part needs a component of its own.",
"keymap.command_id": "Command ID",
"keymap.empty": "No matching commands",
"keymap.use_anyway": "Use {key} here anyway",
"manage.appearance_hint": "Choose a look for the selection, or drag a swatch onto a body or face.",
"manage.assign_hint": "Select bodies and choose their plastic design rule.",
"manage.config_hint": "Edit configuration rows, columns and rules, then activate or preview a row.",
"manage.empty": "No matching rows.",
"manage.material_hint": "Select bodies and choose their physical material.",
"manage.parameters_hint": "Edit expressions and units. Changes apply on Enter or when leaving a field.",
"manage.rules_hint": "Choose a rule to edit, or create a new rule. Confirm to save the rule.",
"measure.at": "At",
"measure.item": "Item {number}",
"messages.ago_h": "{n} h ago",
"messages.ago_min": "{n} min ago",
"messages.ago_s": "{n} s ago",
@@ -754,8 +916,16 @@
"messages.title": "Messages",
"motion.checked_to": "Checked to step {step} only: the check ran out of its work budget",
"motion.collision": "{a} · {b}: steps {steps}",
"motion.delete_key": "Delete key",
"motion.duration_hint": "seconds (empty: per step)",
"motion.key_here": "Key here",
"motion.key_here_hint": "Add a key at this step with the joint's value now",
"motion.no_collisions": "No collisions over the study",
"motion.step_back": "Step back",
"motion.step_forward": "Step on",
"motion.to_end": "To the end",
"motion.to_start": "To the start",
"motion.track_name": "Track {number}",
"nav.touchpad.help": "Hold Shift and move the pointer to pan; hold Alt/Option and move it to orbit. No mouse button is needed. Vertical scroll zooms.",
"nav.touchpad.label": "Touchpad",
"nav.touchpad.orbit": "Orbit (hold Alt/Option and move; no mouse button)",
@@ -764,13 +934,13 @@
"nav.touchpad.zoom": "Zoom (vertical scroll or pinch)",
"needs two joints": "needs two joints",
"new configuration": "new configuration",
"new name": "new name",
"new parameter": "new parameter",
"no hole": "no hole",
"no interference": "no interference",
"no joint that moves yet": "no joint that moves yet",
"no picture": "no picture",
"none": "none",
"parts.automatic_name": "automatic",
"pattern.alignment_2": "Alignment 2",
"pattern.alignment_hint": "Counts include the original. Center balances copies; Flip chooses the extra side for even totals.",
"pattern.direction_1": "Direction 1",
@@ -778,12 +948,19 @@
"pattern.flip_direction": "Flip direction",
"pick a hole to seat it, or OK places it at the origin": "pick a hole to seat it, or OK places it at the origin",
"pick one or two vertices, edges or faces": "pick one or two vertices, edges or faces",
"prefs.cpu": "CPU (software)",
"prefs.minutes_suffix": " min",
"properties.title": "Properties: {name}",
"recovery.days_ago": "{count} days ago",
"recovery.detail": "{file} · {features} features · autosaved {when}",
"recovery.hours_ago": "{count} h ago",
"recovery.just_now": "just now",
"recovery.min_ago": "{count} min ago",
"recovery.never_saved": "never saved",
"render.background": "Scene background",
"render.fit": "Fit Product",
"render.image_cancel": "Cancel Render",
"render.image_cancelled": "Render cancelled",
"render.image_cancelling": "Cancelling",
"render.image_check": "Check Render Request",
"render.image_completed": "Image ready to export",
"render.image_encoding": "Encoding PNG",
"render.image_export": "Export PNG",
@@ -830,6 +1007,19 @@
"sample.plate.name": "Sample Plate",
"sample.vents.about": "Slots in a rectangular pattern: rows, columns and pitch are parameters",
"sample.vents.name": "Vent Plate",
"search.command_keys": "Enter run Shift+Enter run and keep open Esc close",
"search.commands": "Commands",
"search.design": "In this design",
"search.feature": "Feature",
"search.invalid": "Not valid",
"search.keys": "↑ ↓ choose Enter run Tab fill name Esc close",
"search.no_match": "No command matches '{query}'",
"search.parameter": "Parameter",
"search.recent": "Recent",
"search.run": "Run command",
"search.suggest": "Did you mean {label}?",
"search.try": "Type a command name, a feature in this design, or a parameter. To run a command with values, type its id and a JSON object: {example}",
"search.valid": "Valid",
"settings.display_quality_note": "Finer levels draw curved faces and edges smoother and take longer to mesh. Only the viewport changes, never the design or exports.",
"settings.frame_rate_note": "Shows frames per second in the view's bottom-right corner. While it is on, the view redraws continuously.",
"settings.measure_axis_distances": "Show X/Y/Z distances",
@@ -840,17 +1030,30 @@
"settings.reset_intro": "These settings don't work on this computer and would keep SolidCraft from starting, so they were changed:",
"settings.reset_title": "Settings were changed",
"settings.supersampling_note": "Draws the view at a higher resolution and filters it down: smoother edges, lines and grid on screens below 4K. It costs GPU time: about 2.25× the pixels at 1.5× and 4× at 2×.",
"shortcut.json_error": "parameters must be JSON: {error}",
"shortcut.no_command": "no command `{id}`; the text before the JSON must be a command id",
"shortcut.pin": "Pin to the top of the S box",
"shortcut.unpin": "Unpin",
"sketch.degrees_of_freedom": "{count} degrees of freedom",
"sketch.dof_left": "{count} degrees of freedom left",
"sketch.dof_left_one": "{count} degree of freedom left",
"sketch.draw_curves": "Draw as {linetype}",
"sketch.make_selection": "Convert the selection to {linetype}",
"sketch.open_ends": "Open {count}",
"sketch.untied_ends": "Untied {count}",
"sketch_panel.attached": "Attached to {body} / {path}",
"sketch_panel.author": "Author placement: {path}",
"sketch_panel.dimension_hint": "Enter a value or expression, then press OK.",
"sketch_panel.move_hint": "Enter distances and an angle, then press OK.",
"sketch_panel.redefine_title": "{command}: {name}",
"sketch_panel.sheet_attachment": "Attached to a flat face of a sheet metal body; it follows that panel when the sheet changes.",
"sketch_panel.text_hint": "Edit the text and its placement, then press OK.",
"sketch_panel.use_plane": "Use {plane} plane",
"sketch_text.align_center": "Center",
"sketch_text.align_left": "Left",
"sketch_text.align_right": "Right",
"sketch_text.bold": "Bold",
"sketch_text.builtin_font": "{family} (built-in)",
"sketch_text.font_search": "Search fonts",
"sketch_text.italic": "Italic",
"sketch_text.loading_fonts": "Looking for installed fonts…",
"sketch_text.on_path": "The text follows its path: the curve places it",
@@ -871,6 +1074,7 @@
"timeline.ungroup": "Remove Feature Folders",
"to the face": "to the face",
"to the next face": "to the next face",
"toolbar.customize_note": "{tab} tab. Tick a command to give it a toolbar button. You can also drag a row of a panel's drop-down onto the toolbar, drag a button off the toolbar to remove it, or use the pin on a drop-down row.",
"toolbar.image": "IMAGE",
"toolbar.render": "RENDER",
"toolbar.render_scene": "SCENE",
@@ -879,11 +1083,11 @@
"trace.stopped_by_loop": "Ends at sample {sample}: the loop through {joint} can't close any further",
"ui.1_degree_of_freedom": "1 degree of freedom",
"ui.3d_mouse_speed": "3D mouse speed",
"ui.3d_mouse_windows": "3D mouse",
"ui.3d_sketch": "3D Sketch",
"ui.about": "About",
"ui.activate": "Activate",
"ui.active_component": "Active component",
"ui.add_configuration": "Add configuration",
"ui.add_parameter": "Add parameter",
"ui.agent_chat": "Agent chat",
"ui.agent_chat_add": "Attach files",
"ui.agent_chat_add_file": "Choose a file…",
@@ -925,6 +1129,7 @@
"ui.agent_chat_file_error": "{name}: {error}",
"ui.agent_chat_file_heic": "{name}: HEIC pictures can't be read here; export it as JPEG or PNG first",
"ui.agent_chat_file_importing": "Importing into the design…",
"ui.agent_chat_file_kinds": "{name}: pictures (PNG, JPEG, GIF, WebP, BMP, TIFF), text files (SVG too) and CAD files (STEP, IGES, FreeCAD, Rhino, STL, 3MF, OBJ, glTF) can be attached",
"ui.agent_chat_file_loading": "Reading the file…",
"ui.agent_chat_file_not_a_file": "{name}: not a file",
"ui.agent_chat_file_not_a_picture": "{name}: not a picture",
@@ -947,7 +1152,7 @@
"ui.agent_chat_model_tip": "The model the agent uses. Your choice is remembered for this agent.",
"ui.agent_chat_new": "New chat",
"ui.agent_chat_new_confirm": "Start a new chat? The current one is cleared.",
"ui.agent_chat_new_yes": "New chat",
"ui.agent_chat_next_picture": "Next picture",
"ui.agent_chat_no_cli": "solidcraft-cli was not found next to the app, so the agent can't reach the design.",
"ui.agent_chat_no_control": "The agent chat's endpoint did not start, so the agent can't reach the design.",
"ui.agent_chat_no_images": "This agent doesn't take pictures",
@@ -963,6 +1168,7 @@
"ui.agent_chat_placeholder": "Ask the agent to change the design…",
"ui.agent_chat_placeholder_busy": "Queue a message for when the agent is done (Ctrl+Enter: send now)",
"ui.agent_chat_pop_out": "Open in its own window, which you can move to another screen",
"ui.agent_chat_previous_picture": "Previous picture",
"ui.agent_chat_privacy": "Nothing goes anywhere except to the agent you run, under your own account.",
"ui.agent_chat_privacy_note": "SolidCraft sends nothing anywhere: the agent you run talks to its own service under your own account. SolidCraft never sees or stores a key or password.",
"ui.agent_chat_queue": "Queue",
@@ -1018,6 +1224,7 @@
"ui.ai_bad_url": "Enter a URL that starts with http:// or https://",
"ui.ai_base_url": "Base URL",
"ui.ai_button": "AI",
"ui.ai_command_hint": "my-agent --acp",
"ui.ai_copy_suffix": "copy",
"ui.ai_custom_url": "Custom URL",
"ui.ai_default": "Default",
@@ -1038,8 +1245,10 @@
"ui.ai_mode_edits_note": "Modelling commands run without asking; saving, opening, exporting and other file or destructive actions still ask.",
"ui.ai_mode_global": "As set above",
"ui.ai_model": "Model",
"ui.ai_model_hint": "qwen3:14b",
"ui.ai_modes_scope": "These settings cover SolidCraft's tools. An agent's own tools (its shell, its file edits) follow the agent's own mode, which you pick in the chat panel's header when the agent has modes.",
"ui.ai_name": "Name",
"ui.ai_name_hint": "My agent",
"ui.ai_need_command": "Enter the command that starts the agent",
"ui.ai_need_model": "Enter the model name",
"ui.ai_no_answer": "The agent did not answer in time",
@@ -1067,9 +1276,10 @@
"ui.ai_tool_running": "running",
"ui.ai_tool_unfold": "Click to show what it returned",
"ui.ai_tool_waiting": "waiting",
"ui.ai_toolbar_button": "Show button in toolbar",
"ui.ai_toolbar_button_note": "The AI button at the right end of the toolbar. Ctrl+Shift+A and the View menu still open the panel.",
"ui.ai_toolbar_button": "Show Assistant button in application bar",
"ui.ai_toolbar_button_note": "The Assistant button in the application bar. Ctrl+Shift+A and the View menu still open the panel.",
"ui.ai_undo_tip": "Undo everything the agent changed for this message, as one step",
"ui.ai_url_hint": "https://example.com/v1",
"ui.all": "All",
"ui.ambient_occlusion": "Ambient occlusion",
"ui.ambient_occlusion_help": "Soft shading where opaque surfaces meet. Requires the GPU viewport.",
@@ -1083,7 +1293,8 @@
"ui.anti_aliasing": "Anti-aliasing",
"ui.apply": "Apply",
"ui.arc": "Arc",
"ui.assemble": "Assemble",
"ui.assistant": "Assistant",
"ui.assistant_waiting": "{count} approvals waiting",
"ui.autosave_every": "Autosave every",
"ui.axes": "Axes",
"ui.axis": "Axis",
@@ -1094,6 +1305,11 @@
"ui.bodies_all_if_none": "Bodies (all if none)",
"ui.box": "Box",
"ui.break": "Break",
"ui.browser_bodies_one": "{count} body",
"ui.browser_bodies_other": "{count} bodies",
"ui.browser_collapse": "Collapse the browser",
"ui.browser_expand": "Expand the browser",
"ui.browser_title": "Browser",
"ui.calibrate": "Calibrate",
"ui.cancel": "Cancel",
"ui.canvases": "Canvases",
@@ -1103,7 +1319,6 @@
"ui.chamfer": "Chamfer",
"ui.chamfer_type": "Chamfer Type",
"ui.change_workspace": "Change workspace",
"ui.chord_length": "Chord Length",
"ui.circle": "Circle",
"ui.circular_pattern": "Circular Pattern",
"ui.clear": "Clear",
@@ -1136,11 +1351,9 @@
"ui.command_done": "{command} done",
"ui.command_palette_hint": "Type a command, e.g. {example} — or press S to search",
"ui.concentric": "Concentric",
"ui.configurations": "Configurations",
"ui.conic_curve": "Conic Curve",
"ui.constant": "Constant",
"ui.constraints_conflict": "Constraints conflict",
"ui.construct": "Construct",
"ui.construction": "Construction",
"ui.context_menu": "Context menu",
"ui.continuity_g0": "G0 crease",
@@ -1166,7 +1379,6 @@
"ui.cylinder": "Cylinder",
"ui.dark": "Dark",
"ui.default_modelling_orientation": "Default modelling orientation",
"ui.degrees_of_freedom": "degrees of freedom",
"ui.delete": "Delete",
"ui.depth": "Depth",
"ui.design": "Design",
@@ -1208,7 +1420,6 @@
"ui.equivalent_radius": "Equivalent Radius",
"ui.expand": "Expand",
"ui.exportDxf": "Export DXF…",
"ui.export_ellipsis": "Export...",
"ui.export_ellipsis_keymap": "Export…",
"ui.export_keymap_tip": "Keys, S box favourites and the toolbar, as a file",
"ui.extend": "Extend",
@@ -1232,6 +1443,7 @@
"ui.frame_rate": "Frame rate",
"ui.front": "Front",
"ui.fully_constrained": "Fully constrained",
"ui.function": "Function",
"ui.general": "General",
"ui.graphics": "Graphics",
"ui.graphics_custom": "Custom",
@@ -1298,7 +1510,6 @@
"ui.heading_sheet_metal": "SHEET METAL",
"ui.heading_shell": "SHELL",
"ui.heading_sketch": "SKETCH",
"ui.heading_sketch_palette": "SKETCH PALETTE",
"ui.heading_sketch_scale": "SKETCH SCALE",
"ui.heading_solid": "SOLID",
"ui.heading_sphere": "SPHERE",
@@ -1317,6 +1528,7 @@
"ui.hideOrigin": "Hide Origin",
"ui.hidePlanes": "Hide Planes",
"ui.hideProfile": "Hide Profile",
"ui.history_title": "History",
"ui.hole": "Hole",
"ui.hole_tap_type": "Hole Tap Type",
"ui.hole_type": "Hole Type",
@@ -1336,7 +1548,6 @@
"ui.length_decimals": "Length decimals",
"ui.light": "Light",
"ui.line": "Line",
"ui.linetype": "Linetype",
"ui.lock": "Lock",
"ui.loft": "Loft",
"ui.look_at": "Look At",
@@ -1370,10 +1581,8 @@
"ui.mesh_format_3mf": "3MF",
"ui.mesh_format_3mf_tagged": "3MF with face tags",
"ui.mesh_format_stl": "STL",
"ui.midpoint": "Midpoint",
"ui.mirror": "Mirror",
"ui.mirror_line": "Mirror line",
"ui.modify": "Modify",
"ui.move": "Move",
"ui.multiple": "Multiple",
"ui.named_views": "Named Views",
@@ -1394,7 +1603,6 @@
"ui.offset_plane": "Offset Plane",
"ui.one_side": "One Side",
"ui.open": "Open",
"ui.open_ellipsis": "Open...",
"ui.open_ends_tip": "Touches nothing",
"ui.open_preferences": "Open Preferences",
"ui.open_recent": "Open Recent",
@@ -1404,8 +1612,9 @@
"ui.orbit_around": "Orbit around",
"ui.orientation": "Orientation",
"ui.origin": "Origin",
"ui.palette_points": "Points",
"ui.palette_profile": "Profile",
"ui.palette_construction_mode": "Construction mode (X)",
"ui.palette_more": "More",
"ui.palette_sketch_options": "Sketch options",
"ui.pan": "Pan",
"ui.pan_zoom_orbit": "Pan, zoom, orbit",
"ui.parallel": "Parallel",
@@ -1446,7 +1655,6 @@
"ui.quit": "Quit",
"ui.radial_context_menu": "Radial context menu",
"ui.radius": "Radius",
"ui.radius_type": "Radius Type",
"ui.rail": "Rail",
"ui.rail_fit": "Rail Fit",
"ui.reachable": "Reachable",
@@ -1473,10 +1681,12 @@
"ui.reverse_horizontal_pan_tip": "Horizontal two-finger scrolling and the thumb wheel pan the other way",
"ui.reverse_zoom_direction": "Reverse zoom direction",
"ui.revolve": "Revolve",
"ui.ribbon_exit": "Exit",
"ui.ribbon_pin_footer": "Pinned commands show on the ribbon. Hover a row to pin or unpin it.",
"ui.ribbon_workspace": "Workspace",
"ui.right": "Right",
"ui.save": "Save",
"ui.save_as": "Save As",
"ui.save_as_ellipsis": "Save As...",
"ui.save_mesh_no_slicer": "No slicer found: choose Custom… and pick your slicer program, or turn off Send to slicer",
"ui.save_mesh_pick_bodies": "Select the bodies to save",
"ui.save_mesh_temp_failed": "Couldn't make {folder}: {error}",
@@ -1489,7 +1699,6 @@
"ui.select": "Select",
"ui.selectOther": "Select Other…",
"ui.selectTangentChain": "Select Tangent Chain",
"ui.select_other_hint": "Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there.",
"ui.selection": "Selection",
"ui.selection_modifiers": "Selection modifiers",
"ui.selection_shift_add_only": "Shift+click adds without removing",
@@ -1509,7 +1718,6 @@
"ui.show_constraints": "Constraints",
"ui.show_construction_geometries": "Construction Geometries",
"ui.show_dimensions": "Dimensions",
"ui.show_points": "Show Points",
"ui.show_profile": "Show Profile",
"ui.show_projected_geometries": "Projected Geometries",
"ui.simple": "Simple",
@@ -1520,7 +1728,6 @@
"ui.sketch_grid": "Sketch Grid",
"ui.sketch_modifiers": "Sketch modifiers",
"ui.sketch_scale": "Sketch Scale",
"ui.sketch_status": "Sketch Status",
"ui.sketches": "Sketches",
"ui.slice": "Slice",
"ui.slicer": "Slicer",
@@ -1566,7 +1773,7 @@
"ui.stand_up_tip": "After a STEP or IGES file opens with Z up, offer a quarter turn about X",
"ui.start": "Start",
"ui.start_over": "Start over",
"ui.start_radius": "Start Radius",
"ui.start_page": "Start page",
"ui.stretch": "Stretch",
"ui.supersampling": "Supersampling (SSAA)",
"ui.supersampling_adaptive": "Only when the view is still",
@@ -1616,13 +1823,24 @@
"ui.untitled": "Untitled",
"ui.up_axis_note": "Which axis points up in the views (axis view, Home, Look At). Designs are unchanged.",
"ui.updateToCurrentView": "Update to Current View",
"ui.variable": "Variable",
"ui.view_centre": "View centre",
"ui.visual_shaded_hidden_edges": "Shaded with hidden edges",
"ui.visual_shaded_with_edges": "Shaded with edges",
"ui.visual_style": "Visual style",
"ui.visual_wireframe": "Wireframe",
"ui.workspace": "workspace",
"ui.workspace_animation_description": "Exploded views, motion studies and storyboards",
"ui.workspace_available": "Available",
"ui.workspace_current": "Current",
"ui.workspace_design_description": "Sketch, model, assemble and inspect",
"ui.workspace_design_tabs": "Model · Surface · Assemble · Insert · Inspect · Manage · View",
"ui.workspace_drawing_description": "2D drawings with views, dimensions and title blocks",
"ui.workspace_footer": "Every workspace works on the same design, history and selection; only the ribbon tabs change. The switcher is locked while a sketch or feature is being edited.",
"ui.workspace_manufacture_description": "Slicing, toolpaths and setup",
"ui.workspace_planned": "Planned",
"ui.workspace_render_description": "Product preview, scene settings and rendered images",
"ui.workspace_render_tabs": "Scene · Image",
"ui.workspace_simulation_description": "Loads, constraints and results",
"ui.x_distance": "X Distance",
"ui.y_distance": "Y Distance",
"ui.y_up": "Y up",
@@ -1632,7 +1850,6 @@
"ui.zoom_to_the_cursor": "Zoom to the cursor",
"update.available": "SolidCraft {version} is available",
"update.button": "Update",
"update.button_tip": "SolidCraft {version} is available",
"update.button_version": "Update to {version}",
"update.check_automatically": "Check for updates automatically",
"update.check_now": "Check now",
@@ -1664,5 +1881,6 @@
"update.title": "Update available",
"update.up_to_date": "SolidCraft is up to date.",
"update.update_now": "Update now",
"update.you_have": "You have {version}."
"update.you_have": "You have {version}.",
"↑ ↓ choose · Enter / Tab complete · Esc dismiss": "↑ ↓ choose · Enter / Tab complete · Esc dismiss"
}
+362 -39
View File
File diff suppressed because it is too large Load Diff
+298 -39
View File
@@ -3,11 +3,14 @@
"1. Open GitHub draft below, then paste the report copied to your clipboard.": "1. Ouvrir le brouillon GitHub ci-dessous, puis coller le rapport copié dans le presse-papiers.",
"1. Open GitHub draft below. Your title and report will be filled in.": "1. Ouvrir le brouillon GitHub ci-dessous. Votre titre et votre rapport y seront déjà remplis.",
"2. Attach your screenshot: copy and paste it, or save and upload the PNG.": "2. Joindre votre capture d'écran : la copier-coller, ou enregistrer le PNG puis le téléverser.",
"2D": "2D",
"3. Click Submit new issue on GitHub to send your report.": "3. Cliquer sur Submit new issue sur GitHub pour envoyer votre rapport.",
"A new configuration, a copy of the active one": "Nouvelle configuration, copie de la configuration active",
"AI": "IA",
"ARRANGE": "DISPOSER",
"About SolidCraft": "À propos de SolidCraft",
"Acceleration": "Accélération",
"Access": "Accès",
"Actions": "Actions",
"Active": "Actif",
"Active rule": "Règle active",
"Add": "Ajouter",
@@ -15,6 +18,7 @@
"Add Mid Radius": "Ajouter un rayon intermédiaire",
"Add Row": "Ajouter une ligne",
"Add condition": "Ajouter une condition",
"Add or edit rule": "Ajouter ou modifier une règle",
"Against": "Contre",
"Alignment": "Alignement",
"All Sketch Points": "Tous les points d'esquisse",
@@ -28,20 +32,33 @@
"Applied Radius": "Rayon appliqué",
"Apply To": "Appliquer à",
"Apply to selection": "Appliquer à la sélection",
"Apply your appearance first. Exports assigned appearances and materials created in this design.": "Appliquez d’abord votre apparence. Exporte les apparences attribuées et les matériaux créés dans cette conception.",
"Arc length": "Longueur d'arc",
"Area": "Aire",
"As-Built": "Tel que construit",
"Assign Rule": "Attribuer une règle",
"Attachment": "Rattachement",
"BLEND SURFACE": "SURFACE DE RACCORD",
"BOUNDARY SURFACE": "SURFACE FRONTIÈRE",
"BROWSER": "NAVIGATEUR",
"Bend Line Position": "Position de la ligne de pli",
"Bend Position": "Position du pli",
"Bend Radius": "Rayon de pliage",
"Bend Table": "Table de pliage",
"Bends": "Plis",
"Bodies": "Corps",
"Body": "Corps",
"Boundary": "Contour",
"Bounding box": "Boîte englobante",
"CHECK": "VÉRIFIER",
"COMPONENTS": "COMPOSANTS",
"CONFIGURATIONS": "CONFIGURATIONS",
"CONSTRAIN": "CONTRAINDRE",
"CONTACT": "CONTACT",
"CONVERT": "CONVERTIR",
"CSV or JSON: updates existing parameters, adds new ones": "CSV ou JSON : met à jour les paramètres existants, ajoute les nouveaux",
"Capture All Positions": "Capturer toutes les positions",
"Capture Pos.": "Capturer pos.",
"Catch Direction": "Direction de l'ergot",
"Cells": "Cellules",
"Center X": "Centre X",
@@ -52,15 +69,19 @@
"Check collisions": "Vérifier les collisions",
"Checked": "Vérifié",
"Choose a new plane. Sketch geometry and dimensions stay unchanged in its two-dimensional coordinates.": "Choisir un nouveau plan. La géométrie et les cotes de l'esquisse restent inchangées dans ses coordonnées bidimensionnelles.",
"Choose…": "Choisir…",
"Circular": "Circulaire",
"Circumference": "Circonférence",
"Close draft": "Fermer le brouillon",
"Collisions": "Collisions",
"Colour": "Couleur",
"Comment": "Commentaire",
"Component1": "Composant1",
"Config Table": "Table de config.",
"Configuration": "Configuration",
"Configuration rules": "Règles de configuration",
"Construction": "Construction",
"Contact": "Contact",
"Contact Sets": "Jeux de contacts",
"Contact sets": "Jeux de contact",
"Continuity": "Continuité",
"Copy Error Message": "Copier le message d'erreur",
@@ -78,15 +99,17 @@
"Cube": "Cube",
"Current orthographic view (1:1 mm)": "Vue orthographique actuelle (1:1 mm)",
"Current perspective view": "Vue en perspective actuelle",
"Curv. Comb": "Peigne de courbure",
"Curvature (G2)": "Courbure (G2)",
"Curvature change (G3)": "Variation de courbure (G3)",
"Customize Toolbar": "Personnaliser la barre d'outils",
"Delete": "Supprimer",
"Delete all": "Tout supprimer",
"Delete rule": "Supprimer la règle",
"Delete the row": "Supprimer la ligne",
"Delete the set": "Supprimer le jeu",
"Delete this parameter": "Supprimer ce paramètre",
"Density": "Masse volumique",
"Dependents": "Dépendants",
"Designs you open or save appear here.": "Les conceptions que vous ouvrez ou enregistrez apparaissent ici.",
"Determined by To Object. Choose Angle to edit the angular extent.": "Déterminé par Jusqu'à l'objet. Choisir Angle pour modifier l'étendue angulaire.",
"Direction": "Direction",
@@ -98,49 +121,72 @@
"Drive Joint": "Piloter la liaison",
"Duration": "Durée",
"Edges": "Arêtes",
"Edit": "Modifier",
"Edit Profile Sketch": "Modifier l'esquisse du profil",
"Enable rule": "Activer la règle",
"Enabled": "Activé",
"End bulge": "Renflement de fin",
"End continuity": "Continuité de fin",
"Explode": "Éclater",
"Explode Text": "Décomposer le texte",
"Export Animation…": "Exporter l'animation…",
"Export CSV…": "Exporter CSV…",
"Export Current View as DXF": "Exporter la vue actuelle en DXF",
"Export Current View as DXF…": "Exporter la vue actuelle en DXF…",
"Export DXF": "Exporter en DXF",
"Export DXF…": "Exporter en DXF…",
"Export JSON…": "Exporter JSON…",
"Export hidden edges as dashed lines": "Exporter les arêtes cachées en tirets",
"Expression": "Expression",
"Extend to meet": "Prolonger jusqu'à la rencontre",
"Extents": "Étendues",
"FILES": "FICHIERS",
"FLAT": "DÉPLIÉ",
"Face": "Face",
"Face attachment lost": "Rattachement à la face perdu",
"Faces": "Faces",
"Faces / edges / vertices": "Faces / arêtes / sommets",
"Fastener": "Fixation",
"Favorites": "Favoris",
"Favourite": "Favori",
"Feature": "Fonction",
"Files from many CAD programs have Y up, so they open lying on their back here (Z up).": "Les fichiers de nombreux logiciels de CAO ont Y vers le haut : ils s'ouvrent donc couchés ici (Z vers le haut).",
"Filter": "Filtrer",
"Find in Browser": "Trouver dans le navigateur",
"Flat": "Plat",
"Finish": "Terminer",
"Flat Pattern": "Dépliage",
"Flat size": "Dimensions à plat",
"Follow camera (Look At sketches)": "Suivre la caméra (vue normale aux esquisses)",
"Follow the camera (Look At sketches)": "Suivre la caméra (vue normale aux esquisses)",
"Font": "Police",
"From Bodies": "À partir de corps",
"From edges": "Arêtes de départ",
"Full Length": "Longueur totale",
"Gap": "Jeu",
"Grid": "Grille",
"HISTORY": "HISTORIQUE",
"Height": "Hauteur",
"Hidden edges": "Arêtes cachées",
"Holes": "Trous",
"Holes (edges on them)": "Trous (par leurs arêtes)",
"Home view": "Vue d'accueil",
"Horiz./Vert.": "Horiz./Vert.",
"IMAGES": "IMAGES",
"Import…": "Importer…",
"In This Design": "Dans cette conception",
"In place (surfaces)": "En place (surfaces)",
"In plane": "Dans le plan",
"Inside": "Intérieur",
"Interferes": "Interfère",
"Isocurve": "Isoparamétrique",
"Issue title": "Titre du ticket",
"JOINTS": "LIAISONS",
"Joint Origin1": "Origine de liaison1",
"Keep (points on it)": "À conserver (points dessus)",
"Keep Tools": "Conserver les outils",
"Keep of the other surface": "À conserver de l'autre surface",
"LIBRARY": "BIBLIOTHÈQUE",
"LINKS": "LIENS",
"Later": "Plus tard",
"Leave it": "Laisser tel quel",
"Legacy face attachment. Rebinding to a certified face creates an exact attachment.": "Rattachement à la face hérité. Le relier à une face certifiée crée un rattachement exact.",
@@ -148,47 +194,70 @@
"Length units": "Unités de longueur",
"Letter Spacing": "Espacement des lettres",
"Library": "Bibliothèque",
"Library file": "Fichier de bibliothèque",
"Line Spacing": "Interligne",
"Local library": "Bibliothèque locale",
"Loop": "Boucle",
"MANAGE": "GÉRER",
"MATERIALS": "MATÉRIAUX",
"MODEL": "MODÈLE",
"MOTION": "MOUVEMENT",
"Margin": "Marge",
"Mass": "Masse",
"Mass Center": "Centre de masse",
"Matched restrictions check the final values.": "Les restrictions dont les conditions sont remplies vérifient les valeurs finales.",
"Material": "Matériau",
"Max": "Max",
"Mid Radius": "Rayon intermédiaire",
"Middle double-click": "Double-clic milieu",
"Min": "Min",
"Mitre": "Onglet",
"Model Parameters": "Paramètres du modèle",
"Modeled": "Modélisé",
"More toolbar panels": "Autres groupes de la barre d'outils",
"Motion": "Mouvement",
"Move / Copy": "Déplacer / Copier",
"Move Type": "Type de déplacement",
"Move down": "Déplacer vers le bas",
"Move earlier": "Déplacer plus tôt",
"Move later": "Déplacer plus tard",
"Move up": "Déplacer vers le haut",
"Name": "Nom",
"Nearest": "Corps le plus proche",
"New": "Nouveau",
"New Comp.": "Nouv. comp.",
"New Sketch": "Nouvelle esquisse",
"New rule…": "Nouvelle règle…",
"New study": "Nouvelle étude",
"No crash reports.": "Aucun rapport de plantage.",
"No matching appearance presets.": "Aucun préréglage d’apparence correspondant.",
"No matching appearances": "Aucune apparence correspondante",
"No visible bodies to export.": "Aucun corps visible à exporter.",
"None": "Aucun",
"Normal to sketch": "Normale à l'esquisse",
"Not available yet": "Pas encore disponible",
"Notch": "Encoche",
"OUTPUT": "SORTIE",
"Opacity": "Opacité",
"Open GitHub draft": "Ouvrir le brouillon GitHub",
"Open local libraries in the desktop app. You can export this design's library here.": "Ouvrez les bibliothèques locales dans l’application de bureau. Vous pouvez exporter ici la bibliothèque de cette conception.",
"Open-source parametric 3D CAD in Rust. Licensed MIT OR Apache-2.0.": "CAO 3D paramétrique open source en Rust. Sous licence MIT OR Apache-2.0.",
"Operator": "Opérateur",
"Other side": "Autre côté",
"Other surface (mutual trim)": "Autre surface (ajustement mutuel)",
"Outside": "Extérieur",
"Overall length": "Longueur hors tout",
"Parameter values": "Valeurs des paramètres",
"Param. Graph": "Graphe param.",
"Parameter": "Paramètre",
"Parts": "Pièces",
"Pass through": "Passer par",
"Path": "Trajectoire",
"Pattern": "Répétition",
"Per Edge": "Par arête",
"Pick the tool bodies": "Choisir les corps outils",
"Picked": "Sélectionnés",
"Picked point": "Point choisi",
"Place at the origin instead": "Placer plutôt à l'origine",
"Plastic Rules": "Règles plastique",
"Playback": "Lecture",
"Plot": "Graphique",
"Position": "Position",
@@ -199,10 +268,11 @@
"Preview Row": "Aperçu de la ligne",
"Preview leaves the current design unchanged.": "L'aperçu ne modifie pas la conception actuelle.",
"Profiles/Curves": "Profils/Courbes",
"Projection": "Projection",
"Quantity": "Grandeur",
"RECENT": "RÉCENTS",
"REFERENCE": "RÉFÉRENCE",
"RULED SURFACE": "SURFACE RÉGLÉE",
"RULES": "RÈGLES",
"Radius Fit": "Ajustement du rayon",
"Ratio": "Rapport",
"Reattach selected face": "Rattacher la face sélectionnée",
@@ -220,6 +290,8 @@
"Remove this point": "Retirer ce point",
"Report a bug — review a screenshot and open a GitHub draft": "Signaler un bug — relire une capture d'écran et ouvrir un brouillon GitHub",
"Reset Toolbar": "Réinitialiser la barre d'outils",
"Resize Part": "Redimensionner la pièce",
"Restore View": "Restaurer la vue",
"Restore outer boundary": "Restaurer le contour extérieur",
"Restore the inherited component or physical material appearance": "Rétablir l'apparence héritée du composant ou du matériau physique",
"Result": "Résultat",
@@ -228,18 +300,27 @@
"Reverse Direction": "Inverser la direction",
"Revert": "Rétablir",
"Revert All Positions": "Rétablir toutes les positions",
"Revert Pos.": "Rétablir pos.",
"Revert Position": "Rétablir la position",
"Review the report and screenshot before opening GitHub.": "Relisez le rapport et la capture d'écran avant d'ouvrir GitHub.",
"Rim Edge": "Arête du bord",
"Roll History Marker Here": "Placer le marqueur d'historique ici",
"Round": "Arrondi",
"Rule": "Règle",
"Rule name": "Nom de la règle",
"Rules run from top to bottom. Matched drives change effective values.": "Les règles s’exécutent de haut en bas. Les règles de pilotage dont les conditions sont remplies modifient les valeurs effectives.",
"SECTION": "COUPE",
"SURFACE QUALITY": "QUALITÉ DE SURFACE",
"Save Rule": "Enregistrer la règle",
"Save View": "Enregistrer la vue",
"Save the study as an animated GIF, seen as the viewport shows it": "Enregistrer l'étude en GIF animé, telle que la fenêtre d'affichage la montre",
"Saved preview. Geometry is being checked before editing.": "Aperçu enregistré. La géométrie est en cours de vérification avant modification.",
"Scale factor": "Facteur d'échelle",
"Scene": "Scène",
"Search": "Rechercher",
"Search commands": "Rechercher des commandes",
"Search this library": "Rechercher dans cette bibliothèque",
"Section": "Coupe",
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "Sélectionner un autre élément : maintenir le bouton gauche immobile sur le modèle (ou clic droit › Sélectionner un autre…) pour lister tout ce qui se trouve sous le curseur ; cliquer de nouveau au même endroit pour sélectionner l'élément suivant. Double-cliquer avec le bouton du milieu pour revenir à l'accueil (Préférences › Navigation).",
"Select bodies (a face picks its body)": "Sélectionner des corps (une face sélectionne son corps)",
"Select bodies or faces instead": "Sélectionner plutôt des corps ou des faces",
@@ -247,6 +328,7 @@
"Semantic faces share their appearance across instances.": "Les faces sémantiques partagent leur apparence entre les instances.",
"Send your report on GitHub": "Envoyer votre rapport sur GitHub",
"Shape": "Forme",
"Sheet Rules": "Règles de tôlerie",
"Show Agent Cursor": "Afficher le curseur de l'agent",
"Show in folder": "Afficher dans le dossier",
"Show the pointer an AI agent moves (#35)": "Afficher le pointeur déplacé par un agent IA (#35)",
@@ -260,10 +342,13 @@
"Sketch points": "Points d'esquisse",
"Slot width": "Largeur de rainure",
"SolidCraft closed before these designs were saved. Their last autosave can be opened.": "SolidCraft s'est fermé avant l'enregistrement de ces conceptions. Leur dernier enregistrement automatique peut être ouvert.",
"Specialise": "Spécialiser",
"Speed": "Vitesse",
"Stand it up": "Redresser",
"Stand it up: a quarter turn about X, as one Move feature you can undo.": "Redresser : un quart de tour autour de X, sous forme d'une fonction Déplacer que vous pouvez annuler.",
"Stand this part up?": "Redresser cette pièce ?",
"Start bulge": "Renflement de début",
"Start continuity": "Continuité de début",
"Step": "Étape",
"Steps": "Étapes",
"Stitch the kept pieces": "Coudre les parties conservées",
@@ -296,17 +381,19 @@
"Turn off section analysis before exporting the view.": "Désactivez l'analyse de coupe avant d'exporter la vue.",
"Type": "Type",
"UNTRIM": "ANNULER L'AJUSTEMENT",
"Unit": "Unité",
"Updating the preview": "Mise à jour de l'aperçu",
"Use active author placement": "Utiliser le placement d'auteur actif",
"Use fixed plane": "Utiliser un plan fixe",
"Use saved fixed plane": "Utiliser le plan fixe enregistré",
"Use selected face as fixed plane": "Utiliser la face sélectionnée comme plan fixe",
"User Parameters": "Paramètres utilisateur",
"VIEW": "VUE",
"VIEWS": "VUES",
"Value": "Valeur",
"Velocity": "Vitesse",
"Vertices": "Sommets",
"View navigator": "Repère de navigation",
"Visible bodies only. Curves use line segments (0.01 mm tolerance).": "Corps visibles uniquement. Les courbes sont approchées par des segments de droite (tolérance de 0,01 mm).",
"Volume": "Volume",
"Wall": "Paroi",
"Wall Location": "Position de la paroi",
@@ -314,6 +401,13 @@
"When": "Quand",
"Width": "Largeur",
"Will fail": "Échouera",
"X angle": "Angle X",
"X distance": "Distance X",
"Y angle": "Angle Y",
"Y distance": "Distance Y",
"Z angle": "Angle Z",
"Z distance": "Distance Z",
"Zebra": "Zébrures",
"angle, height": "angle, hauteur",
"appearance.dielectric_ior": "Indice de réfraction (diélectrique)",
"appearance.metallic": "Métallique",
@@ -322,7 +416,13 @@
"appearance.roughness": "Rugosité",
"appearance.surface_response": "Réponse de la surface",
"browser.body_name": "Corps{number}",
"browser.calibrate_title": "Calibrer : {name}",
"browser.capture_many": "{count} composants ont été déplacés, mais leurs positions ne sont pas capturées.",
"browser.capture_note": "Capturer la position conserve la nouvelle position ; Rétablir la position restaure la précédente.",
"browser.capture_one": "Un composant a été déplacé, mais sa position n’est pas capturée.",
"browser.component_name": "Composant{number}",
"browser.edit_canvas_title": "Modifier la toile : {name}",
"browser.move_title": "Déplacer : {name}",
"browser.parameters": "Paramètres ({count})",
"browser.precision": "Précision : {precision}",
"browser.sketch_name": "Esquisse{number}",
@@ -337,6 +437,7 @@
"cmd.appearance_assign": "Apparence",
"cmd.appearance_library": "Bibliothèque d'apparences",
"cmd.appearance_list": "Lister les apparences",
"cmd.assembly_bom": "Nomenclature",
"cmd.body_rename": "Renommer le corps",
"cmd.browser_group": "Nouveau groupe",
"cmd.browser_move": "Déplacer des éléments du navigateur",
@@ -358,10 +459,13 @@
"cmd.component_insert_design": "Insérer une conception",
"cmd.component_links": "Lister les liens",
"cmd.component_list": "Lister les composants",
"cmd.component_metadata": "Propriétés de la pièce",
"cmd.component_metadata_set": "Modifier les propriétés de la pièce",
"cmd.component_move_bodies": "Déplacer les corps vers le composant",
"cmd.component_move_sketches": "Déplacer les esquisses vers le composant",
"cmd.component_paste_new": "Coller comme nouveau",
"cmd.component_properties": "Propriétés physiques du composant",
"cmd.component_relink": "Relier à nouveau la source",
"cmd.component_rename": "Renommer le composant",
"cmd.component_revert_position": "Rétablir la position",
"cmd.component_unlock": "Déverrouiller le contenu",
@@ -413,6 +517,7 @@
"cmd.edit_checkpoint": "Point de restauration",
"cmd.edit_restore_checkpoint": "Revenir au point de restauration",
"cmd.engine_commands": "Lister les commandes",
"cmd.engine_describe": "Décrire la commande",
"cmd.explode_create": "Vue éclatée",
"cmd.explode_delete": "Supprimer la vue éclatée",
"cmd.explode_list": "Lister les vues éclatées",
@@ -445,6 +550,7 @@
"cmd.joint_drive": "Piloter les liaisons",
"cmd.joint_edit": "Modifier la liaison",
"cmd.joint_gear": "Engrenage",
"cmd.joint_inspect": "Inspecter la liaison",
"cmd.joint_limits": "Limites de liaison",
"cmd.joint_list": "Lister les liaisons",
"cmd.joint_motion_link": "Lien de mouvement",
@@ -603,6 +709,7 @@
"cmd.sketch_spline_fit_point": "Spline par points",
"cmd.sketch_spun_profile": "Profil de révolution",
"cmd.sketch_toggle_driven": "Basculer en cote pilotée",
"cmd.sketch_trim_path": "Ajuster le long du chemin",
"cmd.solid_align": "Aligner",
"cmd.solid_boundary_fill": "Remplissage de frontière",
"cmd.solid_bounding_solid": "Solide englobant",
@@ -653,7 +760,15 @@
"cmd.view_navigate": "Naviguer dans la vue",
"cmd.view_rename": "Renommer la vue nommée",
"cmd.view_save": "Nouvelle vue nommée",
"command name": "nom de la commande",
"color.copy": "Cliquer pour copier les valeurs de couleur",
"color.edit": "Cliquer pour modifier la couleur",
"color.gamma": "Valeurs de couleur affichées en espace gamma 0-255",
"color.hue": "Teinte",
"color.linear": "Valeurs de couleur affichées en espace linéaire 0-1",
"color.saturation_value": "Saturation et valeur",
"color.selected": "Couleur sélectionnée",
"contact.components_count": "{count} composants",
"contact.enabled_label": "activé",
"coupling.gear_1": "Engrenage 1",
"coupling.gear_1_pick": "Engrenage 1 (facultatif)",
"coupling.gear_2": "Engrenage 2",
@@ -670,6 +785,21 @@
"coupling.rack": "Crémaillère",
"coupling.radius_1": "Rayon 1",
"coupling.radius_2": "Rayon 2",
"crash.announce": "SolidCraft s’est fermé de façon inattendue ou a rencontré une erreur interne la dernière fois. Un rapport de plantage a été enregistré (Aide > Rapports de plantage…).",
"crash.copy_folder": "Copier le chemin du dossier",
"crash.copy_path": "Copier le chemin",
"crash.deleted": "{count} rapport(s) supprimé(s).",
"crash.detected_time": "Détecté {time} UTC",
"crash.intro": "Les rapports de plantage restent sur cet ordinateur et ne sont jamais envoyés. Signaler… ouvre un brouillon que vous relisez avant tout partage.",
"crash.kind_command": "Erreur interne dans une commande",
"crash.kind_internal": "Erreur interne",
"crash.kind_kernel": "Erreur du noyau (récupérée)",
"crash.kind_unclean": "Fermeture inattendue",
"crash.previous_session": "Une session précédente de SolidCraft s’est terminée de façon inattendue ou a rencontré une erreur interne. Les rapports de sorties anormales sont détectés au démarrage. Voir Aide > Rapports de plantage… pour plus de détails.",
"crash.replayable": "rejouable",
"crash.report": "Signaler…",
"crash.unclean_explanation": "Détecté lors d’un lancement ultérieur ; l’heure exacte de la sortie n’a pas été enregistrée. Un arrêt du système ou une interruption forcée peut aussi provoquer une sortie anormale.",
"delete.and_more": "… et {count} de plus",
"dialog.bodies_count": "{count} corps",
"dialog.clearance_invalid": "le jeu doit être une longueur positive ou nulle",
"dialog.compute": "Calculer",
@@ -677,6 +807,8 @@
"dialog.computing": "Calcul… {seconds} s",
"dialog.could_not_be_checked": "non vérifiable",
"dialog.custom_axis": "↖ Axe personnalisé",
"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.select": "↖ Sélectionner",
"dialog.selected_count": "↖ {count} sélectionné(s)",
@@ -700,9 +832,30 @@
"dxf.progress.write": "Écriture du DXF (5/5)",
"dxf.tolerance": "Tolérance des courbes",
"dxf.tolerance_help": "Corps visibles uniquement. Les courbes sont approchées par des segments de droite. Une tolérance plus faible donne des courbes plus fines, au prix de plus de temps et de mémoire.",
"expression, e.g. 25 mm": "expression, par ex. 25 mm",
"file_load.built": "Construit jusqu’ici : {built} entités sur {total}.",
"file_load.converting": "Conversion du solide {done} sur {total}",
"file_load.inserting": "Insertion de la géométrie",
"file_load.opening": "Ouverture de la conception",
"file_load.preparing_thumbnail": "Préparation de la miniature...",
"file_load.preparing_view": "Préparation de la vue...",
"file_load.preview_note": "Aperçu enregistré. La géométrie est en cours de vérification avant modification.",
"file_load.reading": "Lecture du fichier...",
"file_load.reading_percent": "Lecture du fichier... {percent} %",
"file_load.rebuilding": "Reconstruction de l’entité {done} sur {total}",
"file_load.recovering": "Récupération de la conception",
"file_load.stopping": "Attente de l’arrêt de l’opération précédente...",
"file_load.waiting": "En attente d’une opération sur fichier",
"fits the face, e.g. M8": "adapté à la face, par ex. M8",
"from the rule": "selon la règle",
"help.apache": "Licence Apache 2.0",
"help.attributions": "Attributions (ATTRIBUTION.md)",
"help.build": "Version {version} · commit {commit} · compilée le {date}",
"help.font_details": "L’interface utilise les polices intégrées d’egui (Ubuntu Light et Hack, sous les licences Ubuntu Font Licence et MIT/Bitstream Vera ; Noto Emoji et la police d’icônes emoji, sous OFL et MIT). Le texte des esquisses utilise Liberation Sans (SIL OFL 1.1). Le pack de polices facultatif de The Crafting Apps, craft-fonts (BIZ UDPGothic, Shippori Mincho, BIZ UDMincho, Noto Sans CJK SC, Noto Sans Arabic ; SIL OFL 1.1), n’est pas inclus dans les versions de SolidCraft.",
"help.fonts": "Polices",
"help.licences": "Licences",
"help.mit": "Licence MIT",
"help.notice": "NOTICE",
"help.third_party": "Licences tierces",
"home.documentation": "Documentation",
"home.in_this_browser": "dans ce navigateur",
"home.missing": "introuvable",
@@ -728,13 +881,51 @@
"install.menu": "Installer l'app…",
"install.steps_intro": "Safari installe les apps web depuis ses propres menus :",
"install.title": "Installer SolidCraft",
"issue.body_label": "Texte du rapport",
"issue.note_image_copied": "Collez l’image dans le brouillon GitHub, ou enregistrez le PNG si la copie d’image n’est pas disponible.",
"issue.note_opened": "Relisez le brouillon GitHub et joignez la capture d’écran. Le bouton Submit new issue de GitHub le publie.",
"issue.note_opened_copied": "Rapport copié. Collez-le dans le brouillon GitHub, joignez la capture d’écran, puis cliquez sur Submit new issue sur GitHub.",
"issue.note_removed": "Capture d’écran retirée de ce brouillon.",
"issue.note_report_copied": "Rapport copié. Collez-le dans le brouillon GitHub si nécessaire.",
"issue.note_save_failed": "Impossible d’enregistrer la capture d’écran : {error}",
"issue.note_saved": "Capture d’écran enregistrée. Joignez le PNG dans le brouillon GitHub.",
"issue.save_screenshot": "Enregistrer la capture d’écran…",
"joint.from_bodies_hint": "Chaque corps sélectionné passe dans un nouveau composant à lui et reste où il est.",
"joint.one_body": "Les deux sélections sont sur {body}. Choisissez la seconde sur une autre pièce.",
"joint.one_component": "Ces pièces sont des corps d'un même composant : {bodies}. Une liaison déplace un composant par rapport à un autre, chaque pièce a donc besoin de son propre composant.",
"keymap.command_id": "ID de commande",
"keymap.empty": "Aucune commande correspondante",
"keymap.use_anyway": "Utiliser quand même {key} ici",
"manage.appearance_hint": "Choisissez une apparence pour la sélection ou faites glisser un échantillon sur un corps ou une face.",
"manage.assign_hint": "Sélectionnez des corps et choisissez leur règle de conception plastique.",
"manage.config_hint": "Modifiez les lignes, colonnes et règles de configuration, puis activez une ligne ou affichez son aperçu.",
"manage.empty": "Aucune ligne correspondante.",
"manage.material_hint": "Sélectionnez des corps et choisissez leur matériau physique.",
"manage.parameters_hint": "Modifiez les expressions et les unités. Les modifications s’appliquent avec Entrée ou lorsque vous quittez un champ.",
"manage.rules_hint": "Choisissez une règle à modifier ou créez-en une. Confirmez pour enregistrer la règle.",
"measure.at": "À",
"measure.item": "Élément {number}",
"messages.ago_h": "il y a {n} h",
"messages.ago_min": "il y a {n} min",
"messages.ago_s": "il y a {n} s",
"messages.chip_hint": "Cliquer pour voir tous les messages de cette session",
"messages.clear": "Effacer",
"messages.copy_all": "Tout copier",
"messages.menu": "Messages…",
"messages.none": "Aucun message pour l’instant.",
"messages.title": "Messages",
"motion.checked_to": "Vérifié jusqu'à l'étape {step} seulement : la vérification a épuisé son budget de calcul",
"motion.collision": "{a} · {b} : étapes {steps}",
"motion.delete_key": "Supprimer l’image clé",
"motion.duration_hint": "secondes (vide : par étape)",
"motion.key_here": "Placer une image clé ici",
"motion.key_here_hint": "Ajouter une image clé à cette étape avec la valeur actuelle de la liaison",
"motion.no_collisions": "Aucune collision au cours de l'étude",
"motion.step_back": "Étape précédente",
"motion.step_forward": "Étape suivante",
"motion.to_end": "À la fin",
"motion.to_start": "Au début",
"motion.track_name": "Piste {number}",
"nav.touchpad.help": "Maintenir Maj et déplacer le pointeur pour faire un panoramique ; maintenir Alt/Option et le déplacer pour orbiter. Aucun bouton de souris n'est nécessaire. Le défilement vertical zoome.",
"nav.touchpad.label": "Pavé tactile",
"nav.touchpad.orbit": "Orbite (maintenir Alt/Option et déplacer ; sans bouton de souris)",
@@ -743,13 +934,13 @@
"nav.touchpad.zoom": "Zoom (défilement vertical ou pincement)",
"needs two joints": "nécessite deux liaisons",
"new configuration": "nouvelle configuration",
"new name": "nouveau nom",
"new parameter": "nouveau paramètre",
"no hole": "sans perçage",
"no interference": "aucune interférence",
"no joint that moves yet": "aucune liaison mobile pour l'instant",
"no picture": "aucune image",
"none": "aucun",
"parts.automatic_name": "automatique",
"pattern.alignment_2": "Alignement 2",
"pattern.alignment_hint": "La quantité inclut l’original. Le centrage répartit les copies de chaque côté ; si la quantité est paire, l’inversion choisit le côté qui reçoit la copie supplémentaire.",
"pattern.direction_1": "Direction 1",
@@ -757,12 +948,19 @@
"pattern.flip_direction": "Inverser la direction",
"pick a hole to seat it, or OK places it at the origin": "choisir un trou pour la loger, ou OK la place à l'origine",
"pick one or two vertices, edges or faces": "choisir un ou deux sommets, arêtes ou faces",
"prefs.cpu": "CPU (logiciel)",
"prefs.minutes_suffix": " min",
"properties.title": "Propriétés : {name}",
"recovery.days_ago": "il y a {count} j",
"recovery.detail": "{file} · {features} entités · enregistrement auto {when}",
"recovery.hours_ago": "il y a {count} h",
"recovery.just_now": "à l’instant",
"recovery.min_ago": "il y a {count} min",
"recovery.never_saved": "jamais enregistré",
"render.background": "Arrière-plan de la scène",
"render.fit": "Cadrer le produit",
"render.image_cancel": "Annuler le rendu",
"render.image_cancelled": "Rendu annulé",
"render.image_cancelling": "Annulation",
"render.image_check": "Vérifier la demande de rendu",
"render.image_completed": "Image prête à exporter",
"render.image_encoding": "Encodage du PNG",
"render.image_export": "Exporter en PNG",
@@ -809,6 +1007,19 @@
"sample.plate.name": "Plaque d'exemple",
"sample.vents.about": "Rainures en réseau rectangulaire : lignes, colonnes et pas sont des paramètres",
"sample.vents.name": "Plaque d'aération",
"search.command_keys": "Entrée exécuter Maj+Entrée exécuter et garder ouvert Échap fermer",
"search.commands": "Commandes",
"search.design": "Dans cette conception",
"search.feature": "Entité",
"search.invalid": "Non valide",
"search.keys": "↑ ↓ choisir Entrée exécuter Tab remplir le nom Échap fermer",
"search.no_match": "Aucune commande ne correspond à « {query} »",
"search.parameter": "Paramètre",
"search.recent": "Récents",
"search.run": "Exécuter la commande",
"search.suggest": "Vouliez-vous dire {label} ?",
"search.try": "Saisissez un nom de commande, une entité de cette conception ou un paramètre. Pour exécuter une commande avec des valeurs, saisissez son id et un objet JSON : {example}",
"search.valid": "Valide",
"settings.display_quality_note": "Les niveaux plus fins affichent plus lisses les faces et arêtes courbes, mais leur maillage prend plus de temps. Seule la vue change, jamais la conception ni les exports.",
"settings.frame_rate_note": "Affiche les images par seconde dans le coin inférieur droit de la vue. Tant qu'elle est activée, la vue est redessinée en continu.",
"settings.measure_axis_distances": "Afficher les cotes X/Y/Z",
@@ -819,17 +1030,30 @@
"settings.reset_intro": "Ces réglages ne fonctionnent pas sur cet ordinateur et empêcheraient SolidCraft de démarrer ; ils ont donc été modifiés :",
"settings.reset_title": "Des réglages ont été modifiés",
"settings.supersampling_note": "Dessine la vue à une résolution plus élevée puis la réduit : arêtes, lignes et grille plus lisses sur les écrans de définition inférieure à la 4K. Cela coûte du temps GPU : environ 2,25× plus de pixels à 1,5× et 4× plus à 2×.",
"shortcut.json_error": "les paramètres doivent être au format JSON : {error}",
"shortcut.no_command": "la commande `{id}` n’existe pas ; le texte avant le JSON doit être un ID de commande",
"shortcut.pin": "Épingler en haut de la boîte S",
"shortcut.unpin": "Désépingler",
"sketch.degrees_of_freedom": "{count} degrés de liberté",
"sketch.dof_left": "{count} degrés de liberté restants",
"sketch.dof_left_one": "{count} degré de liberté restant",
"sketch.draw_curves": "Dessiner en {linetype}",
"sketch.make_selection": "Convertir la sélection en {linetype}",
"sketch.open_ends": "{count} ouvertes",
"sketch.untied_ends": "{count} sans coïncidence",
"sketch_panel.attached": "Attaché à {body} / {path}",
"sketch_panel.author": "Placement d’origine : {path}",
"sketch_panel.dimension_hint": "Saisissez une valeur ou une expression, puis appuyez sur OK.",
"sketch_panel.move_hint": "Saisissez les distances et un angle, puis appuyez sur OK.",
"sketch_panel.redefine_title": "{command} : {name}",
"sketch_panel.sheet_attachment": "Attaché à une face plane d’un corps de tôlerie ; suit ce panneau lorsque la tôle change.",
"sketch_panel.text_hint": "Modifiez le texte et son placement, puis appuyez sur OK.",
"sketch_panel.use_plane": "Utiliser le plan {plane}",
"sketch_text.align_center": "Centre",
"sketch_text.align_left": "Gauche",
"sketch_text.align_right": "Droite",
"sketch_text.bold": "Gras",
"sketch_text.builtin_font": "{family} (intégrée)",
"sketch_text.font_search": "Rechercher des polices",
"sketch_text.italic": "Italique",
"sketch_text.loading_fonts": "Recherche des polices installées…",
"sketch_text.on_path": "Le texte suit sa trajectoire : la courbe le positionne",
@@ -850,6 +1074,7 @@
"timeline.ungroup": "Supprimer les dossiers de fonctions",
"to the face": "jusqu'à la face",
"to the next face": "jusqu'à la face suivante",
"toolbar.customize_note": "Onglet {tab}. Cochez une commande pour lui attribuer un bouton dans la barre d’outils. Vous pouvez aussi faire glisser une ligne du menu déroulant d’un panneau vers la barre d’outils, retirer un bouton en le faisant glisser hors de la barre ou utiliser l’épingle d’une ligne du menu déroulant.",
"toolbar.image": "IMAGE",
"toolbar.render": "RENDU",
"toolbar.render_scene": "SCÈNE",
@@ -858,11 +1083,11 @@
"trace.stopped_by_loop": "Se termine à l'échantillon {sample} : la boucle passant par {joint} ne peut plus se fermer",
"ui.1_degree_of_freedom": "1 degré de liberté",
"ui.3d_mouse_speed": "Vitesse de la souris 3D",
"ui.3d_mouse_windows": "Souris 3D",
"ui.3d_sketch": "Esquisse 3D",
"ui.about": "À propos",
"ui.activate": "Activer",
"ui.active_component": "Composant actif",
"ui.add_configuration": "Ajouter une configuration",
"ui.add_parameter": "Ajouter un paramètre",
"ui.agent_chat": "Chat de l'agent",
"ui.agent_chat_add": "Joindre des fichiers",
"ui.agent_chat_add_file": "Choisir un fichier…",
@@ -904,6 +1129,7 @@
"ui.agent_chat_file_error": "{name} : {error}",
"ui.agent_chat_file_heic": "{name} : les images HEIC ne peuvent pas être lues ici ; les exporter d'abord en JPEG ou PNG",
"ui.agent_chat_file_importing": "Importation dans la conception…",
"ui.agent_chat_file_kinds": "{name} : vous pouvez joindre des images (PNG, JPEG, GIF, WebP, BMP, TIFF), des fichiers texte (SVG compris) et des fichiers CAO (STEP, IGES, FreeCAD, Rhino, STL, 3MF, OBJ, glTF)",
"ui.agent_chat_file_loading": "Lecture du fichier…",
"ui.agent_chat_file_not_a_file": "{name} : n'est pas un fichier",
"ui.agent_chat_file_not_a_picture": "{name} : n'est pas une image",
@@ -926,7 +1152,7 @@
"ui.agent_chat_model_tip": "Le modèle utilisé par l'agent. Votre choix est mémorisé pour cet agent.",
"ui.agent_chat_new": "Nouveau chat",
"ui.agent_chat_new_confirm": "Démarrer un nouveau chat ? Le chat actuel sera effacé.",
"ui.agent_chat_new_yes": "Nouveau chat",
"ui.agent_chat_next_picture": "Image suivante",
"ui.agent_chat_no_cli": "solidcraft-cli est introuvable à côté de l'application : l'agent ne peut donc pas accéder à la conception.",
"ui.agent_chat_no_control": "Le point d'accès du chat de l'agent n'a pas démarré : l'agent ne peut donc pas accéder à la conception.",
"ui.agent_chat_no_images": "Cet agent n'accepte pas les images",
@@ -942,6 +1168,7 @@
"ui.agent_chat_placeholder": "Demander à l'agent de modifier la conception…",
"ui.agent_chat_placeholder_busy": "Mettre un message en file d'attente pour quand l'agent aura terminé (Ctrl+Enter : envoyer maintenant)",
"ui.agent_chat_pop_out": "Ouvrir dans une fenêtre séparée, déplaçable vers un autre écran",
"ui.agent_chat_previous_picture": "Image précédente",
"ui.agent_chat_privacy": "Rien n'est envoyé ailleurs qu'à l'agent que vous lancez, sous votre propre compte.",
"ui.agent_chat_privacy_note": "SolidCraft n'envoie rien nulle part : l'agent que vous lancez dialogue avec son propre service, sous votre propre compte. SolidCraft ne voit ni ne stocke jamais de clé ni de mot de passe.",
"ui.agent_chat_queue": "Mettre en file",
@@ -997,6 +1224,7 @@
"ui.ai_bad_url": "Saisir une URL commençant par http:// ou https://",
"ui.ai_base_url": "URL de base",
"ui.ai_button": "IA",
"ui.ai_command_hint": "my-agent --acp",
"ui.ai_copy_suffix": "copie",
"ui.ai_custom_url": "URL personnalisée",
"ui.ai_default": "Par défaut",
@@ -1017,8 +1245,10 @@
"ui.ai_mode_edits_note": "Les commandes de modélisation s'exécutent sans demander ; l'enregistrement, l'ouverture, l'exportation et les autres actions sur les fichiers ou destructrices demandent toujours.",
"ui.ai_mode_global": "Comme défini ci-dessus",
"ui.ai_model": "Modèle",
"ui.ai_model_hint": "qwen3:14b",
"ui.ai_modes_scope": "Ces réglages concernent les outils de SolidCraft. Les outils propres à un agent (son shell, ses modifications de fichiers) suivent le mode de l'agent, que vous choisissez dans l'en-tête du panneau de chat lorsque l'agent propose des modes.",
"ui.ai_name": "Nom",
"ui.ai_name_hint": "Mon agent",
"ui.ai_need_command": "Saisir la commande qui lance l'agent",
"ui.ai_need_model": "Saisir le nom du modèle",
"ui.ai_no_answer": "L'agent n'a pas répondu à temps",
@@ -1049,6 +1279,7 @@
"ui.ai_toolbar_button": "Afficher le bouton dans la barre d'outils",
"ui.ai_toolbar_button_note": "Le bouton IA à l'extrémité droite de la barre d'outils. Ctrl+Shift+A et le menu Vue ouvrent toujours le panneau.",
"ui.ai_undo_tip": "Annuler tout ce que l'agent a modifié pour ce message, en une seule étape",
"ui.ai_url_hint": "https://example.com/v1",
"ui.all": "Tout",
"ui.ambient_occlusion": "Occlusion ambiante",
"ui.ambient_occlusion_help": "Ombrage doux aux jonctions des surfaces opaques. Nécessite la vue GPU.",
@@ -1062,7 +1293,8 @@
"ui.anti_aliasing": "Anticrénelage",
"ui.apply": "Appliquer",
"ui.arc": "Arc",
"ui.assemble": "Assembler",
"ui.assistant": "Assistant",
"ui.assistant_waiting": "{count} approbations en attente",
"ui.autosave_every": "Enregistrement automatique toutes les",
"ui.axes": "Axes",
"ui.axis": "Axe",
@@ -1073,6 +1305,11 @@
"ui.bodies_all_if_none": "Corps (tous si aucun)",
"ui.box": "Boîte",
"ui.break": "Fractionner",
"ui.browser_bodies_one": "{count} corps",
"ui.browser_bodies_other": "{count} corps",
"ui.browser_collapse": "Réduire le navigateur",
"ui.browser_expand": "Développer le navigateur",
"ui.browser_title": "Navigateur",
"ui.calibrate": "Étalonner",
"ui.cancel": "Annuler",
"ui.canvases": "Canevas",
@@ -1082,7 +1319,6 @@
"ui.chamfer": "Chanfrein",
"ui.chamfer_type": "Type de chanfrein",
"ui.change_workspace": "Changer d'espace de travail",
"ui.chord_length": "Longueur de corde",
"ui.circle": "Cercle",
"ui.circular_pattern": "Réseau circulaire",
"ui.clear": "Effacer",
@@ -1115,11 +1351,9 @@
"ui.command_done": "{command} terminé",
"ui.command_palette_hint": "Tapez une commande, ex. {example} — ou S pour chercher",
"ui.concentric": "Concentricité",
"ui.configurations": "Configurations",
"ui.conic_curve": "Courbe conique",
"ui.constant": "Constant",
"ui.constraints_conflict": "Contraintes en conflit",
"ui.construct": "Construire",
"ui.construction": "Construction",
"ui.context_menu": "Menu contextuel",
"ui.continuity_g0": "G0 angle vif",
@@ -1145,7 +1379,6 @@
"ui.cylinder": "Cylindre",
"ui.dark": "Sombre",
"ui.default_modelling_orientation": "Orientation de modélisation par défaut",
"ui.degrees_of_freedom": "degrés de liberté",
"ui.delete": "Supprimer",
"ui.depth": "Profondeur",
"ui.design": "Conception",
@@ -1187,7 +1420,6 @@
"ui.equivalent_radius": "Rayon équivalent",
"ui.expand": "Développer",
"ui.exportDxf": "Exporter en DXF…",
"ui.export_ellipsis": "Exporter...",
"ui.export_ellipsis_keymap": "Exporter…",
"ui.export_keymap_tip": "Raccourcis, favoris de la boîte S et barre d'outils, dans un fichier",
"ui.extend": "Prolonger",
@@ -1211,6 +1443,7 @@
"ui.frame_rate": "Fréquence d'images",
"ui.front": "Avant",
"ui.fully_constrained": "Entièrement contrainte",
"ui.function": "Fonction",
"ui.general": "Général",
"ui.graphics": "Graphisme",
"ui.graphics_custom": "Personnalisé",
@@ -1277,7 +1510,6 @@
"ui.heading_sheet_metal": "TÔLERIE",
"ui.heading_shell": "COQUE",
"ui.heading_sketch": "ESQUISSE",
"ui.heading_sketch_palette": "PALETTE D'ESQUISSE",
"ui.heading_sketch_scale": "ÉCHELLE D'ESQUISSE",
"ui.heading_solid": "SOLIDE",
"ui.heading_sphere": "SPHÈRE",
@@ -1296,6 +1528,7 @@
"ui.hideOrigin": "Masquer l'origine",
"ui.hidePlanes": "Masquer les plans",
"ui.hideProfile": "Masquer le profil",
"ui.history_title": "Historique",
"ui.hole": "Perçage",
"ui.hole_tap_type": "Type de taraudage",
"ui.hole_type": "Type de perçage",
@@ -1315,11 +1548,12 @@
"ui.length_decimals": "Décimales des longueurs",
"ui.light": "Clair",
"ui.line": "Ligne",
"ui.linetype": "Type de ligne",
"ui.lock": "Verrouiller",
"ui.loft": "Lissage",
"ui.look_at": "Vue normale",
"ui.look_at_anim_desc": "Vue d'axe, Vue normale, Accueil et Cadrer",
"ui.manipulator_free_alt": "Glissement libre du manipulateur aussi avec Alt/Option",
"ui.manipulator_free_alt_tip": "Activé : Alt/Option ou Ctrl/Cmd sur les flèches d’extrusion / du manipulateur désactive l’accrochage aux valeurs. Désactivé : Ctrl/Cmd uniquement.",
"ui.manufacture": "Fabrication",
"ui.material_review.axes": "Axes locaux au matériau ; aucune orientation du corps attribuée.",
"ui.material_review.claims": "Les sources et licences sont des déclarations non vérifiées. Les valeurs absentes restent inconnues.",
@@ -1347,10 +1581,8 @@
"ui.mesh_format_3mf": "3MF",
"ui.mesh_format_3mf_tagged": "3MF avec étiquettes de faces",
"ui.mesh_format_stl": "STL",
"ui.midpoint": "Point milieu",
"ui.mirror": "Symétrie",
"ui.mirror_line": "Ligne de symétrie",
"ui.modify": "Modifier",
"ui.move": "Déplacer",
"ui.multiple": "Multiple",
"ui.named_views": "Vues nommées",
@@ -1371,7 +1603,6 @@
"ui.offset_plane": "Plan décalé",
"ui.one_side": "Un côté",
"ui.open": "Ouvrir",
"ui.open_ellipsis": "Ouvrir...",
"ui.open_ends_tip": "Ne touche rien",
"ui.open_preferences": "Ouvrir les préférences",
"ui.open_recent": "Ouvrir un fichier récent",
@@ -1381,12 +1612,14 @@
"ui.orbit_around": "Orbite autour de",
"ui.orientation": "Orientation",
"ui.origin": "Origine",
"ui.palette_points": "Points",
"ui.palette_profile": "Profil",
"ui.palette_construction_mode": "Mode construction (X)",
"ui.palette_more": "Plus",
"ui.palette_sketch_options": "Options d’esquisse",
"ui.pan": "Panoramique",
"ui.pan_zoom_orbit": "Panoramique, zoom, orbite",
"ui.parallel": "Parallélisme",
"ui.parameters": "Paramètres",
"ui.part_size_shared": "Reconstruit la pièce en remplaçant les entités ajoutées. Toutes les occurrences de ce composant sont mises à jour.",
"ui.paste": "Coller",
"ui.path": "Trajectoire",
"ui.pattern_on_path": "Réseau sur trajectoire",
@@ -1398,6 +1631,12 @@
"ui.placement": "Placement",
"ui.plane": "Plan",
"ui.plane_at_angle": "Plan incliné",
"ui.plugins": "Modules externes",
"ui.plugins_desktop": "Les modules externes sont disponibles dans l’application de bureau.",
"ui.plugins_empty": "Aucun module externe installé.",
"ui.plugins_note": "Chaque module est un dossier dans {path} contenant un fichier plugin.json. SolidCraft lit ce fichier et n’exécute aucun code du dossier. Désactiver un module le laisse installé.",
"ui.plugins_skipped": "{count} dossier(s) illisible(s).",
"ui.plugins_version": "{version}",
"ui.point": "Point",
"ui.point_under_the_cursor": "Point sous le curseur",
"ui.points": "Points",
@@ -1416,13 +1655,17 @@
"ui.quit": "Quitter",
"ui.radial_context_menu": "Menu contextuel radial",
"ui.radius": "Rayon",
"ui.radius_type": "Type de rayon",
"ui.rail": "Rail",
"ui.rail_fit": "Ajustement au rail",
"ui.reachable": "Accessible",
"ui.rectangle": "Rectangle",
"ui.rectangular_pattern": "Réseau rectangulaire",
"ui.redo": "Rétablir",
"ui.relink_design_changed": "L’assemblage a changé pendant le choix d’une source. Relancez Relier à nouveau la source.",
"ui.relink_done": "Source reliée à nouveau pour toutes les instances.",
"ui.relink_not_linked": "Ce composant n’est lié à aucune conception source.",
"ui.relink_select_one": "Sélectionnez d’abord un composant lié ou l’une de ses instances.",
"ui.relink_source_all": "Relier à nouveau la source (toutes les instances)…",
"ui.removeFromToolbar": "Retirer de la barre d'outils",
"ui.removeSection": "Retirer la coupe",
"ui.render": "Rendu",
@@ -1438,10 +1681,12 @@
"ui.reverse_horizontal_pan_tip": "Le défilement horizontal à deux doigts et la molette latérale font le panoramique dans l'autre sens",
"ui.reverse_zoom_direction": "Inverser le sens du zoom",
"ui.revolve": "Révolution",
"ui.ribbon_exit": "Quitter",
"ui.ribbon_pin_footer": "Les commandes épinglées s’affichent dans le ruban. Survolez une ligne pour l’épingler ou la détacher.",
"ui.ribbon_workspace": "Espace de travail",
"ui.right": "Droite",
"ui.save": "Enregistrer",
"ui.save_as": "Enregistrer sous",
"ui.save_as_ellipsis": "Enregistrer sous...",
"ui.save_mesh_no_slicer": "Aucun slicer trouvé : choisissez Personnalisé… et sélectionnez votre slicer, ou désactivez Envoyer au slicer",
"ui.save_mesh_pick_bodies": "Sélectionner les corps à enregistrer",
"ui.save_mesh_temp_failed": "Impossible de créer {folder} : {error}",
@@ -1454,8 +1699,10 @@
"ui.select": "Sélectionner",
"ui.selectOther": "Sélectionner un autre…",
"ui.selectTangentChain": "Sélectionner la chaîne tangente",
"ui.select_other_hint": "Sélectionner un autre élément : maintenir le bouton gauche immobile sur le modèle (ou clic droit › Sélectionner un autre…) pour lister tout ce qui se trouve sous le curseur ; cliquer de nouveau au même endroit pour sélectionner l'élément suivant.",
"ui.selection": "Sélection",
"ui.selection_modifiers": "Modificateurs de sélection",
"ui.selection_shift_add_only": "Maj+clic ajoute sans retirer",
"ui.selection_shift_add_only_tip": "Activé : Maj ajoute des éléments, Ctrl/Cmd bascule. Désactivé (par défaut) : Maj et Ctrl/Cmd basculent tous deux.",
"ui.send": "Envoyer",
"ui.send_to_slicer": "Envoyer au slicer",
"ui.sent_to_slicer": "Envoyé à {slicer}",
@@ -1471,7 +1718,6 @@
"ui.show_constraints": "Contraintes",
"ui.show_construction_geometries": "Géométries de construction",
"ui.show_dimensions": "Cotes",
"ui.show_points": "Afficher les points",
"ui.show_profile": "Afficher le profil",
"ui.show_projected_geometries": "Géométries projetées",
"ui.simple": "Simple",
@@ -1480,8 +1726,8 @@
"ui.sketch": "Esquisse",
"ui.sketch_dimension": "Cote d'esquisse",
"ui.sketch_grid": "Grille d'esquisse",
"ui.sketch_modifiers": "Modificateurs d’esquisse",
"ui.sketch_scale": "Échelle d'esquisse",
"ui.sketch_status": "État de l'esquisse",
"ui.sketches": "Esquisses",
"ui.slice": "Trancher",
"ui.slicer": "Slicer",
@@ -1527,12 +1773,14 @@
"ui.stand_up_tip": "Après l'ouverture d'un fichier STEP ou IGES avec Z vers le haut, proposer un quart de tour autour de X",
"ui.start": "Début",
"ui.start_over": "Recommencer",
"ui.start_radius": "Rayon initial",
"ui.start_page": "Page d’accueil",
"ui.stretch": "Étirer",
"ui.supersampling": "Suréchantillonnage (SSAA)",
"ui.supersampling_adaptive": "Seulement quand la vue est immobile",
"ui.supersampling_off": "Désactivé (1×)",
"ui.suppress": "Désactiver",
"ui.suppress_auto_snap": "Supprimer l’accrochage auto pendant le dessin / le déplacement",
"ui.suppress_auto_snap_note": "Par défaut : Ctrl/Cmd ou Alt/Option suppriment chacun l’accrochage d’esquisse. Choisissez une seule touche pour restreindre.",
"ui.sweep": "Balayage",
"ui.sweep_follow_direction": "Suivre une direction",
"ui.sweep_keep_as_drawn": "Garder tel que dessiné",
@@ -1575,13 +1823,24 @@
"ui.untitled": "Sans titre",
"ui.up_axis_note": "Axe orienté vers le haut dans les vues (vue d'axe, Accueil, Vue normale). Les conceptions ne sont pas modifiées.",
"ui.updateToCurrentView": "Mettre à jour avec la vue actuelle",
"ui.variable": "Variable",
"ui.view_centre": "Centre de la vue",
"ui.visual_shaded_hidden_edges": "Ombré avec arêtes cachées",
"ui.visual_shaded_with_edges": "Ombré avec arêtes",
"ui.visual_style": "Style visuel",
"ui.visual_wireframe": "Filaire",
"ui.workspace": "espace de travail",
"ui.workspace_animation_description": "Vues éclatées, études de mouvement et storyboards",
"ui.workspace_available": "Disponible",
"ui.workspace_current": "Actuel",
"ui.workspace_design_description": "Esquisser, modéliser, assembler et inspecter",
"ui.workspace_design_tabs": "Modèle · Surface · Assemblage · Insérer · Inspecter · Gérer · Vue",
"ui.workspace_drawing_description": "Mises en plan 2D avec vues, cotes et cartouches",
"ui.workspace_footer": "Chaque espace de travail s’applique à la même conception, au même historique et à la même sélection ; seuls les onglets du ruban changent. Le sélecteur est verrouillé pendant la modification d’une esquisse ou d’une entité.",
"ui.workspace_manufacture_description": "Tranchage, parcours d’outil et configuration",
"ui.workspace_planned": "Prévu",
"ui.workspace_render_description": "Aperçu du produit, paramètres de scène et images rendues",
"ui.workspace_render_tabs": "Scène · Image",
"ui.workspace_simulation_description": "Charges, contraintes et résultats",
"ui.x_distance": "Distance X",
"ui.y_distance": "Distance Y",
"ui.y_up": "Y vers le haut",
@@ -1591,7 +1850,6 @@
"ui.zoom_to_the_cursor": "Zoom vers le curseur",
"update.available": "SolidCraft {version} est disponible",
"update.button": "Mettre à jour",
"update.button_tip": "SolidCraft {version} est disponible",
"update.button_version": "Mettre à jour vers {version}",
"update.check_automatically": "Rechercher automatiquement les mises à jour",
"update.check_now": "Vérifier maintenant",
@@ -1623,5 +1881,6 @@
"update.title": "Mise à jour disponible",
"update.up_to_date": "SolidCraft est à jour.",
"update.update_now": "Mettre à jour maintenant",
"update.you_have": "Vous avez la version {version}."
"update.you_have": "Vous avez la version {version}.",
"↑ ↓ choose · Enter / Tab complete · Esc dismiss": "↑ ↓ choisir · Entrée / Tab compléter · Échap fermer"
}
+399 -39
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+342 -39
View File
File diff suppressed because it is too large Load Diff
+259 -41
View File
@@ -3,11 +3,14 @@
"1. Open GitHub draft below, then paste the report copied to your clipboard.": "1. 下のボタンでGitHubの下書きを開き、クリップボードにコピーされたレポートを貼り付けます。",
"1. Open GitHub draft below. Your title and report will be filled in.": "1. 下のボタンでGitHubの下書きを開きます。タイトルとレポートは入力済みです。",
"2. Attach your screenshot: copy and paste it, or save and upload the PNG.": "2. スクリーンショットを添付します。コピーして貼り付けるか、PNGを保存してアップロードします。",
"2D": "2D",
"3. Click Submit new issue on GitHub to send your report.": "3. GitHubでSubmit new issueをクリックしてレポートを送信します。",
"A new configuration, a copy of the active one": "アクティブな構成をコピーして新しい構成を作成",
"AI": "AI",
"ARRANGE": "配置",
"About SolidCraft": "SolidCraft について",
"Acceleration": "加速度",
"Access": "アクセス性",
"Actions": "操作",
"Active": "アクティブ",
"Active rule": "有効なルール",
"Add": "追加",
@@ -15,6 +18,7 @@
"Add Mid Radius": "中間半径を追加",
"Add Row": "行を追加",
"Add condition": "条件を追加",
"Add or edit rule": "ルールを追加または編集",
"Against": "相手",
"Alignment": "配置",
"All Sketch Points": "すべてのスケッチ点",
@@ -28,8 +32,11 @@
"Applied Radius": "適用半径",
"Apply To": "適用先",
"Apply to selection": "選択項目に適用",
"Apply your appearance first. Exports assigned appearances and materials created in this design.": "先に外観を適用してください。割り当てた外観と、このデザインで作成したマテリアルを書き出します。",
"Arc length": "円弧の長さ",
"Area": "表面積",
"As-Built": "現状位置",
"Assign Rule": "ルールを割当",
"Attachment": "アタッチメント",
"BLEND SURFACE": "ブレンドサーフェス",
"BOUNDARY SURFACE": "境界サーフェス",
@@ -37,12 +44,21 @@
"Bend Line Position": "曲げ線位置",
"Bend Position": "曲げ位置",
"Bend Radius": "曲げ半径",
"Bend Table": "曲げテーブル",
"Bends": "曲げ",
"Bodies": "ボディ",
"Body": "ボディ",
"Boundary": "境界",
"Bounding box": "境界ボックス",
"CHECK": "チェック",
"COMPONENTS": "コンポーネント",
"CONFIGURATIONS": "構成",
"CONSTRAIN": "拘束",
"CONTACT": "接触",
"CONVERT": "変換",
"CSV or JSON: updates existing parameters, adds new ones": "CSVまたはJSON:既存のパラメータを更新し、新しいパラメータを追加します",
"Capture All Positions": "すべての位置をキャプチャ",
"Capture Pos.": "位置を記録",
"Catch Direction": "キャッチ方向",
"Cells": "セル",
"Center X": "中心 X",
@@ -53,15 +69,19 @@
"Check collisions": "衝突チェック",
"Checked": "チェック済み",
"Choose a new plane. Sketch geometry and dimensions stay unchanged in its two-dimensional coordinates.": "新しい平面を選択してください。スケッチジオメトリと寸法は2次元座標系のまま維持されます。",
"Choose…": "選択…",
"Circular": "円形",
"Circumference": "円周",
"Close draft": "下書きを閉じる",
"Collisions": "衝突",
"Colour": "色",
"Comment": "コメント",
"Component1": "コンポーネント1",
"Config Table": "構成テーブル",
"Configuration": "構成",
"Configuration rules": "構成ルール",
"Construction": "コンストラクション",
"Contact": "接触",
"Contact Sets": "接触セット",
"Contact sets": "接触セット",
"Continuity": "連続性",
"Copy Error Message": "エラーメッセージをコピー",
@@ -79,15 +99,17 @@
"Cube": "キューブ",
"Current orthographic view (1:1 mm)": "現在の平行投影(実寸・mm)",
"Current perspective view": "現在の透視投影",
"Curv. Comb": "曲率コーム",
"Curvature (G2)": "曲率 (G2)",
"Curvature change (G3)": "曲率変化 (G3)",
"Customize Toolbar": "ツールバーのカスタマイズ",
"Delete": "削除",
"Delete all": "すべて削除",
"Delete rule": "ルールを削除",
"Delete the row": "行を削除",
"Delete the set": "セットを削除",
"Delete this parameter": "このパラメータを削除",
"Density": "密度",
"Dependents": "依存先",
"Designs you open or save appear here.": "開いたデザインや保存したデザインがここに表示されます。",
"Determined by To Object. Choose Angle to edit the angular extent.": "「対象オブジェクトまで」により決定されます。角度範囲を編集するには「角度」を選択してください。",
"Direction": "方向",
@@ -99,6 +121,7 @@
"Drive Joint": "ジョイントを駆動",
"Duration": "時間",
"Edges": "エッジ",
"Edit": "編集",
"Edit Profile Sketch": "プロファイルスケッチを編集",
"Enable rule": "ルールを有効化",
"Enabled": "有効",
@@ -107,43 +130,63 @@
"Explode": "分解",
"Explode Text": "テキストを分解",
"Export Animation…": "アニメーションをエクスポート…",
"Export CSV…": "CSVを書き出し…",
"Export Current View as DXF": "現在の表示をDXFでエクスポート",
"Export Current View as DXF…": "現在の表示をDXFでエクスポート…",
"Export DXF": "DXF出力",
"Export DXF…": "DXFをエクスポート…",
"Export JSON…": "JSONを書き出し…",
"Export hidden edges as dashed lines": "点線で出力",
"Expression": "式",
"Extend to meet": "交わるまで延長",
"Extents": "範囲",
"FILES": "ファイル",
"FLAT": "展開",
"Face": "面",
"Face attachment lost": "面アタッチメントが失われました",
"Faces": "面",
"Faces / edges / vertices": "面 / エッジ / 頂点",
"Fastener": "締結部品",
"Favorites": "お気に入り",
"Favourite": "お気に入り",
"Feature": "フィーチャー",
"Files from many CAD programs have Y up, so they open lying on their back here (Z up).": "多くのCADではY軸を上向きとしているため、Z軸を上向きとするSolidCraftでは、読み込んだモデルが横倒しの向きで表示されることがあります。",
"Filter": "フィルター",
"Find in Browser": "ブラウザで検索",
"Flat": "展開",
"Finish": "終了",
"Flat Pattern": "フラットパターン",
"Flat size": "展開サイズ",
"Follow camera (Look At sketches)": "カメラ追従(スケッチを正面視)",
"Follow the camera (Look At sketches)": "カメラ追従(スケッチを正面視)",
"Font": "フォント",
"From Bodies": "ボディから",
"From edges": "開始側エッジ",
"Full Length": "全長",
"Gap": "間隔",
"Grid": "グリッド",
"HISTORY": "履歴",
"Height": "高さ",
"Hidden edges": "隠線",
"Holes": "穴",
"Holes (edges on them)": "穴(そのエッジ)",
"Home view": "ホームビュー",
"Horiz./Vert.": "水平 / 垂直",
"IMAGES": "画像",
"Import…": "読み込み…",
"In This Design": "このデザイン内",
"In place (surfaces)": "その場で(サーフェス)",
"In plane": "平面内",
"Inside": "内側",
"Interferes": "干渉あり",
"Isocurve": "等パラメータ線",
"Issue title": "Issueタイトル",
"JOINTS": "ジョイント",
"Joint Origin1": "ジョイント原点1",
"Keep (points on it)": "残す部分(その上の点)",
"Keep Tools": "ツールボディを保持",
"Keep of the other surface": "相手のサーフェスで残す部分",
"LIBRARY": "ライブラリ",
"LINKS": "リンク",
"Later": "後で",
"Leave it": "そのままにする",
"Legacy face attachment. Rebinding to a certified face creates an exact attachment.": "従来の面アタッチメントです。確定された面に再バインドすると正確なアタッチメントが作成されます。",
@@ -151,47 +194,70 @@
"Length units": "長さの単位",
"Letter Spacing": "文字間隔",
"Library": "ライブラリ",
"Library file": "ライブラリファイル",
"Line Spacing": "行間",
"Local library": "ローカルライブラリ",
"Loop": "ループ",
"MANAGE": "管理",
"MATERIALS": "材料",
"MODEL": "モデル",
"MOTION": "モーション",
"Margin": "マージン",
"Mass": "質量",
"Mass Center": "重心",
"Matched restrictions check the final values.": "条件に一致する制限ルールが最終値を確認します。",
"Material": "材料",
"Max": "最大",
"Mid Radius": "中間半径",
"Middle double-click": "中ボタンのダブルクリック",
"Min": "最小",
"Mitre": "留め継ぎ",
"Model Parameters": "モデルパラメータ",
"Modeled": "モデル化済み",
"More toolbar panels": "その他のツールバーパネル",
"Motion": "モーション",
"Move / Copy": "移動 / コピー",
"Move Type": "移動タイプ",
"Move down": "下へ移動",
"Move earlier": "前へ移動",
"Move later": "後へ移動",
"Move up": "上へ移動",
"Name": "名前",
"Nearest": "最も近い",
"New": "新規",
"New Comp.": "新規コンポーネント",
"New Sketch": "新規スケッチ",
"New rule…": "新規ルール…",
"New study": "新規スタディ",
"No crash reports.": "クラッシュレポートはありません。",
"No matching appearance presets.": "一致する外観プリセットはありません。",
"No matching appearances": "一致する外観がありません",
"No visible bodies to export.": "出力する表示中のボディがありません。",
"None": "なし",
"Normal to sketch": "スケッチに垂直",
"Not available yet": "まだ利用できません",
"Notch": "ノッチ",
"OUTPUT": "出力",
"Opacity": "不透明度",
"Open GitHub draft": "GitHubの下書きを開く",
"Open local libraries in the desktop app. You can export this design's library here.": "ローカルライブラリはデスクトップアプリで開いてください。このデザインのライブラリはここで書き出せます。",
"Open-source parametric 3D CAD in Rust. Licensed MIT OR Apache-2.0.": "Rust製オープンソース・パラメトリック3D CAD。ライセンス: MIT または Apache-2.0。",
"Operator": "演算子",
"Other side": "反対側",
"Other surface (mutual trim)": "相手のサーフェス(相互トリム)",
"Outside": "外側",
"Overall length": "全長",
"Parameter values": "パラメータ値",
"Param. Graph": "パラメータ図",
"Parameter": "パラメータ",
"Parts": "部品",
"Pass through": "通過点",
"Path": "パス",
"Pattern": "パターン",
"Per Edge": "エッジごと",
"Pick the tool bodies": "ツールボディを選択",
"Picked": "選択したもの",
"Picked point": "選択した点",
"Place at the origin instead": "代わりに原点に配置",
"Plastic Rules": "樹脂ルール",
"Playback": "再生",
"Plot": "グラフ",
"Position": "位置",
@@ -202,10 +268,11 @@
"Preview Row": "行をプレビュー",
"Preview leaves the current design unchanged.": "プレビューは現在のデザインを変更しません。",
"Profiles/Curves": "プロファイル/曲線",
"Projection": "投影",
"Quantity": "量",
"RECENT": "最近使用した項目",
"REFERENCE": "参照",
"RULED SURFACE": "ルールドサーフェス",
"RULES": "ルール",
"Radius Fit": "半径フィット",
"Ratio": "比率",
"Reattach selected face": "選択した面を再アタッチ",
@@ -223,6 +290,8 @@
"Remove this point": "この点を削除",
"Report a bug — review a screenshot and open a GitHub draft": "バグを報告 — スクリーンショットを確認してGitHub下書きを作成",
"Reset Toolbar": "ツールバーをリセット",
"Resize Part": "部品サイズ変更",
"Restore View": "ビューを復元",
"Restore outer boundary": "外側の境界を復元",
"Restore the inherited component or physical material appearance": "コンポーネントまたは物理マテリアルから継承した外観に戻す",
"Result": "結果",
@@ -231,18 +300,27 @@
"Reverse Direction": "方向を反転",
"Revert": "元に戻す",
"Revert All Positions": "すべての位置を元に戻す",
"Revert Pos.": "位置を復元",
"Revert Position": "位置を元に戻す",
"Review the report and screenshot before opening GitHub.": "GitHubを開く前にレポートとスクリーンショットを確認してください。",
"Rim Edge": "リムエッジ",
"Roll History Marker Here": "履歴マーカーをここに移動",
"Round": "丸め",
"Rule": "ルール",
"Rule name": "ルール名",
"Rules run from top to bottom. Matched drives change effective values.": "ルールは上から順に実行されます。条件に一致する駆動ルールが有効値を変更します。",
"SECTION": "断面",
"SURFACE QUALITY": "サーフェス品質",
"Save Rule": "ルールを保存",
"Save View": "ビューを保存",
"Save the study as an animated GIF, seen as the viewport shows it": "ビューポートの表示どおりにスタディをアニメーション GIF として保存",
"Saved preview. Geometry is being checked before editing.": "保存済みのプレビューです。編集前にジオメトリを検証しています。",
"Scale factor": "尺度係数",
"Scene": "シーン",
"Search": "検索",
"Search commands": "コマンドを検索",
"Search this library": "このライブラリを検索",
"Section": "断面",
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "その他を選択: モデル上で左ボタンを長押し(または右クリック › その他を選択…)するとカーソル下の要素が一覧表示されます。同じ場所を再度クリックすると次の要素が選択されます。中ボタンをダブルクリックするとホームビューに戻ります(環境設定 › ナビゲーション)。",
"Select bodies (a face picks its body)": "ボディを選択(面をクリックするとそのボディが選択されます)",
"Select bodies or faces instead": "代わりにボディまたは面を選択",
@@ -250,6 +328,7 @@
"Semantic faces share their appearance across instances.": "セマンティック面はインスタンス間で外観を共有します。",
"Send your report on GitHub": "GitHubでレポートを送信",
"Shape": "形状",
"Sheet Rules": "板金ルール",
"Show Agent Cursor": "エージェントカーソルを表示",
"Show in folder": "フォルダに表示",
"Show the pointer an AI agent moves (#35)": "エージェントが操作するポインターを表示 (#35)",
@@ -263,6 +342,7 @@
"Sketch points": "スケッチ点",
"Slot width": "長穴の幅",
"SolidCraft closed before these designs were saved. Their last autosave can be opened.": "これらのデザインが保存される前にSolidCraftが終了しました。最後の自動保存ファイルを開くことができます。",
"Specialise": "特殊操作",
"Speed": "速度",
"Stand it up": "正立させる",
"Stand it up: a quarter turn about X, as one Move feature you can undo.": "正立させる: 取り消し可能な1つの移動フィーチャーとして、X軸周りに90度回転します。",
@@ -301,17 +381,19 @@
"Turn off section analysis before exporting the view.": "断面解析をオフにしてからエクスポートしてください。",
"Type": "タイプ",
"UNTRIM": "トリム解除",
"Unit": "単位",
"Updating the preview": "プレビューを更新中",
"Use active author placement": "アクティブな作成元配置を使用",
"Use fixed plane": "固定平面を使用",
"Use saved fixed plane": "保存された固定平面を使用",
"Use selected face as fixed plane": "選択した面を固定平面として使用",
"User Parameters": "ユーザーパラメータ",
"VIEW": "表示",
"VIEWS": "ビュー",
"Value": "値",
"Velocity": "速度",
"Vertices": "頂点",
"View navigator": "ビューナビゲーター",
"Visible bodies only. Curves use line segments (0.01 mm tolerance).": "表示中のボディを出力します。曲線は線分で近似します(許容誤差0.01 mm)。",
"Volume": "体積",
"Wall": "肉厚",
"Wall Location": "壁の位置",
@@ -319,6 +401,13 @@
"When": "条件 (When)",
"Width": "幅",
"Will fail": "失敗します",
"X angle": "X角度",
"X distance": "X距離",
"Y angle": "Y角度",
"Y distance": "Y距離",
"Z angle": "Z角度",
"Z distance": "Z距離",
"Zebra": "ゼブラ",
"angle, height": "角度, 高さ",
"appearance.dielectric_ior": "誘電体の屈折率",
"appearance.metallic": "金属度",
@@ -327,7 +416,13 @@
"appearance.roughness": "粗さ",
"appearance.surface_response": "表面の応答",
"browser.body_name": "ボディ{number}",
"browser.calibrate_title": "尺度を設定: {name}",
"browser.capture_many": "{count}個のコンポーネントが移動されましたが、位置がキャプチャされていません。",
"browser.capture_note": "「位置をキャプチャ」で新しい位置を保持し、「位置を元に戻す」で以前の位置に戻します。",
"browser.capture_one": "コンポーネントが移動されましたが、位置がキャプチャされていません。",
"browser.component_name": "コンポーネント{number}",
"browser.edit_canvas_title": "キャンバスを編集: {name}",
"browser.move_title": "移動: {name}",
"browser.parameters": "パラメータ ({count})",
"browser.precision": "精度: {precision}",
"browser.sketch_name": "スケッチ{number}",
@@ -665,7 +760,15 @@
"cmd.view_navigate": "ビューナビゲーション",
"cmd.view_rename": "名前付きビューの名前を変更",
"cmd.view_save": "新規名前付きビュー",
"command name": "コマンド名",
"color.copy": "クリックして色の値をコピー",
"color.edit": "クリックして色を編集",
"color.gamma": "0〜255のガンマ空間で色の値を表示",
"color.hue": "色相",
"color.linear": "0〜1の線形空間で色の値を表示",
"color.saturation_value": "彩度と明度",
"color.selected": "選択した色",
"contact.components_count": "{count}個のコンポーネント",
"contact.enabled_label": "有効",
"coupling.gear_1": "ギア 1",
"coupling.gear_1_pick": "ギア 1 (オプション)",
"coupling.gear_2": "ギア 2",
@@ -682,6 +785,21 @@
"coupling.rack": "ラック",
"coupling.radius_1": "半径 1",
"coupling.radius_2": "半径 2",
"crash.announce": "前回、SolidCraftが予期せず終了したか、内部エラーが発生しました。クラッシュレポートが保存されています(ヘルプ > クラッシュレポート…)。",
"crash.copy_folder": "フォルダのパスをコピー",
"crash.copy_path": "パスをコピー",
"crash.deleted": "{count}件のレポートを削除しました。",
"crash.detected_time": "検出日時: {time} UTC",
"crash.intro": "クラッシュレポートはこのコンピュータ内に保存され、外部に送信されることはありません。「報告…」で下書きが開き、共有前に内容を確認できます。",
"crash.kind_command": "コマンド内の内部エラー",
"crash.kind_internal": "内部エラー",
"crash.kind_kernel": "カーネルエラー(復旧済み)",
"crash.kind_unclean": "予期しない終了",
"crash.previous_session": "以前の SolidCraft セッションが予期せず終了したか、内部エラーが発生しました。正常終了が記録されていないセッションは起動時に検出されます。詳細は ヘルプ > クラッシュレポート… を参照してください。",
"crash.replayable": "再現可能",
"crash.report": "報告…",
"crash.unclean_explanation": "次回以降の起動時に検出されました。正確な終了時刻は記録されていません。システムのシャットダウンや強制停止によっても、正常終了が記録されない場合があります。",
"delete.and_more": "…ほか{count}件",
"dialog.bodies_count": "{count} ボディ",
"dialog.clearance_invalid": "クリアランスは 0 以上の長さにしてください",
"dialog.compute": "計算",
@@ -689,6 +807,8 @@
"dialog.computing": "計算中… {seconds} 秒",
"dialog.could_not_be_checked": "チェックできません",
"dialog.custom_axis": "↖ カスタム軸",
"dialog.dimension_insert_hint": "別の駆動寸法をクリックして名前を挿入",
"dialog.dimension_name": "寸法: {name}",
"dialog.edit_title": "{title}を編集",
"dialog.select": "↖ 選択",
"dialog.selected_count": "↖ {count} 個選択中",
@@ -712,9 +832,30 @@
"dxf.progress.write": "DXFを書き込み中 (5/5)",
"dxf.tolerance": "曲線の許容誤差",
"dxf.tolerance_help": "表示中のボディを出力し、曲線は線分で近似します。許容誤差を小さくすると曲線が滑らかになりますが、処理時間とメモリ使用量が増えます。",
"expression, e.g. 25 mm": "計算式 (例: 25 mm)",
"file_load.built": "構築済み: 全{total}フィーチャー中{built}件。",
"file_load.converting": "ソリッドを変換中: {done}/{total}",
"file_load.inserting": "ジオメトリを挿入中",
"file_load.opening": "デザインを開いています",
"file_load.preparing_thumbnail": "サムネイルを準備中...",
"file_load.preparing_view": "ビューを準備中...",
"file_load.preview_note": "保存済みのプレビューです。編集前にジオメトリを確認しています。",
"file_load.reading": "ファイルを読み込み中...",
"file_load.reading_percent": "ファイルを読み込み中... {percent}%",
"file_load.rebuilding": "フィーチャーを再構築中: {done}/{total}",
"file_load.recovering": "デザインを復元中",
"file_load.stopping": "前の操作の停止を待っています...",
"file_load.waiting": "ファイル操作を待っています",
"fits the face, e.g. M8": "面に適合するサイズ (例: M8)",
"from the rule": "ルールから",
"help.apache": "Apacheライセンス2.0",
"help.attributions": "帰属表示 (ATTRIBUTION.md)",
"help.build": "バージョン {version} · コミット {commit} · ビルド日 {date}",
"help.font_details": "インターフェースにはegui組み込みのフォントを使用しています(Ubuntu Font LicenceおよびMIT/Bitstream VeraライセンスのUbuntu LightとHack、OFLおよびMITのNoto Emojiと絵文字アイコンフォント)。スケッチのテキストにはLiberation Sans(SIL OFL 1.1)を使用しています。The Crafting Appsのオプションフォントパックcraft-fonts(BIZ UDPGothic、Shippori Mincho、BIZ UDMincho、Noto Sans CJK SC、Noto Sans Arabic、SIL OFL 1.1)はSolidCraftのビルドに同梱されていません。",
"help.fonts": "フォント",
"help.licences": "ライセンス",
"help.mit": "MITライセンス",
"help.notice": "NOTICE",
"help.third_party": "サードパーティライセンス",
"home.documentation": "ドキュメント",
"home.in_this_browser": "このブラウザ内",
"home.missing": "見つかりません",
@@ -740,9 +881,30 @@
"install.menu": "アプリをインストール…",
"install.steps_intro": "Safari では、Web アプリを Safari 自身のメニューからインストールします:",
"install.title": "SolidCraft をインストール",
"issue.body_label": "レポート本文",
"issue.note_image_copied": "GitHubの下書きに画像を貼り付けてください。画像をコピーできない場合はPNGを保存してください。",
"issue.note_opened": "GitHubの下書きを確認し、スクリーンショットを添付してください。GitHubのSubmit new issueボタンで公開されます。",
"issue.note_opened_copied": "レポートをコピーしました。GitHubの下書きに貼り付け、スクリーンショットを添付してから、GitHubでSubmit new issueをクリックしてください。",
"issue.note_removed": "この下書きからスクリーンショットを削除しました。",
"issue.note_report_copied": "レポートをコピーしました。必要に応じてGitHubの下書きに貼り付けてください。",
"issue.note_save_failed": "スクリーンショットを保存できませんでした: {error}",
"issue.note_saved": "スクリーンショットを保存しました。GitHubの下書きにPNGを添付してください。",
"issue.save_screenshot": "スクリーンショットを保存…",
"joint.from_bodies_hint": "選択した各ボディを、位置はそのままで専用の新しいコンポーネントに移動します。",
"joint.one_body": "両方の選択が {body} 上にあります。2 つ目は別の部品で選択してください。",
"joint.one_component": "これらの部品は 1 つのコンポーネントのボディです: {bodies}。ジョイントはコンポーネント同士を動かすため、部品ごとに専用のコンポーネントが必要です。",
"keymap.command_id": "コマンドID",
"keymap.empty": "一致するコマンドはありません",
"keymap.use_anyway": "ここでは{key}をそのまま使用",
"manage.appearance_hint": "選択対象の外観を選ぶか、スウォッチをボディまたは面にドラッグしてください。",
"manage.assign_hint": "ボディを選択し、プラスチック設計ルールを指定してください。",
"manage.config_hint": "構成の行、列、ルールを編集し、行を有効にするかプレビューしてください。",
"manage.empty": "一致する行はありません。",
"manage.material_hint": "ボディを選択し、物理マテリアルを指定してください。",
"manage.parameters_hint": "式と単位を編集してください。Enterを押すか、フィールドから移動すると変更が適用されます。",
"manage.rules_hint": "編集するルールを選ぶか、新規ルールを作成してください。確定するとルールが保存されます。",
"measure.at": "位置",
"measure.item": "項目 {number}",
"messages.ago_h": "{n} 時間前",
"messages.ago_min": "{n} 分前",
"messages.ago_s": "{n} 秒前",
@@ -754,8 +916,16 @@
"messages.title": "メッセージ",
"motion.checked_to": "ステップ {step} までしか確認していません: 確認の計算量の上限に達しました",
"motion.collision": "{a} · {b}: ステップ {steps}",
"motion.delete_key": "キーフレームを削除",
"motion.duration_hint": "秒 (空欄: ステップごと)",
"motion.key_here": "ここにキーフレームを追加",
"motion.key_here_hint": "このステップにジョイントの現在値でキーフレームを追加",
"motion.no_collisions": "スタディ全体で衝突はありません",
"motion.step_back": "前のステップ",
"motion.step_forward": "次のステップ",
"motion.to_end": "最後へ",
"motion.to_start": "最初へ",
"motion.track_name": "トラック {number}",
"nav.touchpad.help": "Shift を押しながらポインターを動かすとパン、Alt/Option を押しながら動かすと回転します。マウスボタンは不要です。縦スクロールでズームします。",
"nav.touchpad.label": "タッチパッド",
"nav.touchpad.orbit": "回転(Alt/Option を押しながら移動、マウスボタン不要)",
@@ -764,13 +934,13 @@
"nav.touchpad.zoom": "ズーム(縦スクロールまたはピンチ)",
"needs two joints": "2つのジョイントが必要",
"new configuration": "新規構成",
"new name": "新しい名前",
"new parameter": "新規パラメータ",
"no hole": "穴なし",
"no interference": "干渉なし",
"no joint that moves yet": "可動ジョイントがまだありません",
"no picture": "画像なし",
"none": "なし",
"parts.automatic_name": "自動",
"pattern.alignment_2": "配置 2",
"pattern.alignment_hint": "個数には元の図形が含まれます。中央配置ではコピーを両側に配置し、反転で偶数個の場合に多く配置する側を切り替えます。",
"pattern.direction_1": "方向 1",
@@ -778,12 +948,19 @@
"pattern.flip_direction": "方向を反転",
"pick a hole to seat it, or OK places it at the origin": "配置先の穴を選択するか、[OK]を押すと原点に配置します。",
"pick one or two vertices, edges or faces": "1つまたは2つの頂点、エッジ、面を選択",
"prefs.cpu": "CPU(ソフトウェア)",
"prefs.minutes_suffix": " min",
"properties.title": "プロパティ: {name}",
"recovery.days_ago": "{count}日前",
"recovery.detail": "{file} · {features}フィーチャー · 自動保存: {when}",
"recovery.hours_ago": "{count}時間前",
"recovery.just_now": "たった今",
"recovery.min_ago": "{count}分前",
"recovery.never_saved": "未保存",
"render.background": "シーンの背景",
"render.fit": "製品を画面に合わせる",
"render.image_cancel": "レンダリングをキャンセル",
"render.image_cancelled": "レンダリングをキャンセルしました",
"render.image_cancelling": "キャンセル中",
"render.image_check": "レンダリング要求を確認",
"render.image_completed": "画像をエクスポートできます",
"render.image_encoding": "PNGをエンコード中",
"render.image_export": "PNGをエクスポート",
@@ -830,6 +1007,19 @@
"sample.plate.name": "サンプル プレート",
"sample.vents.about": "矩形パターンに並んだスロット: 行数、列数、ピッチはパラメータ",
"sample.vents.name": "通気プレート",
"search.command_keys": "Enter 実行 Shift+Enter 実行して開いたままにする Esc 閉じる",
"search.commands": "コマンド",
"search.design": "このデザイン内",
"search.feature": "フィーチャー",
"search.invalid": "無効",
"search.keys": "↑ ↓ 選択 Enter 実行 Tab 名前を補完 Esc 閉じる",
"search.no_match": "「{query}」に一致するコマンドはありません",
"search.parameter": "パラメーター",
"search.recent": "最近使った項目",
"search.run": "コマンドを実行",
"search.suggest": "{label} ですか?",
"search.try": "コマンド名、このデザインのフィーチャー、またはパラメーターを入力します。値を指定して実行するには、IDとJSONオブジェクトを入力します:{example}",
"search.valid": "有効",
"settings.display_quality_note": "細かいほど曲面とエッジが滑らかに表示されますが、メッシュの生成に時間がかかります。変わるのはビューポートだけで、設計やエクスポートは変わりません。",
"settings.frame_rate_note": "ビューの右下に 1 秒あたりのフレーム数を表示します。オンの間、ビューは常に再描画されます。",
"settings.measure_axis_distances": "計測のX・Y・Z距離を表示",
@@ -840,17 +1030,30 @@
"settings.reset_intro": "次の設定はこのコンピューターでは使用できず、SolidCraft が起動できなくなるため変更されました:",
"settings.reset_title": "設定が変更されました",
"settings.supersampling_note": "ビューを高い解像度で描画してから縮小します。4K 未満の画面でエッジ、線、グリッドが滑らかになります。GPU の負荷が増えます(1.5× で約 2.25 倍、2× で 4 倍のピクセル)。",
"shortcut.json_error": "パラメータはJSON形式で指定してください: {error}",
"shortcut.no_command": "コマンド`{id}`がありません。JSONの前のテキストはコマンドIDである必要があります",
"shortcut.pin": "Sボックスの先頭にピン留め",
"shortcut.unpin": "ピン留めを解除",
"sketch.degrees_of_freedom": "自由度 {count}",
"sketch.dof_left": "残り自由度 {count}",
"sketch.dof_left_one": "残り自由度 {count}",
"sketch.draw_curves": "{linetype}として描く",
"sketch.make_selection": "選択を{linetype}にする",
"sketch.open_ends": "開放 {count}",
"sketch.untied_ends": "未拘束 {count}",
"sketch_panel.attached": "接続先: {body} / {path}",
"sketch_panel.author": "作成時の配置: {path}",
"sketch_panel.dimension_hint": "値または式を入力し、OK を押してください。",
"sketch_panel.move_hint": "距離と角度を入力し、OK を押してください。",
"sketch_panel.redefine_title": "{command}:{name}",
"sketch_panel.sheet_attachment": "板金ボディの平面に接続されています。板金が変更されると、そのパネルに追従します。",
"sketch_panel.text_hint": "テキストと配置を編集し、OK を押してください。",
"sketch_panel.use_plane": "{plane} 平面を使用",
"sketch_text.align_center": "中央",
"sketch_text.align_left": "左",
"sketch_text.align_right": "右",
"sketch_text.bold": "太字",
"sketch_text.builtin_font": "{family}(内蔵)",
"sketch_text.font_search": "フォントを検索",
"sketch_text.italic": "斜体",
"sketch_text.loading_fonts": "インストール済みフォントを検索中…",
"sketch_text.on_path": "文字はパスに沿います: 位置は曲線で決まります",
@@ -871,6 +1074,7 @@
"timeline.ungroup": "フィーチャーフォルダを削除",
"to the face": "面まで",
"to the next face": "次の面まで",
"toolbar.customize_note": "{tab}タブ。コマンドにチェックを入れると、ツールバーにボタンが追加されます。パネルのドロップダウンの行をツールバーにドラッグしたり、ボタンをツールバーの外にドラッグして削除したり、ドロップダウンの行のピンを使うこともできます。",
"toolbar.image": "画像",
"toolbar.render": "レンダリング",
"toolbar.render_scene": "シーン",
@@ -879,11 +1083,11 @@
"trace.stopped_by_loop": "サンプル {sample} で終了: {joint} を通るループはこれ以上閉じられません",
"ui.1_degree_of_freedom": "自由度 1",
"ui.3d_mouse_speed": "3D マウス速度",
"ui.3d_mouse_windows": "3D マウス",
"ui.3d_sketch": "3D スケッチ",
"ui.about": "SolidCraft について",
"ui.activate": "アクティブ化",
"ui.active_component": "アクティブなコンポーネント",
"ui.add_configuration": "構成を追加",
"ui.add_parameter": "パラメーターを追加",
"ui.agent_chat": "エージェントチャット",
"ui.agent_chat_add": "ファイルを添付",
"ui.agent_chat_add_file": "ファイルを選択…",
@@ -925,6 +1129,7 @@
"ui.agent_chat_file_error": "{name}: {error}",
"ui.agent_chat_file_heic": "{name}: HEIC 画像はここでは読めません。先に JPEG か PNG に書き出してください",
"ui.agent_chat_file_importing": "デザインにインポート中…",
"ui.agent_chat_file_kinds": "{name}: 添付できるのは画像 (PNG、JPEG、GIF、WebP、BMP、TIFF)、テキストファイル (SVG も)、CAD ファイル (STEP、IGES、FreeCAD、Rhino、STL、3MF、OBJ、glTF) です",
"ui.agent_chat_file_loading": "ファイルを読み込み中…",
"ui.agent_chat_file_not_a_file": "{name}: ファイルではありません",
"ui.agent_chat_file_not_a_picture": "{name}: 画像ではありません",
@@ -947,7 +1152,7 @@
"ui.agent_chat_model_tip": "エージェントが使うモデル。選択はこのエージェントに対して記憶されます。",
"ui.agent_chat_new": "新しいチャット",
"ui.agent_chat_new_confirm": "新しいチャットを始めますか? 現在のチャットは消去されます。",
"ui.agent_chat_new_yes": "新しいチャット",
"ui.agent_chat_next_picture": "次の画像",
"ui.agent_chat_no_cli": "アプリの隣に solidcraft-cli が見つからないため、エージェントはデザインにアクセスできません。",
"ui.agent_chat_no_control": "エージェントチャットのエンドポイントが起動しなかったため、エージェントはデザインにアクセスできません。",
"ui.agent_chat_no_images": "このエージェントは画像を受け付けません",
@@ -963,6 +1168,7 @@
"ui.agent_chat_placeholder": "デザインの変更をエージェントに依頼…",
"ui.agent_chat_placeholder_busy": "エージェントの作業が終わったら送るメッセージを入力(Ctrl+Enter: 今すぐ送信)",
"ui.agent_chat_pop_out": "独立したウィンドウで開く(別の画面に移動できます)",
"ui.agent_chat_previous_picture": "前の画像",
"ui.agent_chat_privacy": "あなたのアカウントで実行するエージェント以外には、何も送信されません。",
"ui.agent_chat_privacy_note": "SolidCraft はどこにも何も送信しません。あなたが実行するエージェントが、あなた自身のアカウントでそのサービスと通信します。SolidCraft がキーやパスワードを見たり保存したりすることはありません。",
"ui.agent_chat_queue": "待機に追加",
@@ -1018,6 +1224,7 @@
"ui.ai_bad_url": "http:// または https:// で始まる URL を入力してください",
"ui.ai_base_url": "ベース URL",
"ui.ai_button": "AI",
"ui.ai_command_hint": "my-agent --acp",
"ui.ai_copy_suffix": "コピー",
"ui.ai_custom_url": "カスタム URL",
"ui.ai_default": "既定",
@@ -1038,8 +1245,10 @@
"ui.ai_mode_edits_note": "モデリングコマンドは確認なしで実行されます。保存・開く・エクスポートなどのファイル操作や破壊的な操作は引き続き確認します。",
"ui.ai_mode_global": "上の設定に従う",
"ui.ai_model": "モデル",
"ui.ai_model_hint": "qwen3:14b",
"ui.ai_modes_scope": "これらの設定は SolidCraft のツールに適用されます。エージェント自身のツール(シェル、ファイル編集)は、エージェントにモードがある場合にチャットパネルのヘッダーで選ぶエージェント自身のモードに従います。",
"ui.ai_name": "名前",
"ui.ai_name_hint": "マイエージェント",
"ui.ai_need_command": "エージェントを起動するコマンドを入力してください",
"ui.ai_need_model": "モデル名を入力してください",
"ui.ai_no_answer": "エージェントが時間内に応答しませんでした",
@@ -1067,9 +1276,10 @@
"ui.ai_tool_running": "実行中",
"ui.ai_tool_unfold": "クリックして結果を表示",
"ui.ai_tool_waiting": "待機中",
"ui.ai_toolbar_button": "ツールバーにボタンを表示",
"ui.ai_toolbar_button_note": "ツールバー右端の AI ボタン。Ctrl+Shift+A と表示メニューからも引き続きパネルを開けます。",
"ui.ai_toolbar_button": "アプリケーションバーにアシスタントボタンを表示",
"ui.ai_toolbar_button_note": "アプリケーションバーのアシスタントボタン。Ctrl+Shift+A と表示メニューからもパネルを開けます。",
"ui.ai_undo_tip": "このメッセージでエージェントが行った変更を、1回でまとめて元に戻します",
"ui.ai_url_hint": "https://example.com/v1",
"ui.all": "すべて",
"ui.ambient_occlusion": "アンビエントオクルージョン",
"ui.ambient_occlusion_help": "不透明な面が接する部分に柔らかい陰影を加えます。GPUビューポートが必要です。",
@@ -1083,7 +1293,8 @@
"ui.anti_aliasing": "アンチエイリアス",
"ui.apply": "適用",
"ui.arc": "円弧",
"ui.assemble": "アセンブリ",
"ui.assistant": "アシスタント",
"ui.assistant_waiting": "{count} 件の承認待ち",
"ui.autosave_every": "自動保存の間隔",
"ui.axes": "軸",
"ui.axis": "軸",
@@ -1094,6 +1305,11 @@
"ui.bodies_all_if_none": "ボディ(未選択ならすべて)",
"ui.box": "直方体",
"ui.break": "分割",
"ui.browser_bodies_one": "{count} ボディ",
"ui.browser_bodies_other": "{count} ボディ",
"ui.browser_collapse": "ブラウザを折りたたむ",
"ui.browser_expand": "ブラウザを展開",
"ui.browser_title": "ブラウザ",
"ui.calibrate": "校正",
"ui.cancel": "キャンセル",
"ui.canvases": "キャンバス",
@@ -1103,7 +1319,6 @@
"ui.chamfer": "面取り",
"ui.chamfer_type": "面取りタイプ",
"ui.change_workspace": "ワークスペースの変更",
"ui.chord_length": "弦長",
"ui.circle": "円",
"ui.circular_pattern": "円形パターン",
"ui.clear": "クリア",
@@ -1136,11 +1351,9 @@
"ui.command_done": "{command} 完了",
"ui.command_palette_hint": "コマンドを入力(例: {example})— または S キーで検索",
"ui.concentric": "同心",
"ui.configurations": "構成",
"ui.conic_curve": "円錐曲線",
"ui.constant": "一定",
"ui.constraints_conflict": "拘束が競合しています",
"ui.construct": "構築",
"ui.construction": "コンストラクション",
"ui.context_menu": "コンテキスト メニュー",
"ui.continuity_g0": "G0 折れ",
@@ -1166,7 +1379,6 @@
"ui.cylinder": "円柱",
"ui.dark": "ダーク",
"ui.default_modelling_orientation": "モデリングのデフォルト方向",
"ui.degrees_of_freedom": "自由度",
"ui.delete": "削除",
"ui.depth": "深さ",
"ui.design": "デザイン",
@@ -1208,7 +1420,6 @@
"ui.equivalent_radius": "等価半径",
"ui.expand": "展開",
"ui.exportDxf": "DXF をエクスポート…",
"ui.export_ellipsis": "エクスポート…",
"ui.export_ellipsis_keymap": "エクスポート…",
"ui.export_keymap_tip": "ショートカット、Sボックスのお気に入り、ツールバー設定をファイルに保存",
"ui.extend": "延長",
@@ -1232,6 +1443,7 @@
"ui.frame_rate": "フレームレート",
"ui.front": "正面",
"ui.fully_constrained": "完全拘束",
"ui.function": "関数",
"ui.general": "一般",
"ui.graphics": "グラフィックス",
"ui.graphics_custom": "カスタム",
@@ -1298,7 +1510,6 @@
"ui.heading_sheet_metal": "板金",
"ui.heading_shell": "シェル",
"ui.heading_sketch": "スケッチ",
"ui.heading_sketch_palette": "スケッチ パレット",
"ui.heading_sketch_scale": "スケッチの尺度",
"ui.heading_solid": "ソリッド",
"ui.heading_sphere": "球",
@@ -1317,6 +1528,7 @@
"ui.hideOrigin": "原点を非表示",
"ui.hidePlanes": "平面を非表示",
"ui.hideProfile": "プロファイルを非表示",
"ui.history_title": "履歴",
"ui.hole": "穴",
"ui.hole_tap_type": "ねじ穴タイプ",
"ui.hole_type": "穴タイプ",
@@ -1336,7 +1548,6 @@
"ui.length_decimals": "長さの小数点以下の桁数",
"ui.light": "ライト",
"ui.line": "線分",
"ui.linetype": "線種",
"ui.lock": "ロック",
"ui.loft": "ロフト",
"ui.look_at": "正面視",
@@ -1370,10 +1581,8 @@
"ui.mesh_format_3mf": "3MF",
"ui.mesh_format_3mf_tagged": "面タグ付き 3MF",
"ui.mesh_format_stl": "STL",
"ui.midpoint": "中点",
"ui.mirror": "ミラー",
"ui.mirror_line": "ミラー線",
"ui.modify": "修正",
"ui.move": "移動",
"ui.multiple": "複数",
"ui.named_views": "名前付きビュー",
@@ -1394,7 +1603,6 @@
"ui.offset_plane": "オフセット平面",
"ui.one_side": "片側",
"ui.open": "開く",
"ui.open_ellipsis": "開く…",
"ui.open_ends_tip": "どこにも接していない",
"ui.open_preferences": "環境設定を開く",
"ui.open_recent": "最近使った項目を開く",
@@ -1404,8 +1612,9 @@
"ui.orbit_around": "回転中心",
"ui.orientation": "向き",
"ui.origin": "原点",
"ui.palette_points": "点",
"ui.palette_profile": "プロファイル",
"ui.palette_construction_mode": "補助線モード (X)",
"ui.palette_more": "その他",
"ui.palette_sketch_options": "スケッチオプション",
"ui.pan": "パン",
"ui.pan_zoom_orbit": "パン、ズーム、オービット",
"ui.parallel": "平行",
@@ -1446,7 +1655,6 @@
"ui.quit": "終了",
"ui.radial_context_menu": "パイ メニュー",
"ui.radius": "半径",
"ui.radius_type": "半径タイプ",
"ui.rail": "レール",
"ui.rail_fit": "レールの合わせ方",
"ui.reachable": "到達可能",
@@ -1473,10 +1681,12 @@
"ui.reverse_horizontal_pan_tip": "水平方向の2本指スクロールとサムホイールで、反対方向にパンします",
"ui.reverse_zoom_direction": "ズーム方向を反転",
"ui.revolve": "回転",
"ui.ribbon_exit": "終了",
"ui.ribbon_pin_footer": "ピン留めしたコマンドはリボンに表示されます。行にポインターを合わせて、ピン留めや解除ができます。",
"ui.ribbon_workspace": "ワークスペース",
"ui.right": "右側面",
"ui.save": "保存",
"ui.save_as": "名前を付けて保存",
"ui.save_as_ellipsis": "名前を付けて保存…",
"ui.save_mesh_no_slicer": "スライサーが見つかりません。「カスタム…」でスライサーのプログラムを選ぶか、「スライサーに送る」をオフにしてください",
"ui.save_mesh_pick_bodies": "保存するボディを選択してください",
"ui.save_mesh_temp_failed": "{folder} を作成できません: {error}",
@@ -1489,7 +1699,6 @@
"ui.select": "選択",
"ui.selectOther": "その他を選択…",
"ui.selectTangentChain": "接線チェーンを選択",
"ui.select_other_hint": "その他を選択: モデル上で左ボタンを長押し(または右クリック › その他を選択…)するとカーソル下の要素が一覧表示されます。同じ場所を再度クリックすると次の要素が選択されます。",
"ui.selection": "選択",
"ui.selection_modifiers": "選択修飾キー",
"ui.selection_shift_add_only": "Shift+クリックで追加のみ(解除なし)",
@@ -1509,7 +1718,6 @@
"ui.show_constraints": "拘束",
"ui.show_construction_geometries": "構築ジオメトリ",
"ui.show_dimensions": "寸法",
"ui.show_points": "点を表示",
"ui.show_profile": "プロファイルを表示",
"ui.show_projected_geometries": "投影ジオメトリ",
"ui.simple": "単純",
@@ -1520,7 +1728,6 @@
"ui.sketch_grid": "スケッチ グリッド",
"ui.sketch_modifiers": "スケッチ修飾キー",
"ui.sketch_scale": "スケッチ尺度",
"ui.sketch_status": "スケッチの状態",
"ui.sketches": "スケッチ",
"ui.slice": "スライス",
"ui.slicer": "スライサー",
@@ -1566,7 +1773,7 @@
"ui.stand_up_tip": "Z軸上向きで開いたSTEPまたはIGESファイルに対し、X軸周りに90度回転させるか確認します",
"ui.start": "開始",
"ui.start_over": "最初からやり直す",
"ui.start_radius": "開始半径",
"ui.start_page": "スタートページ",
"ui.stretch": "伸縮",
"ui.supersampling": "スーパーサンプリング (SSAA)",
"ui.supersampling_adaptive": "ビューが静止しているときのみ",
@@ -1616,13 +1823,24 @@
"ui.untitled": "無題",
"ui.up_axis_note": "ビュー(軸ビュー、ホーム、正面視)で上向きにする軸を指定します。デザインデータ自体は変更されません。",
"ui.updateToCurrentView": "現在のビューに更新",
"ui.variable": "可変",
"ui.view_centre": "ビュー中心",
"ui.visual_shaded_hidden_edges": "隠線付きシェーディング",
"ui.visual_shaded_with_edges": "エッジ付きシェーディング",
"ui.visual_style": "表示スタイル",
"ui.visual_wireframe": "ワイヤーフレーム",
"ui.workspace": "ワークスペース",
"ui.workspace_animation_description": "分解図、モーション スタディ、ストーリーボード",
"ui.workspace_available": "利用可能",
"ui.workspace_current": "現在",
"ui.workspace_design_description": "スケッチ、モデリング、アセンブリ、検査",
"ui.workspace_design_tabs": "モデル · サーフェス · アセンブリ · 挿入 · 検査 · 管理 · 表示",
"ui.workspace_drawing_description": "ビュー、寸法、表題欄を含む2D図面",
"ui.workspace_footer": "すべてのワークスペースで同じデザイン、履歴、選択を使用し、リボンのタブだけが変わります。スケッチやフィーチャーの編集中は切り替えできません。",
"ui.workspace_manufacture_description": "スライス、ツールパス、セットアップ",
"ui.workspace_planned": "予定",
"ui.workspace_render_description": "製品プレビュー、シーン設定、レンダリング画像",
"ui.workspace_render_tabs": "シーン · 画像",
"ui.workspace_simulation_description": "荷重、拘束、結果",
"ui.x_distance": "X 距離",
"ui.y_distance": "Y 距離",
"ui.y_up": "Y軸上向き",
@@ -1632,7 +1850,6 @@
"ui.zoom_to_the_cursor": "カーソル位置へズーム",
"update.available": "SolidCraft {version} が利用できます",
"update.button": "アップデート",
"update.button_tip": "SolidCraft {version} が利用できます",
"update.button_version": "{version} にアップデート",
"update.check_automatically": "アップデートを自動的に確認",
"update.check_now": "今すぐ確認",
@@ -1664,5 +1881,6 @@
"update.title": "アップデートがあります",
"update.up_to_date": "SolidCraft は最新です。",
"update.update_now": "今すぐ更新",
"update.you_have": "現在のバージョンは {version} です。"
"update.you_have": "現在のバージョンは {version} です。",
"↑ ↓ choose · Enter / Tab complete · Esc dismiss": "↑ ↓ 選択 · Enter / Tab 補完 · Esc 閉じる"
}
+617 -31
View File
File diff suppressed because it is too large Load Diff
+395 -39
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+387 -39
View File
File diff suppressed because it is too large Load Diff
+419 -39
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More