Commit Graph
1007 Commits
Author SHA1 Message Date
lforgiariniandClaude Sonnet 5.5 b8efe39acc fix(ui): Properties palette tells values apart that the display rounds alike
The shared values of several objects were compared as the text each row shows, so a
text height of 7 and one of 7.000001 (both "7.0000") read as one common value. Numeric
rows now carry the number they were formatted from and compare it to 12 significant
digits, which separates anything the display could show at 8 decimals and ignores float
noise.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-01 12:42:12 +01:00
lforgiariniandClaude Sonnet 5.5 b1bab3043c test(ui): Properties palette with several objects: undo, mixed types, one object
Covers the cases a reviewer asked for: one undo restores every edited
object, a mixed selection shows General only (no Radius row), and one
object shows its own value with nothing varying.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-01 09:20:46 +01:00
lforgiariniandClaude Sonnet 5.5 50bc6c1158 fix(ui): Properties palette keeps the polyline Closed value untranslated in its memo
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-31 09:38:12 +01:00
lforgiariniandClaude Sonnet 5.5 f8f0504b9a perf(ui): Properties palette keeps the selection's shared values per revision and selection
Working out every shared value on each frame made the palette slow with thousands of
objects selected. The values (and the selected handles' hex ids) are kept in ui.data
until the drawing revision, the selection or the units change. Entity helpers no longer
clone whole hatches, texts or polylines for one field. Tests: a value typed in the
palette reaches every selected object; the shown values follow revision and selection.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-31 09:38:12 +01:00
lforgiariniandClaude Sonnet 5.5 6ecabc3451 fix(ui): several block references show the dynamic block's name; gaps.md status line
The Name row of the several-objects Properties palette used the block's own name, which for a
dynamic block is an anonymous `*U..` that differs from one reference to the next. It now resolves
each reference's name as the single-object palette does. docs/gaps.md: status line updated for the
revision entry.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-31 09:38:11 +01:00
lforgiariniandClaude Sonnet 5.5 c223550cb3 feat(ui): Properties palette shows the type's properties for several objects of one type
With more than one object selected the palette showed only General. When all selected objects are
of one type (line, circle, arc, text, mtext, polyline, hatch, block reference, dimension) it now
shows that type's properties, `*VARIES*` where they differ; an edit applies to every selected
object. Positions stay read-only, since typing one would move all objects to the same place.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-31 09:38:10 +01:00
ZaxaerithandZaxaerith 7cee28580d fix(dxf): preserve 3D LEADER vertex elevations (#2245) (#2397)
Co-authored-by: Zaxaerith <189452125+Zaxaerith@users.noreply.github.com>
2026-10-31 00:34:27 -07:00
a83af92eb2 wblock: write the source drawing's $INSUNITS and $MEASUREMENT (#2002) (#2023)
* wblock: write the source drawing's $INSUNITS and $MEASUREMENT

run_wblock built the written drawing from Drawing::new_imperial() and
never copied the source's units, so every WBLOCK file declared inches
($INSUNITS 1) and imperial measurement ($MEASUREMENT 0) whatever the
source: a block written from a metric drawing came back at 0.0254 of
its size in any program that honours $INSUNITS on insert. The written
drawing now takes the source's values for both variables, as AutoCAD's
-WBLOCK does. A unitless source ($INSUNITS 0, kept by the reader per
#1209) writes unitless.

Co-Authored-By: GLM-5.3 <noreply@z.ai>

* wblock: start from the source drawing's header and styles

Clone the source header (resetting $INSBASE as before) and copy the
multileader, table and multiline styles and the annotation scale list
along with the other tables, so a block written from a metric drawing
keeps its text size, current dimension style, limits and scales, not
the imperial template's (review on #2023).

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

* wblock_units: each WBLOCK call writes into its own TempDir

The two tests shared one temp path and raced when cargo ran them in
parallel (review on #2023).

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

---------

Co-authored-by: GLM-5.3 <noreply@z.ai>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 00:34:23 -07:00
4552b35688 fix(ui): dash only the visible part of a highlight, so zooming far in doesn't freeze (#2299) (#2454)
* fix(ui): dash only the visible part of a highlight, so zooming far in doesn't freeze (#2299)

With the pointer on an object, its rollover highlight is dashed, and draw_highlight dashed the
object's whole outline in screen space, off-screen parts included: one shape per 10 points of
it. Zoomed far in, a short line is billions of points long, so the app froze and then ran out
of memory, which is the freeze and crash #2299 reports. Wheel-zooming a circle and two lines
with the pointer on them reached 7.9 million shapes in one frame at a view height of 0.001
before a 1 GB allocation failed.

The highlight now clips each outline to the view, plus a margin, in world coordinates first
(precise when zoomed far in), and dashes only what is left. Each visible piece starts its dash
pattern at its distance along the whole outline, so dashes don't crawl along a line that runs
off screen while the view pans. The two dashed rectangles drawn in screen space, a layout's
printable area and the crossing window, clip each edge to the painter's clip rect for the same
reason. The same zoom now stays under 1,300 shapes per frame down to the zoom limit.

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

* fix(ui): a clipped rectangle edge keeps its dashes where the whole edge puts them

dashed_rect clipped each edge to the screen and then started its dashes at the clip boundary,
so a printable area or crossing window running off screen had its dashes move with the view,
and its bottom and left edges were drawn in reverse. Each edge now keeps its direction and its
dashes start at their distance along the whole edge, as the highlight's outlines do. The
zoomed-far-in app test also checks that the line really is hovered, so it can't pass with no
highlight drawn.

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

---------

Co-authored-by: austin62jake <340374338+austin62jake@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 00:34:18 -07:00
Tanish Mankuandoctopoulpos e60d546ce4 Picking after an edit no longer ends the app on an rstar panic (#2539)
rstar 0.13 can panic when inserting after removals in a bulk-loaded tree, and picking runs outside the command guard. The patch now runs on a copy under catch_unwind and falls back to a bulk load. The web build can't catch a panic, so it always bulk-loads. Diagnosis, reproduction and fix from #2468.

Fixes #2468

Co-authored-by: octopoulpos <32617732+octopoulpos@users.noreply.github.com>
2026-10-31 00:34:14 -07:00
Tanish MankuandFilippo Reimer 42be8540a9 Backups of drawings sharing a name no longer overwrite each other (#2537)
plan.dwg and plan.dxf in one folder shared plan.bak. A DWG keeps <name>.bak, as AutoCAD names it, and any other drawing appends .bak. The naming comes from #1006.

Co-authored-by: Filippo Reimer <49290121+filforce95@users.noreply.github.com>
2026-10-31 00:26:50 -07:00
Dylan M. Taylor a1335895fc Merge pull request #2527 from aiongg/docs-tidy
docs: tidy agent, contributor and asset docs
2026-10-31 01:06:48 -04:00
Dylan M. Taylor b6c7a73fa6 Merge pull request #2530 from aiongg/update-result-dialog
Updates: shared check-result dialog from craft-update
2026-10-31 01:05:52 -04:00
b35f00847a Validate new linetype names in the rename command (#2519)
* Validate new linetype names in the rename command

Found while testing crates/engine/src/cmd/props.rs:1154-1200 (run_rename, "linetype" branch)

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

* rename: a linetype's new name is stored trimmed, as -RENAME types it (CodeRabbit)

---------

Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-31 00:45:52 -04:00
Brandon ThomasandClaude Opus 5.5 c6e49b177c test(autosave): fix a flake: wait for the background write to finish, not just for its file
Since #2518 the write syncs the folder after the rename, so the autosave
file exists while the write is still running. Until it finishes the drawing
is busy and the next change isn't planned, so the test's tick() returned
None. Autosaver::writing() says whether a write is still running.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 00:44:27 -04:00
aiongg 11ce7eb52c updates: show check results with craft-update's CheckResultDialog 2026-10-31 04:42:07 +00:00
aiongg 9b32882065 docs: tidy wording 2026-10-31 04:25:44 +00:00
aiongg ee715f4636 docs: tidy agent, contributor and asset docs 2026-10-31 04:17:25 +00:00
Brandon ThomasandClaude Opus 5.5 660e15c9f4 Release 0.15.1
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
v0.15.1
2026-10-30 23:46:11 -04:00
Brandon ThomasandClaude Opus 5.5 d4eab70e63 DWG bridge: build without acadrust on FreeBSD aarch64, where 0.6.6 doesn't compile
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 23:46:09 -04:00
Brandon ThomasandClaude Opus 5.5 822f61b73a Release 0.15.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 23:22:50 -04:00
Tanish Manku dc1442ac89 Merge pull request #2525 from storytold/land/2467
fix(io): MTEXT keeps its R2018 embedded column object (#2248)
2026-10-30 19:25:50 -07:00
Tanish Manku 737f8a2ec6 Merge remote-tracking branch 'origin/main' into pr-2467 2026-10-30 19:25:46 -07:00
Tanish Manku b22a068262 Merge pull request #2524 from storytold/land/2201
Update symlink target in write_atomic instead of replacing the link
2026-10-30 19:25:40 -07:00
Tanish Manku d5128dfcd1 Merge remote-tracking branch 'origin/main' into pr-2201 2026-10-30 19:25:36 -07:00
Tanish Manku 60f1bd6277 Merge pull request #2523 from storytold/land/1693
DXF: save pre-2007 text in the file's code page instead of all \U+ escapes
2026-10-30 19:25:30 -07:00
Tanish Manku c808939bd1 Merge remote-tracking branch 'origin/main' into pr-1693 2026-10-30 19:25:26 -07:00
Tanish Manku 344c022071 Merge pull request #2522 from storytold/land/2460
fix(fonts): place Middle and bottom-justified text as AutoCAD does (#2229)
2026-10-30 19:24:52 -07:00
Tanish Manku 5e52e5285a Merge remote-tracking branch 'origin/main' into pr-2460 2026-10-30 19:24:37 -07:00
Jonasandcloudj 5bc93ccf18 test(io): add independent DXF/DWG conformance and preservation matrices (#2237)
* test(dxf): add deterministic codec and drawing round-trip matrices

Exercise typed tags and model-space LINE/CIRCLE/TEXT through in-memory ASCII and binary DXF at every save version. Compare both reads with the original model and check independent handwritten byte/entity fixtures. Keep desired-behaviour tests for extrusion and ByBlock plot-style loss explicitly ignored until fixed. No production or docs changes.

* test(dxf): independently verify common entity properties

* test(io): add a broad DWG preservation matrix with focused pending regressions

Share deterministic Drawing cases and exact assertions between DXF and DWG without reducing DXF coverage. Run representable DWG cases at all six save versions, twice in RAM. Test R2000 color/transparency and the single TEXT elevation as explicit format limits. Keep six independently reproduced loss regressions ignored without weakening comparisons. No production or docs changes.

* test(io): close binary-width and referenced-linetype coverage gaps

Check every integer group range against independently constructed binary payloads, catching shared reader/writer width mistakes. Resolve linetypes with Drawing lookup and compare layer linetypes too. Add missing/changed-reference self-tests and inherited-pattern DXF/DWG round trips. Reproduced the review counterexamples before and after; full CI passes with two nextest threads. Test-only changes.

* test(io): expand DXF and DWG matrices across entities and drawing collections

Add basic coverage for all 29 modeled entity kinds, block graphs and attributes, all ACI colors, richer properties/XDATA, sparse handles, headers, symbol tables, views, groups, layouts and foreign payloads. Preserve typed fields and authored references through two RAM saves. Add independent DXF groups and native DWG bridge checks, plus explicit refusal-or-preservation tests for unknown entities. Keep confirmed discrepancies and unresolved preservation contracts as focused ignored tests; native linetype angles and silently dropped unknown DWG entities are independently reproduced. Version limits and foreign object handle remapping are explicit contracts, not blanket normalization. Tests only; no production or docs changes. Full CI passes with two nextest threads: 2777 passed, 27 skipped.

* test(io): cover bounded property domains and targeted field interactions

Independently check all ACI values and transparency percentages, native
layer/style flag combinations, and literal large handles. Add shared DXF/DWG
cases for alignment with mirrored styles, inherited versus explicit zero
properties, repeated nested attributes, and integer-boundary handles.

Keep exhausted handle allocation separate: DWG refuses the save; a focused
ignored DXF regression reproduces silent loss of all nine model entities.
No production or documentation changes.

Validation: full cargo xtask ci with NEXTEST_TEST_THREADS=2 passes all seven
gates (2792 passed, 28 skipped). The pending DXF regression fails explicitly
when run with --ignored.

* test(dxf): separate chunk conformance from binary reader tolerance

Address PR #2237 review: shared exact-tag round trips only use hex chunks
within the documented 127-byte group limit. Independently inspect ASCII
and binary writer bytes, reassemble payloads, and verify following-record
framing. Keep full 0..255 length-byte read tolerance in handwritten fixtures,
not as a claim of conforming writer output.

Add two focused ignored regressions for oversized ASCII/binary output,
reproduced with 128-byte payloads. They extend existing issue #1025.
No production changes.

Validation: cargo xtask ci passes all seven gates with nextest 0.9.150
and two test threads (2782 passed, 30 skipped). Both new pending checks
fail explicitly when run with --ignored.

* test(io): compare DWG dimension blocks through their references

Anonymous dimension-block suffixes may change between drawing sessions (Autodesk documentation; ODA DWG section 20.4.52.1). Resolve their names through preserved dimension identities and keep strict checks for cache content, entity handles, block properties and reference integrity.

Enable the pending dimensions test across all six DWG versions and two saves. Add a paired-cache fixture and comparator controls rejecting swapped/merged/dangling references, geometry and property changes, stale caches, and named-block renaming. Leave the shared comparator and DXF contract unchanged.

Validation: cargo xtask ci (all seven stages; 2786 passed, 29 skipped); roundtrip matrix (211 passed, 26 ignored).

Resolves the preservation-contract decision in #2257 (tracker #2238, B4); no production codec fix.

* test(io): specify WIPEOUT explicit closure on roundtrip

An implicit WIPEOUT boundary gains one explicit closing marker in DXF/DWG. Compare against independently authored explicit boundaries in both winding directions across all six versions and two saves; retain strict field and resave comparisons. Enable the two pending closure checks without changing the shared comparator.

Distinguish this valid file representation from the grip-edit defect: the latter is fixed separately in PR #2284, with engine, outline and rendering regressions. No production change in this test-only branch.

Validation: cargo xtask ci, all seven stages passed (2788 passed, 27 skipped); active roundtrip matrix 213 passed; remaining ignored checks reproduce 24 drawing failures and two codec failures. One unrelated HTTP control-server test failed transiently, then passed in isolation and in the full rerun.

Related #2256 and tracker #2238 B3.

* test(io): expose multiline ATTRIB alignment corruption

Keep the original origin witness and add distinct nonzero TEXT/MTEXT anchors. An active R2018 ASCII/binary two-save check verifies world-space multiline geometry and value edits; this does not excuse corrupted fallback TEXT fields.

Retain the strict pending #2255 check and add comparison of independently downgraded R2000/R2004 geometry. The observed Left -> Center change is a real reader error, not permitted alignment-point canonicalization. The reader hunk from existing PR #1942 makes the extended strict check pass in an isolated experiment; no competing production fix is included here.

Validation of this test-only tree: all seven cargo xtask ci stages pass (2789 passed, 27 skipped); active matrix 214 passed; remaining 24 drawing and two codec diagnostics still fail. An unrelated HTTP control-server test failed transiently, then passed alone and in the complete rerun.

Related #2255, #1942 and tracker #2238 B2.

* test(io): separate ATTRIB value and legacy-placement guarantees

Assert exact saved ATTRIB values, read_as and authored embedded MTEXT contents before and after value edits. A mutation test rejects corrupt fields, including a value/read_as pair whose rendered geometry remains unchanged.

Make legacy placement an independently runnable pending regression instead of a callback behind the failing field comparator. Compare direct R2000/R2004 exports with exports after one/two R2018 roundtrips, both encodings in each stage, and verify that the primary single-line fallback actually survives. Keep all three contracts local and distinct; remove the old cross-module callback.

The new legacy test fails directly on world-space geometry with the unchanged reader. Both pending ATTRIB tests pass with the reader hunk from existing PR #1942 in an isolated experiment (548 IO tests also pass). No production fix is included.

Validation: all seven cargo xtask ci stages pass with NEXTEST_TEST_THREADS=1 (2790 passed, 28 skipped). Earlier parallel runs and one isolated retry hit pre-existing HTTP/busy control-server failures; no exclusions or test changes. Active matrix 215 passed; all 25 drawing and two codec pending diagnostics still fail.

Addresses review of #2237; related #2255, #1942 and tracker #2238 B2.

---------

Co-authored-by: cloudj <jonbahnes@gmail.com>
2026-10-30 19:24:31 -07:00
Tanish Manku 9c0c02f917 Merge main into #2467 2026-10-30 19:20:06 -07:00
Tanish Manku d79f4f5732 Merge main into #2201 2026-10-30 19:20:04 -07:00
Tanish Manku 8822462be2 Merge main into #1693 2026-10-30 19:19:59 -07:00
Tanish Manku e348a30ebd Merge main into #2460 2026-10-30 19:19:54 -07:00
Tanish Manku b187af646a Save helpers used only by the file system build stay out of the wasm one (#2521) 2026-10-30 19:19:06 -07:00
ElarionusandEthan Dale 75efe07d85 Enter on a partly typed command runs the highlighted suggestion (#2135)
Co-authored-by: Ethan Dale <ethandale@Ethans-MacBook-Pro.local>
2026-10-30 19:16:59 -07:00
assamaralandClaude Fable 5.1 4792ca2941 fix(io): a centred ATTDEF keeps its insertion point in a DWG (#2464) (#2466)
* test(io): a centred ATTDEF keeps its insertion point in a DWG (#2464)

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

* fix(io): an ATTDEF with no default value is placed by its tag's width in DWG (#2464)

The writer keeps a justified text's stored insertion point only when it is near where the
baseline starts for the text's width; an ATTDEF with an empty default value has width 0, so every
AutoCAD insertion point further than half a height from the alignment point was replaced by the
alignment point. The tag is what is drawn: judge against its width.

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

* fix(io): keep a stored ATTDEF point that is close to the empty text's start

The tag-width baseline check replaced a point read from a drawing whenever the
font's width of the tag put the computed start further than the allowance away,
e.g. a centred LONGTAG with group 10 at x=99.5 and group 11 at x=100 moved to
about 92.17 in DXF and DWG. A stored point close to the empty text's own start
is now kept as before; only otherwise is it judged against the tag.

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

* test(io): store an ATTDEF point that differs from the computed baseline

`assert_kept(start)` stored exactly the baseline the tag-width fallback computes, so a write that replaced group 10 with that baseline passed too. The point is now 1 off the start, within the tolerance. Review of #2466.

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

* test(io): an empty-value ATTDEF keeps its point with an empty or absent prompt too

The tag-width check applies to every ATTDEF with no default value, since the
CAD displays the tag whatever the prompt: the guard now says `attdef` instead
of reading as a prompt test, and the test covers a prompt, an empty one and none.

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

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-30 19:16:55 -07:00
assamaralandClaude Sonnet 5.5 b4b8ae294a fix(io): write a LINE's extrusion (210/220/230) (#2241) (#2417)
* test(io): a LINE keeps its extrusion through DXF and DWG (#2241)

`line_extrusion_roundtrips_with_world_endpoints` (red: group 230 is not written for a LINE with
normal (0, 0, -1), so it comes back with (0, 0, 1)).

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

* fix(io): write a LINE's extrusion (210/220/230) (#2241)

A LINE's points are world coordinates and the reader already keeps its extrusion (the direction
of its thickness), but the writer left the groups out, so a normal (0, 0, -1) came back as
(0, 0, 1) from DXF and from DWG, which is written through DXF.

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

* test(io): a LINE's extrusion is written as a unit vector or not at all (#2241)

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

* fix(io): write a LINE's extrusion normalized, nothing for a zero or NaN normal (#2241)

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

* fix(io): write a tiny extrusion's direction, drop a non-finite one

Vec3::normalized returns zero at or below the drawing tolerance, so a LINE with
extrusion (0, 0, -1e-10) saved with no 210/220/230 and reopened as +Z, flipping a
thickness; an infinite component normalized to NaN and was written as a zero
vector. The writer now normalizes by the vector's own length and writes nothing
only for a zero or non-finite vector.

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

* fix(io): keep the direction of a huge finite extrusion

The length of (0, -1.5e308, 1.5e308) overflows to infinity, so the normal came out as (0, 0, 0) and was written as such. The vector is scaled by its largest component before it is normalized; the case is in `line_extrusion_is_written_as_a_unit_vector_or_not_at_all`. Review of #2417.

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

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-30 19:16:51 -07:00
assamaralandClaude Sonnet 5.5 85fc3991b1 fix(io): an attached ATTRIB keeps its Constant mode through DXF and DWG (#2246) (#2418)
* test(io): the Constant mode of an attached ATTRIB survives DXF and DWG (#2246)

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

* fix(io): an attached ATTRIB keeps its Constant mode through DXF and DWG (#2246)

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

* test(io): attached ATTRIB Constant mode over every save version and encoding

The regression covered the default DXF and DWG saves only. It now also saves
with each Version::ALL entry as ASCII DXF, binary DXF and DWG.

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

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-30 19:16:47 -07:00
assamaralandClaude Fable 5.1 9b7149676b fix(io): MTEXT legacy column groups no longer leak into the entity (#2247) (#2416)
* test(io): MTEXT legacy column groups stay in the AcDbMText subclass (#2247)

`legacy_column_groups_stay_in_the_mtext_subclass` (red: the column width 48 is read as the
entity's ltscale, 12.5; the height count 50 as a column height).

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

* fix(io): MTEXT legacy column groups no longer leak into the entity (#2247)

`common()` read the linetype scale (48) and the other AcDbEntity groups from the whole record,
so the column width 48 of an MTEXT's legacy column groups became its ltscale. They are now read
from the AcDbEntity subclass only. `read_columns` took the height count (the first 50) for a
column height and `column_tags` did not write it; both follow the DXF layout now.

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

* test(engine): the MTEXT entmake column heights carry their count (#2247)

Group 50 after the column type is the height count, then the heights. Thanks to austin62jake for the report and the fix.

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

* test(io): a marker-less MTEXT with an embedded object keeps its own ltscale (#2247)

Case from #2276 (Zaxaerith): without subclass markers, the 48 of an embedded object (101) was read as Common.ltscale.

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

* fix(io): stop the entity's own groups at an embedded object (#2247)

Without subclass markers, an embedded object's 48 was read as Common.ltscale. Suggested by austin62jake; case and test from #2276 (Zaxaerith).

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

* style(io): rustfmt the new MTEXT test

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

* fix(io): keep layer and colour that follow a subclass marker in a list with no AcDbEntity marker

entity_groups ended the common region at the first subclass marker other than
AcDbEntity, so an entmake list such as (0 . "LINE") (100 . "AcDbLine") (8 . "Walls")
was read on layer 0. The region now ends at a marker only when the record has an
AcDbEntity marker (files from R13 on always do); other records are read as a
whole up to an embedded object (101), as before the column change.

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

* fix(io): markers inside an application-defined group do not end the entity's groups

A `102 {...102 }` group before AcDbEntity may hold any code, 100 and 101 included: `entity_groups` stopped there and the layer and colour that followed fell back to defaults. The scan now skips the contents of such groups when looking for the AcDbEntity marker and for the end of its groups. Review of #2416.

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

* fix(io): keep every column height of files written before the height count

CADCraft up to f536bf4 wrote group 76 = n and n group-50 heights, no count.
Reading those with the counted format dropped the first height and rewrote
76 as n - 1. The first 50 is a count only when there are 76 + 1 of them.

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

* fix(io): MTEXT column heights skip application-defined groups; no count for an empty list

A `102 {...}` group inside the MTEXT record may hold a 50 of its own: it
counted as a height and made a legacy record (76 = n, n heights, no count)
look counted, dropping the first height. `read_columns` now filters the
record with `outside_app` (shared with `entity_groups`).

`column_tags` wrote a lone `50 = 0` for a dynamic manual column with no
heights, which read back as one height of 0; the count is omitted for an
empty list, so `heights: []` round trips. ZWCAD 2025 also read that lone
50 as the rotation and zeroed it.

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

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-30 19:16:42 -07:00
midasmatterhorn dc6a0e9ec3 engine: renaming a layer repoints every viewport's frozen list and colour overrides (#1264) 2026-10-30 19:16:38 -07:00
2701983688 Ctrl/Cmd+W closes the drawing, as the File menu shows (#2057)
* Ctrl/Cmd+W closes the drawing, as the File menu shows

File > Close shows Cmd+W (Ctrl+W on Windows and Linux), but the keyboard
table had no entry for it, so the key did nothing outside the macOS native
menu. The table moves into builtin_shortcuts() (the shape #1703 also uses),
and a test checks that every shortcut a command shows runs that command.

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

* Clippy: assign operators in the shortcut test's key parser

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

---------

Co-authored-by: ATARYS-LAB <342661122+ATARYS-LAB@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 19:16:34 -07:00
1ecc2a130b DXF: no CLASS instance counts in R2000 files (AutoCAD LT 2026 hung opening them) (#1060)
* DXF: no CLASS instance counts in R2000 files (AutoCAD LT 2026 hung on them)

Since #635 every CLASS record carries group 91, the instance count, at every
version. The count came with the 2004 format: R2000 CLASS records are 1, 2, 3,
90, 280, 281. AutoCAD LT 2026 stops responding when it opens an R2000 DXF
whose CLASS records have a 91, so every R2000 file CADCraft saved locked it up
(a drawing opened from an R2000 file is saved as R2000 again).

Isolated black-box in AutoCAD LT 2026 with synthetic files: the same R2000
file without the 91 groups opens, as does the same content declared AC1032.

The counts are now written only when the file (or the DWG the bridge writes)
is 2004 or later; R2000 output drops the group from every CLASS record,
classes kept from a newer file included.

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

* Test: binary R2000 drops group 91 from a kept foreign CLASS too

Review follow-up: the kept-class check covered R2000 only through ASCII
saves; it now also saves the fixture as binary R2000 and checks that the
ACME_OBJECT CLASS record is 1, 2, 3, 90, 280, 281.

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

* Test: binary 2018 keeps a kept foreign CLASS's instance count

Review follow-up: the kept-class check now saves the fixture as binary at
both R2000 (no group 91) and 2018 (group 91 after the proxy flags, with the
class's count), through one helper.

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

---------

Co-authored-by: Slopapalooza <307952167+Slopapalooza@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 19:16:30 -07:00
0ab812c12a fix(io): write $PSTYLEMODE as read, in DXF and DWG (#2400)
* test(io): `$PSTYLEMODE` round-trips through DXF and DWG (#2332)

Red test: a drawing read from a DXF with `$PSTYLEMODE` 0 (named plot
styles) is written back without the variable, so readers default to
colour-dependent plot styles.

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

* fix(io): write `$PSTYLEMODE` as read, in DXF and DWG (#2332)

`HEADER_VARS` did not list `$PSTYLEMODE`, so a drawing read from a
named-plot-style file (0) was saved without it; the DWG bridge then
defaulted to colour-dependent plot styles (1) and the plot came out
with thicker edges. One more entry in the list; a drawing that never
had the variable still writes nothing.

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

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: assamaral <217119886+assamaral@users.noreply.github.com>
2026-10-30 19:16:25 -07:00
Tanish Manku 47ed3af26c Parity docs are updated by maintainers in batches, not in each PR (#2520)
docs/gaps.md conflicts in 153 open PRs and docs/ui-parity.md in 75, because every PR had to edit them. A PR now names its rows in the description.
2026-10-30 19:11:01 -07:00
Tanish MankuandAldo Zarate 3d623fc4d4 Saving over a DWG asks before it loses content, and saves keep a .bak (#2518)
Includes the .bak work from #1003.

Co-authored-by: Aldo Zarate <azarate@azf.com.mx>
2026-10-30 19:10:57 -07:00
Tanish Manku 04e74a165c Panic hunt covers every command, and a panic after a command restores the drawing (#2517)
panic_hunt runs every command in a fresh session and fails on real panics. The guard now covers the steps after a command, so a late panic restores the drawing instead of ending the app.
2026-10-30 19:10:52 -07:00
Dylan M. Taylor ba3196e3f7 fix(flatpak): allow reading launcher version pins (#2515)
* fix(flatpak): allow reading launcher version pins

* fix(flatpak): allow reading launcher version pins
2026-10-30 20:12:28 -04:00
assamaralandClaude Fable 5.1 8038de04e4 fix(io): an MTEXT with columns takes its width from the column groups
ZWCAD and AutoCAD write the total width of the columns in the entity's group 41 and the column width in the embedded object's 44 (48 of the legacy groups). `mtext_from` took 41 whenever it was positive, so a three-column MTEXT of 50 came back 175 wide and the R2018 object was written with 44 = 175, 42 = 550. With columns and a positive column width, that width is the MTEXT's (it is one column's throughout). Review of #2467.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-30 17:35:29 -03:00
Zinadin ZidanandClaude Opus 5.5 8d1bd19ee2 Save As suggests the default save format for opened drawings too (#2199) (#2509)
* Save As suggests the default save format for opened drawings too (#2199)

Options > Open and Save > "Save as" in AutoCAD is the format every drawing
is saved in. CADCraft applied File > Default Save Format only to untitled
drawings, so Save As on an opened DWG kept DWG with DXF chosen, and the
other way round. Save As now swaps a DWG, DXF or DWT extension for the
chosen format; other names, and Save of an opened file, are unchanged.

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

* Default Save Format's echo says Save As will suggest the format (#2199)

"Save As will save drawings as DWG" promised more than Save As does: it
only swaps a DWG, DXF or DWT extension in the name it suggests, and the
path picked in the dialog decides the format. The echo now reads
"Save As will suggest DWG", and default_save_format_applies_to_opened_drawings
pins it.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 15:46:34 -04:00