The command driver was split into command_driver/ upstream; the PDF
underlay and XCLIP additions to the old file move with it: their
CmdResult arms into the router (mod.rs) and the block-reference
selection filter with its clip lookup into utilities.rs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The palette launcher arrow is recoloured from the theme like the other
interface icons, instead of drawing the raw SVG.
- The Frames dropdown shares one icon constant; its entries were left
misindented.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- An inverted clip's roundtrip record goes in the filter's extension
dictionary through the codec's own API (ensure_extension_dictionary /
xrecord / extension_dictionary_handle) instead of a scan of every
object, which the renderer ran for each clipped reference.
- Pin cadcodec 1c3be0c (the DXF writer now links a spatial filter's
extension dictionary, so that record is not left unreferenced) and
cadkernel deb529b; plugin crates and the Python plugin lock follow.
- The XCLIP boundary preview stays f64.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Pin cadcodec upstream at d290dbe (the merged underlay change) and
cadkernel at 4134ae3, which follows it; drop the fork patch, move the
plugin-API and web-worker crates and the Python plugin lock to the same
codec so one copy is built.
- "Import as block" with raster images left IMAGE entities without an
image definition: block definition now creates it like add_entity does,
through one shared helper.
- The three preview outlines (attach frame, clip boundary, import area)
stay f64 instead of casting world points to f32.
- Page size / total / PDF scale entries use the catalog's positional
placeholder; the bare {name} form failed to format in 20 locales.
- The Underlay Layers bulb is the SVG layer icon, not a text glyph.
- PDF dialogs use the shared dialog buttons and radios.
- Options opened from a PDF dialog clears its saved state on return.
- Paper-space underlay background reuses the model block handle already
resolved; dead `let _ = i` lines and a misplaced doc comment removed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The ribbon definition beside each command stays the one source of its
label and icon. One cached lookup (modules::registry::ribbon_commands)
walks the core ribbon and the expanded Draw/Modify panels; the shortcut
list, the right-click menu, the grip menu and the command-line
suggestions read it.
- Context menu: command rows carry their ribbon icon; Undo/Redo and
Isolate use interface glyphs, Draw Order its ribbon icon; icons and
the caret fade on disabled rows.
- Grip menu: Stretch, Move, Rotate, Scale, Mirror and Copy icons.
- Suggestions: an icon column, aligned when a command has none.
- Shortcut list: dropdown entries were read as (label, command) though
the ribbon stores (command, label), so they never got a label or icon.
Reworked from #1449.
Co-Authored-By: MarcelP102 <30177694+MarcelP102@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
New drawings now default to metre insertion units, and the plot dialog
reads a ratio against the drawing's units, so these tests (written for
millimetres) saw 1:100 as 10 mm per unit. The metre case keeps its own
test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Tests resolved the real per-user config dir. When the alias table
version moved, parallel tests migrated and rewrote the user's alias
file concurrently and one left it holding a single alias. Under test,
config_dir() is a per-process temp folder and the alias table comes
from the shipped defaults with no file I/O.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- `XREF <args>` runs whole: probing the bare verb first now opens the
External References palette, which refreshed every entry and toggled
the palette before `XREF Reload A` / `XREF ?` ran.
- Update the two tests that encoded the old behaviour: `-XREF ?` prints
the reference table, and attaching a missing file is rejected.
- Drop the duplicate "Specify On-screen" catalog entry that shadowed the
translated common key, with its unused ftl keys.
- Move `remove_pipe_symbols`' doc comment back above its function.
- Note in `document_for_save` that resolved xref content stays out.
- Gate the desktop-only preview cap and tab index for wasm.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#1469 inserted `CommitMany` and `CommitManyAndEnd` between `Commit`,
`CommitAndEnd` and `Done`. `CommandStep` crosses the plugin IPC, which
encodes a variant by its position, so a plugin built against the previous
API that answered `Done` would have been read as `CommitAndEnd` — or
rejected as undecodable — by the new host. The two variants now sit after
`Cancel`, as the other additions to the v7 wire types do.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The specification this pull request adds now describes code that exists:
the operations it documents came in with the previous commit. The file
keeps the fork's later revision, rewritten against that code, which also
carries the map of the surface's pieces.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in the DXF round-trip fixes merged as cadcodec #48 and #51 and the
block-definition fixes for cadcodec issues #49 and #52. cadkernel moved to
the same codec revision first, so the graph keeps a single acadrust. The
Python plugin, which is outside the workspace, has its own lock moved too:
its staging script requires the plugin and the host to share one acadrust
source.
The canaries that asserted the old wrong results were flipped against the
real-IPC audits, all 60 of which pass with the plugin built: Helix, Light,
Underlay, ViewBorder and SectionSymbol now round-trip through DXF, as do the
text style flags, the dimension style's text style name, the block
description, the table's merged ranges and the leader's annotation link and
vectors. Those five kinds move to Complete in the coverage ledger.
Two narrower gaps stay pinned. The ATTDEF writer emits no group 280, so
`lock_position` is still lost on a DXF save; every other field now
survives. A true-colour leader `override_color` cannot be held by DXF group
77, which takes an ACI index, and the DWG writer does not store it.
Behaviour change for users: a text style's oblique angle is now written to
DXF in degrees, as the format defines it. DXF files written by earlier
versions carry radians there and reopen with a much smaller slant.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The 2Lines placement (#1459) wrote the second line's new ends back with
`set_resolved_point`, which was not the inverse of the `resolve_point` it
paired with. `resolve_point` reads polyline vertices in WCS through the
polyline's OCS; the writer put that world point straight into the OCS
vertex, so a mirrored polyline (normal -Z, common in real drawings) had
its vertex flipped, and the closing segment's end marker — which names
vertex 0 on a closed polyline — found no vertex and the placement was
silently dropped. The writer now converts back into the OCS, keeps the
polyline's own elevation, and wraps the closing marker the same way.
The same arm pushed `test_pline_line_then_arc` past the default test
thread's stack: its locals, a cloned entity among them, sat in the frame
of the command-result dispatcher, which is on the stack of every click.
The placement is built out of line and handed over boxed.
Merging #1447 had left nothing setting DIMCONSTRAINT's default to
Diameter; the Dispatch arm #1461 turned into a match now covers it with
Radius and Convert. DCCONVERT's result lines go through the translation
macros.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The bundled `opencad.python` plugin drives OCS through plugin API v7: all
43 canvas entity kinds, the drawing tables, and OCS's own commands run
step by step. RustPython stays in the plugin process — the plugin crate is
outside the workspace with its own lock file, so the application's own
dependencies are unchanged.
Reconciled with the plugin command work that landed as #1425, which the
branch was written before:
- Both added `impl Drop for PluginProcessInteractiveAdapter`, which git
merges silently into two impls that do not compile. One body now
cancels the command and then drops it from the runner, in that order: a
plugin waiting on a step has to be released before the runner forgets
the command.
- `execute_command` (#1425) and `run_command` (#1423) are both routes from
a script to the command line, and only the second was guarded. The
SCRIPTCOMMANDS switch and the denylist are shared now, so neither route
is the unguarded one.
- `run_command` dispatched with plugin dispatch still enabled, so a script
naming a plugin command re-entered the plugin that was blocked waiting
for it. It sets the suppression flag for the length of the dispatch, as
`execute_command` does.
The denylist read the command as typed, while the dispatcher resolves
aliases: a user-defined `Q` for `QUIT` (ALIASEDIT writes those) walked
past it. It resolves the alias first.
Also: the active-command comparison ran on every message, a mouse move
included, allocating two strings for names that are already `&'static
str`; SCRIPTCOMMANDS' own messages go through the translation macros; and
`docs/.DS_Store` (now ignored) and an unrelated RTF note about constraint
commands are out of the tree.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
#1447 branched from the same commit as #1443 and rewrote the radial
dimensional constraint a second time, so the two designs had to be
reconciled rather than stacked. #1447's is the one kept: it covers
DCRADIUS and DCDIAMETER through one command, places the dimension by the
direction the location was picked in rather than storing a text point,
and names the parameters `radN` / `diaN`. #1443's radial command result,
its entity builder call and its follow-up code are gone; its own
independent work stays — the ribbon's small-button grid and tool
descriptions, the magnitude reading of a negative radius, and the centre
anchor a parameter edit resizes a circle about.
The DIMCONSTRAINT default follows #1443: running DCRADIUS or DCDIAMETER
directly leaves it alone, and the Dispatch arm sets it when DIMCONSTRAINT
is what asked for the command.
The second commit's radial drawing — the dimension line broken around the
text, the elbowed leader, the centre-mark and arrowhead rules — applied to
every radius and diameter dimension, not just a constraint's, and three
layout-parity tests said the old layout was the measured one. It is now
scoped to a dimensional constraint's own dimension, as #1443 scoped its
version, and ordinary radial dimensions draw as they did.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A new drawing (#1441) recorded ISO-25 as the current dimension style by
name only. Every other place that changes it sets the header's handle as
well, and a DWG carries the current style by handle alone, so a saved new
drawing reopened on Standard. The style's handle now comes back from the
helper that adds it.
A radius constraint (#1443) anchored the circle's centre from the shape of
its references — one whole entity — but a single whole reference is also a
line's length, whose start is what stays. The entity decides now.
The two tests that encoded the replaced rules follow them: a negative
radius validates by its magnitude, as a negative distance already did, and
only zero is refused; a new drawing's dimension text reads at ISO-25's
comma separator.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Three doc paragraphs had stacked above one helper, so each described the
function after the one it sits on.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Direct distance entry (#1429) turned every bare number at a point prompt
into a distance, so ROTATE's angle and the values automation types were
read as coordinates. The step reads the token first now — `feed_command`
reports whether it claimed it — and only an unclaimed one becomes a
distance. That also puts the token back on the unified step driver, which
owns the P / L selection keywords and the dimension-snap bookkeeping, and
stops `on_text_input` running twice for the same token. A tracking ray
carries its own base, so it no longer demands an anchor that the first
point of a command does not have yet.
Plugin command execution (#1425) re-implemented the command line inside
the plugin host: it dispatched families directly, skipping the teardown
that starts a command (grip edits, selection gestures, `last_point`, the
accepted snaps), and made every `CmdResult::Dispatch` in the application
skip plugin dispatch to avoid its own re-entrancy. A scripted line now
runs through `run_command_line`, with the trailing Enter left off while
the plugin streams the command, and the re-entrancy is handled by a flag
set for the length of the plugin's call. A pause parks the line's Enter
with the rest of its tokens instead of dropping it.
A font reference (#1432) tried the family lookup before the face-file
index, and that lookup ends in a prefix match a longer file name loses to
(`ISOCPEUI.TTF` would take `ISOCP`). The exact file answers first.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
#1418 added both as direct dependencies although each was already in the
tree: printpdf's to_lopdf_doc returns the lopdf document whose compress
and save_to are plain methods, and the codec's encoding_from_code_page
hands back the Big5 / GBK / Shift_JIS / EUC-KR encodings by their DXF
code-page names. Cargo.toml keeps its dependency list.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
#1415 made a dimensional Distance keep a negative value's sign in the
parameter while the geometry uses its magnitude (and a zero fold the
points together), but left the value-domain test asserting the old
refusal. Assert the new reading, and keep the radius refusal covered.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Symmetric held the connecting direction perpendicular to the axis with
the kernel's angle-based Perpendicular, which locks its turn side at
construction: a point starting far from its mirror image had to swing
the pair through the axis direction, the residual wrapped, and the
solver stalled with the point unmoved. The perpendicularity is now the
projection of the pair onto the axis direction being zero, a polynomial
residual the solver follows from anywhere. Circle centers and ellipse
centers use the same equation. tests/parametric_constraints_solve.rs
symmetric_constraint_mirrors_one_circles_center_across_the_axis_line
passes again.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- .github/workflows/ci.yml runs `cargo test --workspace --locked` on
pushes to main and on pull requests, with the Linux libraries the
release build installs. No formatter check: the tree is not
rustfmt-clean and stays that way.
- flake.nix, flake.lock and the direnv hook are removed. Nothing used
them, and their pinned dependency hashes were already stale after the
codec and kernel bumps.
- Under cfg(test) the "system" language resolves to en-US. The unit
tests compare English text through one process-wide loader, and every
apply_config of a default config asked for the system language, so a
Turkish locale failed 22 tests and parallel tests raced each other
through a language switch (the thickness-row test found its section
under one language and looked it up under another).
- The arc grip test follows #1352: the start, end and midpoint grips
drive the whole arc.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Follow-up to the block-attribute grips picked from #1386.
- Movability no longer includes the invisible flag: an attribute ATTMODE 2
displays is grippable, so the grip the display-aware list shows can be
dragged. The document-less grips() still skips invisible attributes,
since it cannot consult ATTMODE.
- The grip sits on the displayed attribute, which an annotative insert
scales about its insertion point; Scene::apply_grip now unscales the
dragged target the same way before the attribute stores it, so the grip
no longer jumps to twice the drag at scale 2.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- resolve_point, set_polyline_vertex and the solver's point_for_marker
treat the marker one past the last vertex of a closed polyline as
vertex 0, the way line_segment already did. Equal (#1366), the axis
pre-alignment (#1365) and the transform pin (#1361) address a picked
segment's end that way, so on the closing segment they either refused
the pick or pinned nothing.
- Page-setup dictionary keys compare case-insensitively: page_setup_save
replaces "site" when "Site" exists instead of adding a second entry
(PSETUPIN counted it as replaced, #1344), delete and rename match the
same way, and a rename that only changes case is allowed.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The page merged from #1401 described --serve and --mcp as one surface.
Rewritten against src/app/automation.rs, src/app/control/mod.rs and
src/mcp.rs: the --serve readiness banner and --port mode, the two request
styles on that channel and how each treats a waiting command
(command_busy versus replacement), measure being available headlessly
while capture is not, --export writing DWG for every extension but .dxf,
startup dialogs belonging to the editor behind --mcp only, MCP sessions
being shared rather than one editor per connection, the MTEXT editor's
own actions, and the legacy run reply fields (blocked_by, unconsumed)
that only that path returns.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- The acadrust DXF reader has converted ATTRIB/ATTDEF rotation to radians
since f226110 (in every codec rev OCS pinned from 2026-08-08 on), while
fix_dxf_dimension_rotations still applied to_radians a second time on
load. Only Shape needs the conversion here. Surfaced by #1391's hunk.
- Shape-file text styles are found by their is_shape_file flag instead of
an empty name (also from #1391).
- #1393's add_layer/modify_layer wrote an unresolved linetype name into
the LAYER record; a name the drawing (plus the standard set) cannot
supply is now refused, through one resolve_linetype helper.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
acadrust follows hakanaktt/acadrust main through #107, entity-record
passthrough included; cadkernel pins the same codec rev so one acadrust
stays in the graph. Upstream added BlockFlags::is_xref_unloaded, which
XATTACH now sets to false for a freshly attached reference.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- tests/fixtures/plot/.page-setups-metric.dxf.ocs.lock was the app's own
single-instance lock file, committed by accident in #1343; remove it and
ignore *.ocs.lock.
- load_plotsettings_into_dialog: #1343's refresh_custom_scale_fields borrowed
self while #1360 still wrote through the plot_dialog borrow; write the
style error first.
- The wasm list_printers stub returns the Result #1358 gave the native one.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- acadrust 7ea4247: clamped-knot degree and MINSERT reservation fixes
(cadcodec #47). cadkernel a0b962a: dogleg fails on a non-finite
Jacobian (cadkernel #66) and pins the same codec rev, so one acadrust
stays in the graph.
- The axis re-alignment from #1361 retains its transformed length through
retain_segment_length, which holds a zero-length segment with per-axis
differences instead of a P2PDistance whose gradient is NaN there.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- #1352 pinned cadkernel/cadkernel-constraints to ramox81's fork. Its one
extra commit (cadkernel #67, SymmetricLineDirections) is now on
HakanSeven12/cadkernel main as the same SHA, so the manifests and lock
point back at the upstream repo at rev 0493b59.
- SYCONSTRAINT reports a repeated relation with the same message Fix and
Vertical use, as an error instead of plain output.
- The knot-less spline test consumes the add_entity results.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Follow-up to #1341:
- interactive UCS offers Face / OBject; bare UCS OBJECT starts an entity pick
- one UcsPickCommand serves both FACE and OBJECT
- ucs_from_normal calls the existing arbitrary-axis helper instead of a copy
- prompt catalogue key and en-US text follow the new option list
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>