Commit Graph
2228 Commits
Author SHA1 Message Date
Karim Jerbi e15d801011 fix(tess): clamp isoline counts before they reach the kernel
Bug:
$ISOLINES (header i16) and Surface.u/v_isolines (file-controlled i16)
were only ever floored at 0, never capped, and were handed to the
tessellation kernel as the per-face isolate count - which draws
0..count isolines per face with no ceiling of its own. SETVAR ISOLINES
32767 (or a DWG carrying that header, or a Surface with u_isolines =
32767) turned every rebuild and every per-cursor preview of
PRESSPULL/REVOLVE/SWEEP/LOFT into a multi-million-vertex computation:
measured 4,259,966 edge vertices for one 12-unit cylinder preview.

Reproduce:
1. SETVAR ISOLINES 32767 (or open a DWG whose $ISOLINES is large, or
   edit a Surface whose u_isolines is 32767).
2. Draw a circle and start REVOLVE (or PRESSPULL/SWEEP/LOFT), or shade
   any 3D solid.
3. Before: the app allocates without bound - abort on memory failure or
   a permanent freeze; previews recompute on every mouse move.
4. After: previews stay interactive; a hostile header renders exactly
   like the largest value the Options slider can request.

Fix:
Cap each value at the ceiling this product already agrees on, where the
type changes from i16 to the kernel count: header $ISOLINES <= 64 (the
Options slider's own "Isolines per surface" range), Surface
u/v_isolines <= 200 (the plugin editor's writable range). One helper
(clamp_header_isolines) replaces all 11 copies of .max(0) as usize
across the entity/scene/control/display feed sites,
surface_isoline_counts clamps both axes, and SETVAR ISOLINES stores
x.clamp(0, 64) so the echo, slider and re-saved files agree with what
renders; file-sourced headers keep their raw stored value and are
clamped at consumption. Clamping is density-only: geometry, bounds and
saves are untouched. Three regression tests cover hostile surface
counts, the SETVAR entry, and a file-sourced header measured through
display tessellation (hostile renders like the slider ceiling, negative
renders like zero).
2026-09-30 19:15:49 +01:00
Karim Jerbi 438a46b80f fix(serve): drop idle socket clients so the accept loop keeps serving
Bug:
--serve in socket mode serves one client at a time: the accept loop
blocks in the per-client line read before it reaches the next
connection, and this path never called set_read_timeout - unlike
rest.rs, http_bridge.rs and control::transport, which all use 15
seconds. A client that connects and sends nothing held the read open
forever, wedging the port: every later client hung waiting for the
ready banner (the banner is written before the read loop, so the server
looked healthy while it was stuck).

Reproduce:
1. Start OpenCADStudio --serve --port 8765.
2. From any shell, connect and send nothing:
   python -c "import socket,time;socket.create_connection(('127.0.0.1',8765));time.sleep(3600)"
3. From a second shell, connect and wait for the banner.
4. Before: the second client hangs forever; the port never serves
   another connection.
5. After: the idle first client is disconnected 15 seconds after its
   last read, and the second client receives the ready banner.

Fix:
The one timeout this transport was missing: SERVE_IDLE_TIMEOUT = 15 s
(same value as the other transports), set on each accepted socket via
serve_socket_with_idle - a seam that also lets the regression test run
with 300 ms. A timed-out read surfaces as an error from lines_capped,
which the loop's existing let Ok(line) ... else break already turns
into "drop this client and accept the next one"; a partially read line
is discarded, which is correct because it was never dispatched. The
timeout only runs while a read is pending, so long server-side
operations do not spend the idle budget, and the document session
survives the reconnect. Regression test: an idle client must see EOF
and the following client must still get its banner.
2026-09-30 19:15:49 +01:00
Karim Jerbi a0308dfa88 fix(data-link): clamp cell-range column window to the data width
Bug:
crop_range bounded the row axis with skip/take over the file's real rows
but left the column axis unbounded: c0..=c1 collects one String per
column, and a range like A1:ZZZZZZ1 parses to roughly 308 million
columns, causing a capacity overflow while allocating a row.

Reproduce:
1. Data Link -> pick any CSV/xlsx, set Cell Range to A1:ZZZZZZ1 (or any
   range whose parsed column index is enormous).
2. Before: "capacity overflow" abort while building the preview.
3. After: the window is cropped to the file's widest row; columns past
   the data yield nothing beyond the data width.

Fix:
c1 is clamped to widest.saturating_sub(1) before the range is collected,
so the span is provably at most the file's own data - allocation is
O(file). Rows outside the file still come back empty (in-range blank
padding inside the data width is preserved), and c0 > c1 naturally
yields an empty window. Regression tests cover the 13-Z capacity-overflow
input and a 6-Z range that now crops.
2026-09-30 19:15:49 +01:00
Karim Jerbi c86746726b fix(config): parse colour hex digits as chars, not byte offsets
Bug:
parse_hex validated the colour string by byte length and then sliced at
fixed byte offsets. A multi-byte UTF-8 character inside the field makes
the byte offsets land mid-character, so committing the Options colour
keystroke field (or set_hex) panics at the char-boundary slice.

Reproduce:
1. Options -> any colour field; enter a value that passes the byte
   length check but contains a multi-byte character.
2. Commit the field.
3. Before: panic at the slice.
4. After: the value parses as far as valid hex digits allow or is
   rejected as invalid input - no panic.

Fix:
The parser walks chars, consuming exactly two hex digits per channel via
to_digit(16) and requiring the exact total length - byte offsets no
longer exist in the function. Covers all five colour fields and set_hex.
A regression test covers multi-byte inputs.
2026-09-30 19:15:49 +01:00
Karim Jerbi ee188a6e3c fix(control): reject unknown entities_transform actions instead of panicking
Bug:
control_entities_transform validated the action field only inside the
transform closure - and invoked that closure from three of the match's
arms. Any other action fell into unreachable!("validated by
transform()") and aborted the process. The claim in the panic message
was false: for unknown actions the validating code never ran. The handler
is reachable from every remote surface (REST, MCP, --serve) because they
share this dispatch. Two extra triggers: "move " (trailing space, no
trim) and an all-whitespace action.

Reproduce:
1. Start OpenCADStudio with --http 8765.
2. POST /api/v1/entities (a LINE), take handles[0].
3. POST /api/v1/entities/transform with
   {"handles":["<handle>"],"action":"explode"}.
4. Before: "internal error: entered unreachable code" and the process
   exits.
5. After: {"ok":false,"code":"invalid_action",...} and the server keeps
   serving. "action":"move " is now accepted (trimmed); "   " is
   rejected.

Fix:
The action is trimmed, lowercased and checked against the six-value
whitelist after require_existing (error precedence unchanged: a bad
handle still reports first) but before push_undo_snapshot, so an invalid
request no longer dirties the undo stack. The match's fallback arm
returns the same invalid_action error instead of unreachable!, so a
future mismatch between the whitelist and the match fails closed.
2026-09-30 19:15:49 +01:00
Karim Jerbi be8a7b95c4 fix(io): cap line reads on the serve and MCP transports
Bug:
The --serve automation loop (stdin/socket) and the MCP stdio loop read
input with unbounded read_line/lines. A single line over 64 MiB - a
huge paste or a hostile client - grows the buffer until the allocator
aborts, the same class of failure as the HTTP path, on the non-HTTP
transports.

Reproduce:
1. Start --serve on a socket (or the MCP server on stdio).
2. Send one 65 MiB line with no terminating newline anywhere in it.
3. Before: RSS grows to the full line, then "memory allocation failed"
   (or a multi-second stall while the line is copied).
4. After: the read fails at the cap and the existing EOF arm closes the
   connection/stdio promptly.

Fix:
New shared src/io/line_read.rs module: lines_capped(reader, 64 MiB)
yields Err for an over-cap line, which the serve and MCP loops already
treat as EOF/close. rest.rs was refactored onto the same helpers so the
cap logic lives in one place. A regression test streams a 65 MiB line
at the serve socket and asserts the connection ends without a response.
2026-09-30 19:15:49 +01:00
Karim Jerbi 3345b6453e fix(background): keep the app alive when a background worker panics
Bug:
background_task joined the worker thread with .expect(...). Any panic
inside a background closure (decoding a malformed file, an unexpected
None) was re-raised on the main thread and took the whole application
down, even though the worker's failure was already contained.

Reproduce:
1. Run an operation that executes in a background worker, e.g. loading
   or converting a deliberately corrupted file whose decoder panics.
2. Before: the process dies with the worker's panic message.
3. After: the command line shows "background task failed: <label> -
   <panic>" and the application keeps running.

Fix:
The worker closure is wrapped in catch_unwind; a panic is converted to
Message::BackgroundTaskFailed(context, message) delivered back to the
update loop and surfaced as a command-line error. Each call site passes
a context label naming the operation. A failed thread join (thread
aborted) is handled the same way instead of expect().
2026-09-30 19:15:49 +01:00
Karim Jerbi 2d6f4fcfdf fix(tabs): close by stable document id instead of vector index
Bug:
Message::TabClose carried a vector index captured when the view message
was built. Closing several tabs in one batch (Close All / Close Others
with unsaved-changes dialogs, or a queue of continuation updates) let an
earlier close shift the vector before a later TabClose(idx) was handled:
self.tabs.remove(idx) either panicked (index past the end) or closed a
different tab than the one the user confirmed.

Reproduce:
1. Open several drawings and modify each so they all prompt on close.
2. Use Close All, or close two tabs in quick succession so both messages
   are handled in one batch.
3. Before: panic in remove(idx), or the wrong tab closes while the tab
   the user chose stays open (or its work is lost).
4. After: the chosen tab closes; an id already closed by an earlier
   message is a no-op.

Fix:
TabClose now carries the tab's stable u64 id, resolved to a vector
position when the message is handled, not when it is built;
PendingClose::Tab stores ids the same way. All construction sites pass
tab.id (a raw usize index no longer type-checks), and
DocTabCloseAll/DocTabCloseOthers build their queues from live ids at
handling time. A missing id means the tab is already gone, so the
handler returns Task::none().
2026-09-30 19:15:49 +01:00
Karim Jerbi 6951cc2a7a feat(mcp): declarative op table and per-variant request validation
- Define Ty, Param, Rule, and OpDef in src/mcp_ops.rs for all 39 mutations
- Generate Draft 2020-12 schema using per-op anyOf variants with op enum discriminators
- Disambiguate colliding properties (replace, center, name) across mutation variants
- Add pre-dispatch validator enforcing required fields, rules, and parameter types
- Emit unknown parameter warnings into response warnings
- Introduce target_document_id for entities_copy_to with deprecated document_id fallback
- Update unit tests and automation smoke assertions
2026-09-30 16:54:45 +01:00
ramox81andClaude Opus 5.5 15c0da8b79 Point clouds: measured lighting, highlighted extents box, gizmo shaft grab
- Lighting follows the view: the default source is a key light from the
  upper left and a fill light from the lower right over an ambient term,
  the other source one light from above; POINTCLOUDLIGHTING 1 lights the
  front faces, 2 both.
- A selected or hovered point cloud shows its extents box.
- A move-gizmo arrow can be grabbed along its shaft; the grab offset is
  taken on the press, so click-and-place and typed distances start from
  the insertion point.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-30 17:21:33 +03:00
ramox81andClaude Opus 5.5 4b00504453 Point cloud move gizmo
A lone unlocked point cloud selected in a 3D visual style shows a move
gizmo at its insertion point: X/Y/Z arrows and XY/YZ/ZX squares. Dragging
an arrow moves the cloud along that axis, a square moves it in that
plane; the hovered part is highlighted and each drag is one undo step.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-30 16:09:00 +03:00
ramox81andClaude Opus 5.5 331772eac2 PCEXTRACTSECTION: Extract Section Lines dialog
Picking the cloud opens the Extract Section Lines from Point Cloud
dialog with the reference's settings and defaults: entire cross section
or perimeter only, maximum points to process (slider and field, with
the estimated time), output layer (Use Current or a layer), colour,
lines or 2D polylines with their width, minimum line length and connect
lines tolerance (each measurable by two points on screen), collinear
angle tolerance (0-10) and Preview result. Create extracts; with the
preview it asks [Accept/Settings/Undo] and Settings reopens the dialog.
The settings are kept for the session. Automation action pc_section.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-30 15:50:22 +03:00
ramox81andClaude Opus 5.5 09e9e44c19 Point clouds: scan and region visibility in the cloud record
The manager's switches write the cloud's hidden scans (by the scan
identifier its project and scan file carry) and hidden regions (the
unassigned points are region 0), as the reference stores them, instead
of extended data. A parent switch shows mixed while some of its rows
are on. A cloud that shows none of its points draws its extents box.
The codec reads and writes both lists.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-30 15:38:45 +03:00
ramox81andClaude Opus 5.5 ae3868944a Point cloud manager: automation for search, folding and selection
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-30 15:01:04 +03:00
ramox81andClaude Opus 5.5 b69ae291bb Point clouds: lighting, 2D visual style, real-time density
Points carry their world normal: POINTCLOUDLIGHTING lights them in the
shaded visual styles (1 lit from both sides, 2 with back faces dark),
from the view (POINTCLOUDLIGHTSOURCE 0) or the default lights (1).
The 2D wireframe style draws no cloud points: a found cloud shows its
extents and "Point clouds are not displayed in 2D visual style.",
or nothing with POINTCLOUD2DVSDISPLAY 1. While the view moves only
POINTCLOUDRTDENSITY per cent of the points are drawn; the points are
stored in an order where every leading part samples the whole cloud.
The colour map dialog lists its schemes by name.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-30 14:54:03 +03:00
ramox81 ee0d4aedb5 Merge commit 'ac9a6b07' into codex/pointcloud-attach
# Conflicts:
#	locales/ar-SA/opencadstudio.ftl
#	locales/bg-BG/opencadstudio.ftl
#	locales/cs-CZ/opencadstudio.ftl
#	locales/de-DE/opencadstudio.ftl
#	locales/el-GR/opencadstudio.ftl
#	locales/en-US/opencadstudio.ftl
#	locales/es-ES/opencadstudio.ftl
#	locales/fi-FI/opencadstudio.ftl
#	locales/fr-FR/opencadstudio.ftl
#	locales/hi-IN/opencadstudio.ftl
#	locales/hu-HU/opencadstudio.ftl
#	locales/it-IT/opencadstudio.ftl
#	locales/ja-JP/opencadstudio.ftl
#	locales/ko-KR/opencadstudio.ftl
#	locales/nl-NL/opencadstudio.ftl
#	locales/pl-PL/opencadstudio.ftl
#	locales/pt-BR/opencadstudio.ftl
#	locales/ru-RU/opencadstudio.ftl
#	locales/tr-TR/opencadstudio.ftl
#	locales/zh-CN/opencadstudio.ftl
#	locales/zh-TW/opencadstudio.ftl
#	src/app/control/actions.rs
#	src/locale_catalog.rs
2026-09-30 13:26:52 +03:00
ramox81andClaude Opus 5.5 ac9a6b07b9 Point clouds: POINTCLOUDMANAGER palette
A dockable Point Cloud Manager lists each point cloud of the drawing (only
the selected ones while any is selected) with Regions (disabled, region
data is not read), Unassigned Points and Scans, the scans named by their
file stems. Each switch shows or hides points: the cloud, its unassigned
points, all scans or one scan. The hidden set is the cloud's
OCS_POINTCLOUD_VISIBILITY extended data (scan names plus *CLOUD_OFF /
*UNASSIGNED_OFF markers, app id registered), written as one undo step and
kept on save. The drawn points skip hidden scans by their index ranges,
and the hidden set is part of the placed-cloud key. Collapse all / expand
all, row highlight and a search box that filters rows (keeping parents of
matches). POINTCLOUDMANAGERCLOSE closes it; the pc_manager_toggle
automation action flips a switch.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-30 13:25:22 +03:00
ramox81andClaude Opus 5.5 2eaae1ecab Point clouds: POINTCLOUDCOLORMAP dialog, out-of-range colouring
POINTCLOUDCOLORMAP asks for a cloud (or None for the drawing's default
schemes) and opens the Point Cloud Color Map dialog: intensity and
elevation tabs with the scheme list, New / Delete / Rename, number of
colours (2-25, resampled along the scheme), blend between the ends,
reverse, gradient or bands, the colorized range, the out-of-range
choice and, for elevation, the cloud's extents or a fixed range with its
band height. OK / Apply write the drawing's colour map (created under
ACAD_POINTCLOUD_COLORMAP_DICT when missing) and the cloud's colouring in
one undo step; Make stylization current sets the tab's stylization.
Values outside a scheme's range take the end colours, the scan colours
or are hidden. Automation action pc_colormap edits the dialog.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-30 13:10:45 +03:00
ramox81andClaude Opus 5.5 7761652a9f Point clouds: PCEXTRACTEDGE, PCEXTRACTCORNER, PCEXTRACTCENTERLINE, PCEXTRACTSECTION
Planes are found from the cloud points under the pick and grown over
the connected points on them: two planes give a line along their
intersection over the points' extent, three a point at their corner.
A cylindrical segment gives a line along its axis. A live section
object traces the points on its kept side as polylines or lines in the
section plane, with a preview and Accept/Settings/Undo;
-PCEXTRACTSECTION creates them directly.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-30 12:47:23 +03:00
ramox81andClaude Opus 5.5 2e23a903a4 Point clouds: crops along the view, POINTCLOUDUNCROP, POINTCLOUDLOD
A crop is drawn on the view plane: it keeps the view's right and up
directions in the cloud's coordinates and its points in 3D, and cuts
along the view. POINTCLOUDUNCROP removes a picked cloud's crops.
POINTCLOUDLOD scales the points shown in tenths.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-30 11:50:26 +03:00
ramox81andClaude Opus 5.5 c2d9ac5ae2 Point clouds: attach dialog, crop toolbar, stylize, palette rows
POINTCLOUDATTACH opens the Attach Point Cloud dialog after the file is
chosen; its Attach button lives in the Attach Underlay list instead of a
separate Insert panel. A selected cloud brings the crop tools to the
right-edge toolbar. POINTCLOUDSTYLIZE sets the stylization with the
reference's prompts, the External References palette lists point clouds
(unload, reload, detach, path), and definitions keep the header extents.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-30 10:14:44 +03:00
ramox81 3afd547e28 Merge remote-tracking branch 'origin/main' into codex/pointcloud-attach
# Conflicts:
#	Cargo.lock
2026-09-30 09:15:26 +03:00
Hakan SevenandClaude Opus 5.5 ab0682153d fix(spellcheck): Linux wordlist flags unknown words; only hunspell counts as CLI
- check_word returned "valid" for a word missing from a loaded wordlist
  (!words.is_empty() && !contains, with contains already false), so the
  wordlist backend never flagged anything.
- aspell on PATH counted as a CLI speller, but only hunspell is invoked,
  so on a machine with aspell and no hunspell every CLI check silently
  failed.
- A bare wordlist offers no suggestions; the test no longer demands
  them from the linux_system backend. Unused PathBuf import dropped.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-29 23:25:25 +03:00
Hakan Seven 8ac04533c2 Merge pull request #1534 from yztink/fix/arc-cont-tangent
fix: resolve tangent snap when continuing an arc
2026-09-29 23:05:54 +03:00
Marcel 3e45331de1 Add Graphic Attributes palette toggle and persistence 2026-09-29 21:34:31 +02:00
Marcel 3b74c7e7ea Add graphic attributes controls and layer creation 2026-09-29 20:57:52 +02:00
Marcel 85e2dda6ba Improve graphic attribute handling for open boundaries and varied values 2026-09-29 19:06:29 +02:00
ramox81andClaude Opus 5.5 e9c4e501db Point clouds: drawn from their scans, placed, cropped and attached as the reference does
Scans (.rcs) and projects (.rcp) are read and their points drawn as
screen-sized dots, coloured by the cloud's stylization. The cloud's
placement carries its scale and rotation; its extents and crops stay in
the scan's coordinates. A missing scan shows the extents with the saved
path. -POINTCLOUDATTACH / POINTCLOUDATTACH, POINTCLOUDCROP and the
POINTCLOUD* settings follow the reference's prompts and messages, the
Properties list is the reference's, and a locked cloud is left alone by
edits.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-29 17:08:46 +03:00
Karim Jerbi 77fbb79917 feat(text): native OS spell-checker for text_audit (Windows/macOS/Linux)
Add cross-platform SystemSpeller abstraction with three backends:
- Windows: direct COM vtable calls to ISpellChecker (Win32 Spell API),
  no external crate — uses the same hand-rolled FFI pattern as
  file_association.rs. Handles ALL-CAPS words by also testing lowercase.
- macOS: NSSpellChecker via objc2/objc2-app-kit (already in deps).
- Linux: Hunspell .dic file reader + hunspell CLI pipe fallback.

text_audit gains three new parameters:
  system_spellcheck: true  — activate OS spell-checker
  language: en-US        — BCP-47 language tag (default: OS default)
  suggest: true            — include spelling suggestions

The agent dictionary and system speller work as a layered pipeline:
words matching the agent dictionary are accepted immediately; only
remaining words are forwarded to the OS checker.

Response includes a system_speller object (enabled, available, backend,
language) and an unrecognized_words array with handle, position, and
optional suggestions.

Integration test: text_audit_system_spellcheck_flags_misspelled_words
(20/20 p1_tests pass). Release build verified.
2026-09-29 13:48:27 +01:00
Karim Jerbi 97ea84d99a feat(text): add text_audit spell-check and dry-run replacement simulation
- Adds 	ext_audit operation under ocs_read, control bridge, and headless server
- Implements pairs / dry_run_pairs simulation returning diffs, coordinates, and unmatched queries
- Implements check_terms suspect matching and dictionary vocabulary verification
- Adds dry_run: true to 	ext_replace to preview modifications without mutating entities or pushing undo
- Supports bounds, layers, handles, and scope filtering
- Updates MCP tool definitions, schemas, and automation test suites
2026-09-29 13:01:31 +01:00
Karim Jerbi 88169c1d7a feat(mcp): add text_search and text_replace with multiline MText and accent-insensitive matching
- Add text_search (read) and text_replace (mutating) operations to control bridge, automation, and MCP
- Support single and batch find/replace pairs under atomic undo transactions
- Safely parse and preserve MText formatting tags, inline fonts, colors, and margins
- Add flexible multiline MText matching and replacement across paragraph breaks (\n <-> \P)
- Add accent/diacritic-insensitive matching across Unicode and ASCII character sets
- Support Windows-1252 / ISO-8859-1 code decoding (%%nnn), DXF Unicode escapes (\U+XXXX), and special character codes
- Expose ignore_accents parameter across ocs_read and ocs_execute MCP tool schemas
2026-09-29 12:42:00 +01:00
Marcel abef285cc6 Add Graphic Attributes fill and line controls 2026-09-29 10:10:05 +02:00
Hakan SevenandClaude Opus 5.5 07f25f4bca fix(grips): a grip lands on the snap marker it shows
While a grip drags, the edited entity is hidden from snapping and its
drag-start shape got a reference-only pass: the marker appeared on the
old edge's midpoint or corner, but the grip stayed at the raw cursor,
and the pass ran only with object snap tracking on. The reference pass
now always runs, still limited to discrete points (never nearest-style,
so the point cannot be pulled back onto the old shape) and skipping the
grip's own start point, and when no snap on the rest of the drawing wins
it places the grip too.

Fixes #1528

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-29 09:12:31 +03:00
yztink 596558f80a fix: resolve tangent snap when continuing an arc 2026-09-28 22:52:21 -07:00
Hakan SevenandClaude Opus 5.5 0a3dab12e3 fix(parametric): constraint point picks snap to endpoints and midpoints again
Entity picks switch object snap off, but COINCIDENT (and the other
constraints that take a point) only recognise a point when the pick lands
exactly on it. Without snap there were no point markers and every pick
became the whole curve. Point-accepting entity picks now keep object snap
and hand the snapped point to the command.

Discussion #1526

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 23:25:13 +03:00
Hakan SevenandClaude Opus 5.5 f8db262577 fix(blocks): palette insertion starts like a typed INSERT
The palette installed the insert command directly and kept the previous
command's last point, so ORTHO locked the insertion point to an axis
through it. The command-start reset is now a shared helper that the
palette uses too.

Fixes #1525

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 23:02:39 +03:00
Karim Jerbi fdbbcc419e perf(flatten): pre-sized collect/plan and batched entity publish
flatten_plan 2,289 -> 1,098-1,144 us (-50..-52%); flatten_scene_apply 1,213 -> 789-813 us (-33..-35% vs baseline; -28.5% same-session O2-vs-O1, drift-corrected).

Pre-sizes collect/plan vectors and adds Scene::update_entities so FLATTEN issues one geometry bump per batch instead of per entity; single update_entity keeps its exact bump-per-call semantics.
2026-09-28 20:29:40 +01:00
Karim Jerbi 91900c4e73 bench(flatten): add FLATTEN metrics with extracted helpers
Moves the flatten projection fns and command helpers into modules/draw/modify/flatten.rs (behavior unchanged) and adds flatten_plan / flatten_scene_apply to the COMMANDS suite.
2026-09-28 20:29:39 +01:00
Hakan SevenandClaude Opus 5.5 3754426361 fix(paste): pasted associative hatches follow the pasted boundary
Paste (and WBLOCK, which shares the kernel) kept the source boundary
handles, so the pasted hatch stayed tied to the original outline or to
unrelated handles in another drawing. The COPY remap now lives at the
document level and runs for both.

Refs #1370

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 20:33:54 +03:00
Hakan SevenandClaude Opus 5.5 c45f22652d fix(dimension): grip menu Above Dim Line places text by the style, not a 1-unit nudge
It moved the text a fixed drawing unit off the line and then another unit
up, wrong at any real scale. It now sets DIMTAD = Above on the dimension
and lets the automatic layout place the text.

Fixes #543

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 19:44:09 +03:00
Hakan SevenandClaude Opus 5.5 97a5bc1d39 fix(offset): the offset just created can be hovered and offset again
A running command only knew the entities that existed when it started;
committed results are now handed back to it, and OFFSET adds them to its
pick set.

Fixes #673

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 19:35:19 +03:00
Hakan SevenandClaude Opus 5.5 d70cc46712 fix(web): STEP export no longer panics; STEP/STL exports download in the browser
The STEP header timestamp used SystemTime, which panics on wasm, and the
exports wrote through std::fs, which the browser does not have.

Fixes #761

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 19:18:53 +03:00
Hakan SevenandClaude Opus 5.5 38fe2b0b08 fix(plot): text and leader background masks plot paper-white, not black
The mask is filled with the canvas colour; plotting carried that dark
colour (or the light-to-black rule) onto the sheet.

Fixes #1072

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 19:01:56 +03:00
Hakan SevenandClaude Opus 5.5 c8c2238d4a fix(wblock): exported drawings carry their block definitions and styles
WBLOCK copied only the entities and their layers, so INSERTs pointed at
missing blocks and the file opened almost empty or needed recovery. It
now goes through the cross-drawing paste kernels, which bring layers,
linetypes, styles, block definitions and baked dimension blocks along.

Fixes #1111

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 18:56:08 +03:00
Hakan SevenandClaude Opus 5.5 a94d1cfd17 fix(input): AltGr and macOS Option characters type into the command line
Windows reports AltGr as Ctrl+Alt, so '@' and similar layout glyphs were
treated as shortcuts and dropped; polar input like @10<45 failed.

Fixes #1236

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 18:47:17 +03:00
Hakan SevenandClaude Opus 5.5 c964e365c3 fix(snap): parallel snap keeps its reference, base and lock in f64
The reference direction, base point and locked point went through f32,
so the drawn line drifted off true parallel.

Fixes #1387

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 18:32:50 +03:00
Hakan SevenandClaude Opus 5.5 e97314cdef fix(pick): overlapping entities resolve to the front-most in draw order
Fixes #1439

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 18:01:38 +03:00
Hakan SevenandClaude Opus 5.5 bb9e447c64 fix(grip): drop the polar/ortho tracking guide when a grip edit ends
Fixes #1456

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 17:59:07 +03:00
Hakan Seven 1a7e372f24 Merge pull request #1517 from mechpanos/fix/windows-printer-properties-freeze
fix(print): open the driver's printing preferences without blocking the event loop
2026-09-28 17:27:32 +03:00
Hakan Seven d9899be35e Merge pull request #1514 from ramox81/codex/dwf-dgn-underlays
Attach Underlay ribbon menu, DWF/DGN attach dialog, DGN true colours
2026-09-28 17:17:17 +03:00