Commit Graph
59 Commits
Author SHA1 Message Date
Valentin KleinandClaude Opus 5.5 bcbbc5e8a4 cadcraft-cli mcp: --connect without HOST:PORT is an error, not a headless session (fixes #103) (#167)
* cadcraft-cli mcp: --connect without HOST:PORT is an error, not a headless session (#103)

`mcp --connect` with no address (or an empty or flag-like one) fell through
to the headless backend, so a client meant for the app got successful edits
in a separate drawing. The mcp arguments now go through parse_mcp_args:
--connect must be followed by a non-empty value that doesn't start with `-`,
unknown arguments and a repeated --connect are rejected, and every error
exits non-zero before any MCP request is served. Plain `mcp` is still
headless; a real address still connects, and an unreachable one still fails.

- Unit tests for the parser; integration tests (tests/mcp_connect.rs) run the
  binary: missing/empty/flag-like address, headless control, loopback
  endpoint routing, closed port.
- docs/mcp.md says what happens without an address.

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

* Move tests away from other PRs' hunks to avoid merge conflicts

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 12:15:50 -04:00
Valentin KleinandClaude Opus 5.5 6c145ed733 Menu items open their missing dialogs; COUNT shows its result; New Layout asks for a name (#126, part 1) (#180)
* Menu items open their missing dialogs; COUNT shows its result; New Layout asks for a name (#126)

Eight menu items ran their JSON form with no parameters and changed nothing.
Typed or chosen from a menu they now do what the menu promises; JSON calls
still run the command and never open a dialog.

- New crates/ui-egui/src/managers.rs: Linetype Manager (load from the
  library, set current, global scale, pattern preview), Point Style
  (PDMODE tiles drawn in code, size relative/absolute), Table Style and
  Multileader Style (list, set current, new, edit properties), Page Setup
  Manager (per layout: paper, orientation, plot area, fit/scale, center,
  lineweights) and Constraint Settings (infer, bars, transparency, dynamic
  constraints, AutoConstrain types and tolerances). All changes go through
  their commands (app.run), so they are undoable and scriptable.
- menus::run_ui_command routes LINETYPE/LT, DDPTYPE, TABLESTYLE/TS,
  MLEADERSTYLE/MLS, PAGESETUP and CONSTRAINTSETTINGS/CSETTINGS without
  parameters to the dialogs; dialogs::show dispatches them.
- COUNT returns a `message` (per-block counts and total) that the command
  line shows; Session::start echoes multi-line messages line by line.
- LAYOUT.NEW from the menu or command line prompts
  "Enter name of new layout <LayoutN>:"; Enter takes the default, a taken
  or invalid name is reported and asked again.

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

* Move COUNT test away from other PRs' hunks to avoid merge conflicts

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 12:15:42 -04:00
Valentin KleinandClaude Opus 5.5 8d71f7de4a Properties palette: every control acts or is visibly unavailable (#182)
* Properties palette: every control acts or is visibly unavailable (#6)

Several Properties palette controls looked clickable but did nothing: most
current-property rows with nothing selected (linetype, lineweight, thickness,
text/dimension/multileader/table style), the selection-type drop-down, a
circle's circumference and area, a line's length and angle, a dimension's
style, and the All/My switch.

- Nothing selected: Linetype runs `linetype {current}`, Lineweight `lweight`,
  Thickness `setvar THICKNESS`, the style rows `style.current`,
  `dimstyle.current`, `mleaderstyle` and `tablestyle`. Transparency,
  Annotation scale and Plot style(s) have no command yet: greyed with a
  "Not available yet" tooltip.
- Header: the type drop-down narrows the selection to one object type
  (`select {handles}`), plus Toggle PICKADD (`setvar PICKADD`), Select Objects
  (`select`) and Quick Select buttons, with icons drawn in code.
- Geometry: circle circumference/area, line length/angle and dimension style
  are editable through `properties.set` (new keys `circumference`, `area`,
  `dimStyle`); computed values (line deltas, polyline length/area, block
  name) are greyed read-only.
- PICKADD now changes clicking with no command running: off, each pick
  replaces the selection and Shift adds.
- Palette text fields keep typed text while the window itself is unfocused
  (they read focus from egui memory, not `Response::has_focus`).

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

* Move palette tests away from other PRs' hunks to avoid merge conflicts

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 12:15:34 -04:00
Valentin KleinandClaude Opus 5.5 306ba991a5 Selection commands prompt "Select objects" without a pickfirst selection (#126) (#176)
Edit > Cut / Copy / Copy with Base Point, View > Zoom > Object, Dimension >
Update, Tools > Draw Order > Bring to Front / Send to Back and the Format >
Layer Tools LAYCUR, LAYFRZ, LAYOFF, LAYLCK and LAYULK did nothing when started
from a menu, toolbar or the command line with nothing selected.

- machines::SelectRun: "Select objects:", then run the command's JSON body on
  the picked handles. With a pickfirst selection it runs at once, as before.
  It replaces the DIMREASSOCIATE/DIMDISASSOCIATE SelectThen in annotate.rs and
  calls the command body directly, so the command records one undo step.
- LAYFRZ/LAYOFF/LAYLCK/LAYULK pick one object ("Select an object on the layer
  to be frozen" etc.).
- COPYBASE asks for the base point first, then for objects (skipped with a
  pickfirst selection).
- Objects picked at the prompt are deselected afterwards.
- JSON/programmatic calls are unchanged and never prompt.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 12:15:26 -04:00
Valentin KleinandClaude Opus 5.5 7666c1cd68 LINE: deferred tangent/perpendicular for the first point (fixes #69) (#168)
TAN and PER snaps need the other end of the line, so the first point of a
LINE (no base point yet) could never snap tangent: a belt line tangent to
two circles was impossible to draw.

Now, as in AutoCAD, a point prompt can accept *deferred* snaps:

- snap: with no base and the new `deferred` flag, osnap offers "Deferred
  Tangent" (circles, arcs) and "Deferred Perpendicular" (circles, arcs,
  lines, xlines, rays) where no other snap wins. The hit carries the curve
  (`SnapHit::deferred`, `Deferred`, `SnapCurve`).
- `resolve_deferred` solves the line once the other end is known: tangent
  or perpendicular from a point, tangent-to-tangent (common tangents of two
  circles, new `Circle::common_tangents` in geom) and the perpendicular
  mixes. Among several solutions the one nearest the picks wins. No
  solution (point inside the circle, concentric or nested circles,
  non-finite input) is `None`, never a panic.
- prompt: `Prompt::deferred()` opts a prompt in; `Input::Deferred` reaches
  only such prompts, everywhere else the session turns it into the point it
  was picked at, so other commands snap as before.
- LINE opts in for its first point (and the next one while the first is
  still deferred). No solution reports and re-prompts; Undo drops the
  deferred point. The rubber band shows the resolved line, also the
  tangent-to-tangent one while hovering the second curve
  (`Session::cursor_deferred`, set by the canvas).
- canvas: passes the prompt's flag to osnap and sends a deferred pick as
  `Input::Deferred`.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 12:15:18 -04:00
Valentin KleinandClaude Opus 5.5 4d61f57eec HATCH/GRADIENT/BOUNDARY: islands made of separate lines (fixes #106) (#163)
Point picks found the outer boundary by tracing the planar graph of all
curves, but islands only came from single closed objects (circles, closed
polylines, full ellipses, closed splines). A square drawn as four LINEs
inside a rectangle was ignored: HATCH drew through it, GRADIENT filled it
and BOUNDARY created only the outer polyline.

- geom::region::boundary_at returns the outer loop plus islands: every
  connected group of curves inside the face that doesn't touch its
  boundary and doesn't surround the pick point. Each island is the
  outline of the group's outside, traced with the same sharpest-turn
  walk from its leftmost vertex after dangling ends are pruned. Islands
  report which input curves they came from. Traces are bounded by the
  group's edge count and islands are capped at MAX_ISLANDS.
- loops_at (shared by HATCH, GRADIENT and BOUNDARY) adds those islands
  when every curve in the group is an open LINE, ARC, elliptical arc,
  open polyline or open spline. Groups containing a closed object keep
  the existing per-object islands, so closed-polyline and circle islands
  are unchanged (bulges kept).
- Tests: geom region tests for separate-line islands, dangling ends,
  open and attached curves, nesting and hostile input; engine tests for
  HATCH (rendered pattern stays out of the hole), GRADIENT (375 filled,
  hole clear) and BOUNDARY (two polylines) with four-line and
  closed-polyline islands.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 12:15:09 -04:00
Valentin KleinandClaude Opus 5.5 61875b1f0c DWG open: stop runaway conversions early and name the limit (#57) (#175)
* DWG open: stop runaway conversions early, name the limit (#57)

"file too large" was the DXF reader's 50,000,000 group-code cap (MAX_TAGS),
not a file-size limit. For a DWG it was only checked at the very end: after
acadrust had read the whole file, written all of its DXF text into one Vec,
the text had been copied again for decoding, and 50 million tags had been
parsed. A real 10 MB DWG is nowhere near the cap (a synthetic 10 MB AC1024
file is 3.7 million group codes and opens in about 2 s). A damaged file, or
objects the DWG reader misreads, can be: acadrust caps every count at 100,000
and reads zeros past the end of the data, so one desynchronised object becomes
a polyline with 100,000 vertices. A fuzzed 0.25 MB AC1024 file made it write
11 GB of DXF.

- cadcraft-dwg: Limits { max_dwg_bytes: 512 MB, max_dxf_bytes: 1 GB }. The DWG
  size is checked before the reader runs; the DXF is streamed into a writer
  that fails as soon as it passes the limit, so the conversion stops there
  instead of filling memory. Both errors give actual and allowed sizes. The
  input is no longer copied, and the DXF writer also runs under catch_unwind.
- cadcraft-dxf: TooLarge { tags, limit } says "drawing too large: N DXF group
  codes, more than the 50000000 CADCraft reads". For ASCII the count comes
  from the line count before any decoding or tag allocation. Valid UTF-8 text
  is borrowed instead of copied. MAX_TAGS is public and unchanged.
- cadcraft-io: errors from the DXF converted from a DWG say so.

On the fuzzed file: before, 76 s and 13.4 GB, then the process aborted copying
the 11 GB text (under a 16 GB memory limit). After, 15 s and 5.3 GB, with
"DWG: converting this 0.2 MB file produced more than 1024 MB of drawing data
(the limit); ...". What's left is acadrust's reader itself (8 s, 5.3 GB on that
file), which this bridge can't bound without patching the crate.

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

* Move tests away from other PRs' hunks to avoid merge conflicts

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 12:15:01 -04:00
Valentin KleinandClaude Opus 5.5 7497618f76 fix(engine): programmatic undo/redo ends a running command first (#178)
* Programmatic undo/redo ends a running command first (#177)

`undo` or `redo` run through engine.execute, MCP, the control channel or a
script while an interactive command was still running (e.g. TEXT waiting for
another line) swapped the drawing under that command. Interactive commands
push their undo step only when they end, so `undo` popped the previous
command's step instead: it removed the earlier geometry, dropped the running
command's entities, and left the command running from a stale state.

Both commands now first end the running command the way Esc does, exactly as
Session::start already does for Cmd+Z, the menu and the command line. The
command's work so far becomes one undo step, and `undo` then removes that step.

Regression test: programmatic_undo_during_a_command_ends_it_first.

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

* Move test away from other PRs' hunks to avoid merge conflicts

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 12:14:53 -04:00
Valentin KleinandClaude Opus 5.5 bacb951059 DXF: write non-ASCII text as \U+ escapes so R2000 files read correctly elsewhere (fixes #164) (#169)
* DXF: write non-ASCII text as \U+ escapes so AC1015 files stay valid (#164)

The writer declares $ACADVER AC1015 (R2000) and $DWGCODEPAGE ANSI_1252, where
text is in the code page, but wrote strings as raw UTF-8. Every other reader
decoded those bytes as CP1252 and turned non-ASCII text into mojibake; only
CADCraft (which tries UTF-8 first) read its own files back correctly.

- cadcraft_dxf::write_ascii now writes pure ASCII: every non-ASCII character
  in a string group becomes the pre-R2007 \U+XXXX escape (UTF-16 code units,
  so characters above U+FFFF are a surrogate pair of escapes). This covers
  TEXT, MTEXT, ATTRIB/ATTDEF, table, block and layer names, xdata and
  XRECORD strings alike. The header and the R2000 structure are unchanged,
  so the DWG bridge (acadrust, which decodes the escapes) behaves as before.
- The reader combines surrogate-pair escapes, requires four hex digits, keeps
  malformed escapes and unpaired surrogates as literal text, and now also
  unescapes strings in binary DXF.
- Tests: R2000 save/reopen of CJK, Latin-1 and astral text in TEXT, MTEXT, a
  layer name, a block name and ATTRIB/ATTDEF; pure-ASCII output with the
  AC1015/ANSI_1252 header; UTF-8 (AC1032) and code-page (AC1015) input still
  read; DWG round trip; hostile escapes.

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

* Move code and tests away from other PRs' hunks to avoid merge conflicts

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 12:14:45 -04:00
Valentin KleinandClaude Opus 5.5 3fe93806b7 Entity transparency: set it, report it, round-trip DXF group 440 (#174)
properties.set documented `transparency` but never read it, nothing reported it,
and DXF dropped group 440. Fixes #132.

- properties.set takes "ByLayer", "ByBlock" or a percentage 0..90 (a number or
  "50"/"50%"); anything else (out of range, NaN, junk) is an error and leaves
  the selection unchanged.
- properties and drawing.inspect / drawing.entities report it in the same form.
- Properties palette: the Transparency row is a menu (ByLayer, ByBlock, a
  0..90 percentage) that acts through properties.set.
- DXF: entities write 440 unless ByLayer (ByBlock 0x01000000, a percentage its
  alpha | 0x02000000, the alpha mapping layers use); the reader parses it back,
  any other value reads as ByLayer.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 12:14:37 -04:00
kaffeochknivarandClaude Opus 5.5 0cdde077ef DXF: keep layer transparency and description through save and reopen (fixes #96, fixes #97) (#131)
* Render tests: drop the always-true assert in degenerate_input

`assert!(... || true)` trips clippy's overly_complex_bool_expr on current
stable (1.96), which fails `cargo xtask ci` at the clippy step. The check
only proves that NaN input does not panic, so call the function and
discard the result instead.

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

* DXF: keep layer transparency and description through save and reopen (#96, #97)

The LAYER writer omitted both properties and the reader left them at
their defaults, so a drawing reopened with every layer opaque and every
description empty. Both are xdata on the LAYER record, as AutoCAD writes
them:

- transparency: AcCmTransparency, 1071 = 0x02000000 | alpha, with
  alpha = (100 - percent) * 255 / 100 (40 % is alpha 153); values that
  aren't "by alpha" read as opaque
- description: AcAecLayerStandard, two 1000 strings (the layer
  standard's name, unused, then the description), cut to the 255 bytes
  a 1000 group may hold

Both applications are registered in the APPID table.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 12:14:29 -04:00
MarekSpodBiedry b7f83a6687 Keep viewport frozen-layer lists through DXF save and reopen (#98) (#150) 2026-10-10 12:14:21 -04:00
MarekSpodBiedry 576e6f9020 Write LWPOLYLINE elevation (group 38) to DXF (#77) (#142) 2026-10-10 12:14:13 -04:00
MarekSpodBiedry 56c39f1c17 Keep ATTDEF prompt when saving DXF (#81) (#137) 2026-10-10 12:14:06 -04:00
MarekSpodBiedry df1c074fff Keep an ordinate dimension's datum fixed when its feature moves (#105) (#157) 2026-10-10 12:14:00 -04:00
MarekSpodBiedry 947535158a Store canonical autoTypes names so accepted spellings take effect (#95) (#156) 2026-10-10 12:13:53 -04:00
MarekSpodBiedry 0af1528f4c AREA of a normal hatch no longer counts unfilled islands (#91) (#155) 2026-10-10 12:13:47 -04:00
MarekSpodBiedry 9882b85a41 Reject nonpositive MTEXT height on creation (#84) (#154) 2026-10-10 12:13:39 -04:00
MarekSpodBiedry bd46065c27 HATCHEDIT: clear the gradient when a named pattern is chosen (#107) (#151) 2026-10-10 12:13:31 -04:00
MarekSpodBiedry 52f05f1514 Fence selection now includes crossed XLINE and RAY objects (#92) (#149) 2026-10-10 12:13:24 -04:00
MarekSpodBiedry 74cbdf7e2f Keep selected objects outside the joined chain when using JOIN (#86) (#148) 2026-10-10 12:13:16 -04:00
MarekSpodBiedry a983cf1ed0 MCP bridge: do not resend a mutation whose reply was lost (#102) (#146) 2026-10-10 12:13:06 -04:00
MarekSpodBiedry 24f074a9ad Reject deleting a dimension that a remaining dimension refers to (#94) (#145) 2026-10-10 12:12:58 -04:00
MarekSpodBiedry fabc5bacd9 Fix justified TEXT extents and picking (#89) (#144) 2026-10-10 12:12:50 -04:00
MarekSpodBiedry 279c1bed6a Convert typed ellipse arc angles to ellipse parameters (#82) (#143) 2026-10-10 12:12:41 -04:00
MarekSpodBiedry 3ef52e31d8 Apply DIMOVERRIDE text together with dimension variables (#108) (#141) 2026-10-10 12:12:33 -04:00
MarekSpodBiedry 50b9a23a98 Reject out-of-domain values in PARAMETERS instead of dropping the constraint (#93) (#139) 2026-10-10 12:12:23 -04:00
MarekSpodBiedry fadbea5dfe Keep elevations when EXPLODE splits a 3D polyline (#88) (#138) 2026-10-10 12:12:16 -04:00
kaffeochknivarandClaude Opus 5.5 da8894dd08 Render tests: drop the always-true assert in degenerate_input (#37)
`assert!(... || true)` trips clippy's overly_complex_bool_expr on current
stable (1.96), which fails `cargo xtask ci` at the clippy step. The check
only proves that NaN input does not panic, so call the function and
discard the result instead.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 12:12:08 -04:00
Brandon ThomasandClaude Opus 5.5 43e33add44 Bump version to 0.4.0 (from 0.3.0) for release; refresh contributors
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
v0.4.0
2026-10-09 21:01:59 -04:00
Brandon Thomas 86f3199b40 Merge pull request #13 from XusBadia/dmg-background
macOS DMG: branded window background and icon layout
2026-10-09 09:21:09 -04:00
Xus BadiaandClaude Opus 5.5 15c35620c4 macOS DMG: branded window background and icon layout
The DMG opened as a plain Finder window. It now shows the app icon's figure on
the CADCraft colour, with CADCraft.app and the Applications link side by side.
Same approach as PhotoCraft's and VectorCraft's (storytold/photocraft#1201,
storytold/vectorcraft#493).

- packaging/macos/dmg/: background.svg (source; text outlined, no paper grain,
  the figure an <image> of assets/app-icon), background.tiff (1x + 2x HiDPI,
  16-colour palette, Deflate, sRGB; ~21 KB)
  and dmg-layout.DS_Store (Finder view settings: window, icon size 128,
  positions, background).
- generate.py writes the TIFF and the layout on any OS, byte-for-byte
  reproducible, with nothing from the machine that ran it (no paths, users,
  disk names or volume UUIDs).
- package.sh copies them into the staging folder; the image is still built with
  hdiutil makehybrid. Nothing extra runs in the signing job.
- The volume name drops the version: the layout finds the background through
  an alias that includes the volume name. The DMG file name keeps the version.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 07:43:54 +02:00
Brandon ThomasandClaude Opus 5.5 59631c8d4f Bump version to 0.3.0 (from 0.2.0) for release
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
v0.3.0
2026-10-08 10:02:20 -04:00
ab154a5552 README Downloads: where to get it first, then builds with Windows before macOS (#19)
Co-authored-by: Brandon Thomas <shared2@storyteller.ai>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 09:50:35 -04:00
7c7c1f9a3d Release: Flatpak x86_64/aarch64, AppImage zsync updates, README Downloads (#16)
* Release: Flatpak x86_64/aarch64, AppImage zsync updates, README Downloads

- AppImages embed gh-releases-zsync update information and ship a .zsync beside them.
- New flatpak job repackages each Linux tarball into cadcraft-<ver>-linux-<arch>.flatpak
  (bundle manifest + flatpak-bundle.sh); the from-source Flathub manifest moves to runtime
  26.08 with the same commented, minimal finish-args (packaging-lint checks they agree).
- Jobs that sign nothing (version, linux, flatpak, freebsd, web) leave the release
  environment, so a branch dispatch is a dry run; the draft-release job runs only on release.
- README: Downloads section listing every build.

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

* Release: check AppImage update info without extract-and-run; fix the deb/rpm description

APPIMAGE_EXTRACT_AND_RUN makes the runtime pass --appimage-updateinformation to the app.
The package description was PhotoCraft's.

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

---------

Co-authored-by: Brandon Thomas <shared2@storyteller.ai>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 09:40:49 -04:00
Brandon ThomasandClaude Opus 5.5 834fc19b4c Bump version to 0.2.0 (from 0.1.0) for release
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 08:13:20 -04:00
Brandon ThomasandClaude Opus 5.5 1d7ea8749f Release CI: macOS job on macos-15-xlarge
Custom macOS larger runners in a runner group do not pick up jobs; the
built-in M2 Pro label does. Only release.yml uses it, and the
actions_macos_xl budget caps the monthly spend.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
v0.1.0
2026-10-07 22:07:52 -04:00
Brandon ThomasandClaude Opus 5.5 e1f6b48bfe Release CI: macOS job on the org macos-release runner
The standard macOS runners are shared with all CI and queue for an hour or
more. The macos-release runner (GitHub-hosted M2 Pro) is in a runner group
usable only by release.yml on the release branch, with a monthly spend cap
on the actions_macos_xl SKU.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 21:54:50 -04:00
Brandon ThomasandClaude Opus 5.5 ef1b55dca6 About: contributor and model credits compiled in (grab bag + table, self-submitted names) (#3)
* About: contributors compiled in from contributors.json (grab bag + table, sortable, name toggle)

Replaces the TSV files and `cargo xtask contributors` with the craftrules standard
(standards/contributors.md): contributors/contributors.json is generated by
craftrules/scripts/contributors.py and baked into the binary by build.rs as static tables.
Each contributor line: @username, opt-in display/real name, PRs, commits, lines +/−/Δ,
binary assets +/−, first/last commit. Sortable by every column; alphabetical ignores
case and the leading @.

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

* Contributors docs: craftrules is two levels up (../../craftrules)

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

* Contributors: self-submitted names in contributors/people.toml, read by build.rs

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

* Contributors: regenerate (people_entry verified)

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

* AGENTS.md: contributor credits section (agents offer to credit their own human only); docs

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 21:16:30 -04:00
Brandon ThomasandClaude Opus 5.5 14e143bfdd Rename PrintCraft to PdfCraft (storytold/pdfcraft) (#2)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:45:23 -04:00
Brandon ThomasandClaude Opus 5.5 7d4ca2830d Release CI: builds only from the release branch; FreeBSD release artifact
- version job guarded to refs/heads/release, so a dispatch from any other ref builds nothing
- FreeBSD x86_64 tarball built in a FreeBSD VM as part of the release run
- remove the FreeBSD build that ran on pushes to main
- fix two empty env keys that made the workflow invalid

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 02:15:06 -04:00
Brandon ThomasandClaude Opus 5.5 c582a9ea97 Roadmap: M3 and constraint display landed
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 02:13:13 -04:00
Brandon Thomas 1c86787459 Merge branch 'worktree-agent-a2f28e507557a5b11' 2026-10-06 02:12:34 -04:00
Brandon ThomasandClaude Opus 5.5 a599137356 README screenshots, alpha/parity assessment in ROADMAP, MLEADER written to DXF as LEADER + MTEXT
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 02:12:34 -04:00
Brandon ThomasandClaude Opus 5.5 9646f20b73 Layer Properties Manager, QSELECT + Quick Properties, constraint display, Parameters Manager
- Layer Properties Manager: filter tree (All / All Used Layers) with search, sortable table
  (Status, Name, On, Freeze, Lock, Plot, Color, Linetype, Lineweight, Transparency,
  New VP Freeze, Description; VP Freeze / VP Color inside an MSPACE viewport), inline
  rename and description, ACI + true-colour picker, linetype picker with Load from the
  library, lineweight picker, layer states save/restore/delete.
- Engine: qselect rewritten ({type, property, operator =,!=,>,<,*, value, mode new|append|
  exclude, applyTo}) + qselect.info; layerstate.list/delete/rename; layer list reports
  used layers and states; Layer.vp_freeze_new (DXF flag 2, applied by MVIEW) and
  layer.set newVpFreeze; per-viewport layer colours (Viewport.layer_colors, vplayer
  colors, honoured by the renderer); vplayer defaults to the active viewport; QPMODE
  setting/sysvar/toggle; properties.set length/angle for lines.
- Canvas: constraint bars with code-drawn glyphs and coincident markers, dynamic
  dimensional constraints (aligned/horizontal/vertical/radius/diameter/angular) with
  name=value labels and lock icon, honouring CONSTRAINTBAR/DCDISPLAY; cached per
  drawing revision; double-click a label to edit its expression (parameters).
- Quick Properties palette near the selection (status-bar toggle), Quick Select dialog,
  Parameters Manager (edit, add user parameter, delete).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 02:11:43 -04:00
Brandon Thomas 12d5f5d967 Merge branch 'worktree-agent-a8983b474d3226b17' 2026-10-06 02:06:13 -04:00
Brandon ThomasandClaude Opus 5.5 d2ce7e6fef DXF round trip: DIMSTYLE, DSTYLE overrides, DIMASSOC, constraints, tables, text style flags
- DIMSTYLE: every DimStyle field with its standard group code (DIMBLK/1/2 as
  BLOCK_RECORD handles 342/343/344 with DIMSAH, DIMTXSTY 340, DIMDSEP, DIMCLRx, ...);
  arrowhead blocks generated from our own geometry when no user block exists, and
  not re-imported as user blocks on read.
- Per-dimension overrides as ACAD "DSTYLE" xdata (1070 code + typed value).
- Associativity: standard ACAD_DIMASSOC extension dictionary + DIMASSOC objects
  (AcDbOsnapPointRef) for linear/aligned/ordinate extension origins, plus exact
  links in CADCRAFT xdata; reader infers links from DIMASSOC for foreign files.
- Constraints/parameters: JSON in an XRECORD under the named-object-dictionary
  key CADCRAFT_CONSTRAINTS (250-char chunks, backslashes escaped so no \U+).
- ACAD_TABLE write/read (sizes, cell text, merges) with *T display block,
  TABLESTYLE objects and CADCRAFT xdata for title/header/text height/style.
- STYLE: vertical (70), backwards/upside-down (71), AcadAnnotative xdata.
- Reader: owner 330 taken outside 102 groups; hostile-input caps and tests.
- Validated: ezdxf strict readfile + audit (0 errors/0 fixes) and resave;
  DXF->DWG->DXF via acadrust keeps all of it except TABLESTYLE objects.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 02:05:03 -04:00
Brandon ThomasandClaude Opus 5.5 7c8404794a cadcraft-cli sample subcommand; apartment example drawing
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 01:39:02 -04:00
Brandon ThomasandClaude Opus 5.5 5336475036 Ignore agent worktrees
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 01:36:23 -04:00
Brandon ThomasandClaude Opus 5.5 2287134dbd Incremental R-tree: patch only changed store chunks after an edit
A pick after one edit at 200k entities drops from ~110 ms (full rebuild) to ~4 ms;
steady-state picks stay at ~1 us. Adds a parity test against fresh builds and a
perf row for the incremental path.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 01:36:17 -04:00