11 Commits
Author SHA1 Message Date
Matus Mastena 82e410f8c0 Add a colour wheel for the viewport backgrounds
The Display page could only take a typed #RRGGBB, which is precise but
no way to choose a colour by eye. The swatches are now buttons that
open a wheel.

The wheel does not replace the hex field, it sits beside it: typing an
exact value stays the quickest way to reproduce a known colour. On
submit it hands a hex string to the existing ModelSpaceBgChanged and
friends rather than writing the config itself, so the two routes
cannot drift apart on validation, persistence, or the MatchTheme
fallback an empty field triggers.

Also stops the Browser shipping in the default dock layout. It was
added to the right edge, which shifted the block palette to index 1
and broke four dock tests that encode the existing arrangement. Those
tests were right and the change was wrong: the earlier commit claimed
to leave the layout alone. BROWSER now docks the panel on first use,
so nobody's edges rearrange on upgrade for a panel they never asked
for.
2026-09-17 16:54:11 +02:00
Matus Mastena 1529a8d9c1 Pin the flake's git dependencies and lock its inputs
nix build now succeeds. The hashes were placeholders before, so it
could not have.

Reading Cargo.toml for the git sources missed two of them: cryoglyph
and dpi come in through iced, which pins its own forks, and they only
appear in Cargo.lock. Six entries, not four. The comment says to
regenerate the list from the lock for that reason.

flake.lock is committed alongside, so a checkout resolves nixpkgs and
rust-overlay the same way next month as today.

Also asserts the snap default instead of leaving it to be eyeballed.
It is only visible in a fresh profile — the app persists settings on
change, so an existing settings.json keeps its own osmode and never
shows what a new install would do.
2026-09-17 16:54:11 +02:00
Matus Mastena 930cdc2df8 Cover the face-to-plane path with tests
UCS FACE had no test at all: it was only known to compile. The plane
it builds has two halves and they sit either side of a visibility
boundary, so the coverage splits to match.

tests/ucs_face_plane.rs takes the public route — extrude a disc, then
check the caps come back as distinct planar faces with opposite
outward normals, that an off-centre pick lands on the same face as a
central one, and that the curved wall is never offered as a plane.

The axis construction is pub(crate), so it is unit-tested next to
ucs_from_normal instead: Z is the normal it was handed, the axes stay
orthonormal, a normal along world Z still yields a plane rather than
crossing with itself, the same normal is reproducible, and a zero
vector is refused rather than silently producing a degenerate UCS.
2026-09-17 16:54:11 +02:00
Matus Mastena fe4c337425 Group the Fusion palette literals and mark the constructors must_use
Byte-pair separators in the hex colours, and #[must_use] on the four
constructors, which return a value and do nothing else — dropping one
is always a mistake rather than a choice.

Found by clippy::pedantic. Only this file is touched: the same run
reported ten thousand warnings across the tree, nearly all numeric
cast lints that are load-bearing in geometry code, so fixing them
wholesale is a separate decision rather than a drive-by.
2026-09-17 16:54:11 +02:00
Matus Mastena 7e816f992e Ignore the worktree directory
Isolated runs check out into .claude/worktrees/. Leaving it untracked
would put a whole second copy of the tree in reach of git add -A.
2026-09-17 16:54:11 +02:00
Matus Mastena d1c3106283 Add the Browser panel
A docked outline of the drawing: the three origin planes, the open
sketch, and every solid labelled with the operation that made it,
recovered through primitive_property_operation. Rows run ordinary
commands, so clicking a plane starts a sketch on it and clicking a
body selects it.

It is a table of contents, not a feature tree. The DWG history records
how each solid was built but nothing re-runs it, so an earlier feature
cannot be edited to rebuild the ones after it. Presenting the list as
editable history would promise exactly that, so the panel states what
it is instead.

Off by default and opened with BROWSER, leaving the existing dock
layout alone for anyone who does not want it.
2026-09-17 16:54:11 +02:00
Matus Mastena 1aa299e204 Add sketch mode
CREATESKETCH opens a sketch on the current UCS, or on one of the three
origin planes with XY, XZ or YZ. FINISHSKETCH closes it again.

Nothing here is new machinery: the plane is a UCS, the square-on view
is PLAN, the drafting aids are the snapper. What the pair adds is
holding them together and putting them back afterwards, including the
snap master state, so a user who works with snapping off gets that
back rather than silently keeping it.

Squaring the view up is not cosmetic. Drawing on a plane seen nearly
edge-on is the main way geometry lands somewhere the user did not
mean, because a few pixels of cursor travel cover a long way in world
space.

A sketch is a mode rather than a document object, so what it produces
is ordinary model-space geometry and the file stays a DWG other
software can open. Whatever was drawn becomes the Previous selection
set, so EXTRUDE then Previous consumes it without making the user
reselect a profile they just finished.
2026-09-17 16:51:41 +02:00
Matus Mastena bfb786d56e Add UCS FACE and UCS OBJECT
Draw a profile, extrude it, then sketch on a face of the result: the
operations were all there, but nothing turned a solid's face into the
active drawing plane, so the second half of that loop was unreachable.

UCS FACE takes a surface pick, finds the planar face under it and
builds the plane from the face normal. X comes from the Arbitrary Axis
Algorithm, so the same face always yields the same X axis and a sketch
drawn on it does not spin when the UCS is rebuilt.

UCS OBJECT adopts a planar entity's own stored axes instead of
re-deriving X, so a sketch started from a circle lines up with how
that circle is stored. It reuses the profile extraction EXTRUDE
already runs, which means anything extrudable can also be sketched on.

Bare UCS FACE starts an interactive pick that resolves through the
same inline handler, so the clicked and typed paths cannot diverge.
2026-09-17 16:51:41 +02:00
Matus Mastena 648b261fd1 Add the Fusion Black and Fusion White themes
Fusion Black pairs near-black chrome with a paper-white canvas, so the
drawing is the only bright thing on screen. That breaks the assumption
in theme_canvas_background that the canvas follows the chrome, hence
the explicit lookup: falling through to the palette would paint model
space black and undo the point of the theme.

Both keep a blue accent rather than going fully greyscale. Selection
highlight defaults to the theme primary, and a white primary would be
invisible against a white canvas.

These are Theme::Custom values and so are absent from iced's
Theme::ALL. all_themes() is the list to enumerate from now; the picker
in the options dialog uses it, and parse_theme_name resolves both by
name.
2026-09-17 16:51:41 +02:00
Matus Mastena a570f4ac16 Turn object snap on by default
The running snap set was already seeded with endpoint, midpoint,
centre, node, quadrant, intersection and nearest, but the master
suppress bit was set, so none of them did anything until the user
found the status-bar pill. Snapping is what makes a drawing close
rather than nearly close; it should be live from the first click.

Only fresh configs change. A stored settings.json keeps whatever
osmode it already holds, and a legacy file carrying its own $OSMODE
still overrides on open.
2026-09-17 16:50:46 +02:00
Matus Mastena ce0788389b Add a Nix flake for the dev shell and package build
The devShell carries the toolchain plus the X11, Wayland, Vulkan,
fontconfig and freetype libraries winit and wgpu dlopen at run time,
and the trunk/wasm-bindgen pair the web build needs. LD_LIBRARY_PATH
is set there too, since a dev binary runs straight out of ./target and
never gets the wrapper the package build applies.

The four git dependencies each need an entry in cargoLock.outputHashes.
They are lib.fakeHash for now; the first `nix build` prints the real
values, and they have to be refreshed whenever a rev in Cargo.toml
moves.
2026-09-17 16:50:46 +02:00