- plot dialog: the web build defaults to 'Save to PDF file...' (its only
output choice); native dropdowns and defaults are untouched.
- on_plot_dlg_commit forces to_file on wasm so a stale page setup can no
longer route the web build into the (unreachable) printer path; both
preview branches now go through preview_page_pdf, which is identical to
the old inline temp-PDF + OS-viewer flow on desktop.
- Print ribbon / PRINTALL on wasm render the PDF and open it in a tab
instead of erroring; PrintAllPdf picks a virtual path and downloads.
- pdf_plot_options maps the dialog's Quality dropdown into PdfPlotOptions.
(cherry picked from commit 892d693d2d)
- pdf_export: drop the stale wasm32 gates (printpdf 0.9.1 compiles for
wasm; the memchr/lopdf incompatibility is gone with memchr 2.8.3).
Rendering is portable via render_pdf_bytes; only persistence differs:
atomic temp-file write on desktop, browser download on the web.
pick_pdf_path_owned on wasm returns a virtual path so the existing
message flow works unchanged.
- printpdf moves from the native-only dependency table to the shared one.
- print_to_printer: preview_page_pdf / preview_pages_pdf render the same
bytes and open them in a browser tab on the web (the browser's print
dialog takes over); desktop keeps the temp-PDF + OS-viewer flow.
- sys: open_bytes_in_new_tab (blob URL, popup-blocked falls back to a
download).
- New PlotQuality preset caps embedded raster resolution (High = native,
Normal = 300 DPI, Low = 150 DPI effective); vector output is untouched.
The image resource cache keys on the post-resample dims so one pixels
Arc placed at different effective DPIs embeds per-placement XObjects.
(cherry picked from commit 4812bc775d)
Ported onto main (#1717 review): the plot model lives in io::plot; Normal
keeps native pixels like High (only Low caps images at 150 DPI, so desktop
output is unchanged); the searchable TrueType text layer and its allsorts
subsetting now build on the web too; the plot stamp reads the time through
js_sys on wasm.
Add opt-in response_detail=summary with step and change counts, compact state, warnings, and actionable errors or prompts. Return an ocs_read batch_result request for full cached details without resuming mutations.
Keep compact/full behavior and idempotent batch retries intact. Preserve active-step context and report unavailable cached results explicitly. Document cache scope and retention; update schemas and digest.
Validation: 47 MCP unit tests, live stdio smoke test, rustfmt, and diff checks passed.
Share operation definitions between individual requests and batch steps, reducing the execute request schema by 39%. Place definitions at the tool input schema root so local references resolve correctly.
Update the schema digest and smoke assertions. Verified with 45 MCP tests, the live stdio smoke test, and 228 schema validation comparisons.
new src/scene/block_service.rs (references, delete, rename, purge candidates, name validation, one error type and message). The command-line RENAME, palette Remove, plugin API, control/MCP block_delete and PURGE all use it. Layout, xref, dynamic-block, anonymous and open-in-editor records are protected; PURGE removes an anonymous block only when nothing references it. PROTOCOL CHANGE: control/MCP block_delete refuses a referenced block unless force=true (erases referencing INSERTs in one undo step). New REST route DELETE /api/v1/blocks/{name}?force=true. MCP tool digest re-pinned. Undo labels are Delete block X, Rename block X to Y and Purge.
world = insert_point + Rot * Scale * (P - base_point). Attribute placement, EXPLODE, XCLIP storage, REFEDIT's working copy, the UCS reframe of attributes and the dynamic-block visibility grip ignored the base point and were offset by Rot*Scale*base. They now share insert_transform_with_base. Creation stores a ZERO base and redefine resets it; a no-op redefine moves nothing; the Block Definition dialog prefills the base from the definition. XCLIP filters stored by older builds keep their old displacement.
bake_dimension_blocks copied the dimension's whole EntityCommon, including its extension dictionary handle, into every baked child, so many entities claimed one dictionary and file validation reported an error per entity. Baked children now get no extension dictionary. The same defect is fixed in define_transformed_block and the cross-document paste clone. Each has a test.
The missing-fonts list is as wide as its longest name, so its scrollbar
was drawn over the end of every name ("ROMANT.sh").
Fixes#1715
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Their grip code already works in world space, and the dispatch layer
converted the grips through the OCS a second time, so for a normal of
(0,0,-1) the grips (and grip edits) were mirrored across the Y axis.
Fixes#1683
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A polyline with a thickness but no width is drawn as its extrusion edges
alone, and those edges were forced black (the outline colour meant for the
filled tube of a wide polyline). On a dark background the polyline looked
black and changing its colour did nothing. Only a wide polyline's tube
edges are black now.
Fixes#1682
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The two dock tests that build and drive the real view overflowed a
default test thread's stack, and made in parallel their headless
renderers crashed the test process about one run in three.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
EXPORTSVG left the plot dialog's SVG device on, so a later EXPORTPDF still
offered a .svg file. The SVG save dialog and plot button texts had no
catalog entries.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#1707 took any snap name typed at a point prompt as an override, so a
prompt offering Center, Tangent, Parallel or None (ARC, ELLIPSE, CIRCLE
TTR, ...) could no longer be answered with that option's name. An option
the prompt offers now wins. The point-step test it copied from MTP is one
helper shared by MTP and the override on both input paths.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#1713 dispatched a captured SPACE as a shortcut whenever it was rebound,
so with SPACE on FINALIZE a space typed into any other text field (layer
names, Properties, find) also ran FINALIZE, and the space it typed stayed
in the command line. Ask whether the command input has focus before
running the binding, and drop the space the field already inserted.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#1692 limited no-trim FILLET to two lines and refused arcs and polyline
segments, which #1558 already filleted without trimming (only the arc is
added). It also turned T/N at the object prompts into silent toggles, so
the Trim option no longer reached its Trim/No trim prompt and the choice
was not saved. Keep the arc bookkeeping it added (the new arc can be
picked next, and stays on the picked layer), restore every pair and the
Trim prompt.
CHAMFER's new no-trim mode starts from and saves the same TRIMMODE value
as FILLET instead of always starting trimmed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#1688 accepted any degree (falling back to 3 on a malformed value) and
overwrote the degree the codec had clamped to the control-point count, so
a two-point default spline stored degree 3 against a degree-1 knot vector.
Reject a non-integer or out-of-range degree, too few control points for
the degree, a knot vector that is not control_points + degree + 1
non-decreasing values, and weights that are not one positive value per
control point.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#1666 sets group 295 on extrusion history records, which makes the kernel
read the embedded profile and path where they stand and ignore the
matrices. The planar fallback profile (a region-derived LWPOLYLINE) was
stored in its plane's local frame with the frame as matrix, so with the
flag it rebuilt at the origin. Re-express it in its own OCS (normal and
elevation) when the record is built.
Pins the kernel and graph at upstream (opencadkernel#84 merged as f051138)
instead of the ramox81/cadkernel [patch].
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#1681 places a dimension's picture block by its insertion point, rotation
and scale. Edits (move/rotate/scale/mirror, copy, paste) transform the
picture's sub-entities in world space, and the codec's dimension transform
moves the insertion point too, so a rotated or copied dimension would get
the placement applied twice. Fold the placement into the subs before the
edit and leave the dimension at the identity placement afterwards.
The picture insert no longer takes the dimension's extrusion: OCS bakes
picture sub-entities in world coordinates, so only the stored insertion
point goes through the OCS. A zero insertion-scale component counts as 1.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The informational clippy job fails on two pre-existing literals
(also failing on main, surfaced on this PR's merge): the trim
tangency test's angle list used 3.14 where pi was meant — use the
constant; the point-cloud light-direction fixture in render.rs uses
a hand-tuned 0.318 component that only looks like 1/pi — keep the
tuned value and allow the lint on that statement.
Resolve conflicts from main's rustfmt pass by taking main's versions of
command.rs, style.rs, view/mod.rs, scene/mod.rs, ui/window/mod.rs and
ui/properties.rs and re-applying this branch's Graphic Attributes changes.
Add the Graphic Attributes strings to the new lo-LA catalog.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E1QnME6WJbD5iGFae1eeCb