Sync clients, indexers and virus scanners open a freshly written file
for a moment without delete sharing, which refuses ReplaceFileW with a
sharing violation. Saving straight after Save As into a Dropbox folder
hit this every time. The replace now retries for about two seconds
before reporting the file in use.
Fixes#1712
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The button runs QNEW since #1635, and QNEW was missing from the
commands the Start screen allows, so it was refused until a drawing
was open.
Fixes#1728
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- Selection filter: condition rows name INSERT as a block reference, as
the object-type list does since #1726 (shared qselect_type_label).
- MCP: re-pin the tool schema digest for the view_background action
added in #1722.
- -VIEW: fix an indentation slip and wrap two long lines from #1722.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A layout plotted by window drew its sheet at the paper-space origin, so
the title block sat on the grey background away from the paper. The
sheet now centres on the plot window, or sits at the plot offset from it
when the plot is not centred.
Bump codec 8aa574a (polyline vertices in owned-handle order, which fixes
self-crossing viewport clip boundaries), kernel d79bb30, graph 4810ee0.
Fixes#1678
Co-Authored-By: Claude Opus 5.5 <[email protected]>
#1286 added the drag-to-install Applications symlink to
packaging/build_macos_signed.sh, but official DMGs come from release.yml,
which still ran `hdiutil create -srcfolder OpenCADStudio.app`. That image
holds only the app, so the merge had no effect on the downloads #769 is
about.
Stage the app beside an /Applications symlink, as the signing script
does, and build the image from that folder. A new step mounts the result
and fails the job unless the app is present, `Applications` points at
/Applications, and the app's signature still verifies. Untagged runs
upload the DMG as a workflow artifact, as the snap job does, so the
layout can be checked without cutting a release.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The web build was only deployed to the live site, so a specific version
could not be pinned. The bundle is now zipped before the site data is
added and uploaded to the release; hotfixes built from main skip it.
Closes#1706
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Re-authored constraint-group geometry dependencies carried an empty
persistent subentity id, which crashed strict readers loading the file;
they now name the whole curve with a single-edge id.
Bump codec bc350f2 (declare classes of objects added to pre-R2004
drawings), kernel e692b97, graph 37d38eb.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Document tab: every concrete colour the drawing uses (objects, block
definitions and layers), found in one pass when the panel opens, as a
grid of swatches sortable by recently used, most used, colour and colour
number. The recently-used list is now kept per drawing, so the ribbon's
Recent row shows this drawing's colours.
Color books tab: load AutoCAD .acb colour books (remembered in the
settings on desktop), browse them page by page with the page strip,
search by name and see the RGB equivalent. A book colour is applied as
RGB plus its BOOK$NAME identity on objects and layers instead of
dropping the name.
Strings translated into all 22 locales.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_013rGmDUNr1hUf4zo1X3dak8
Resolve conflicts with the new gradient and graphic-attributes colour
targets: keep main's targets and menu handling, routed through the new
ColorPicker state; adapt dock/view.rs to `color_picker`.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_013rGmDUNr1hUf4zo1X3dak8
- Split ColorEditor::view into mode_row, slider_rows, field_view,
hex_row and hex_field; output is unchanged.
- hex_string reuses config::rgb_to_hex, and parse_hex documents why it
stays separate (it also accepts #RGB).
- ink_is_dark uses Color::into_rgba8 instead of rounding by hand.
- Remove four translation keys the old dialog used and nothing
references any more, from the catalog and all 22 locales.
- Eyedropper: without a main window there is no screenshot to wait for,
so leave the eyedropper; keep the magnifier inside small windows.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_013rGmDUNr1hUf4zo1X3dak8
- Keep the modal keyboard comment with the code it describes and match
shortcut keys without an extra closure.
- Drop unused ColorEditor::mode, EyedropperMsg::Cancel and a duplicate
rgb helper; re-export nearest_aci instead of wrapping it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_013rGmDUNr1hUf4zo1X3dak8
- The gradient view no longer shows the nearest index colour.
- Mode buttons are 44 x 24 and the pipette button matches their height.
- Slider rows are 14 px apart and the gradient field is 108 px tall, so
the hex value sits on the same row in the HSB, RGB and gradient views.
- The interface colour picker (Options) shrinks to its content, ending
just below the hex row.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_013rGmDUNr1hUf4zo1X3dak8
- The gradient mode button is just its gradient, without a frame.
- In the gradient view H, S and B sit on the left and Hex on the right
of one row; the nearest index colour moves to the row below.
- Every colour swatch in Options opens the Select Color panel: the six
theme colours, crosshair, snap marker and command line text join the
three viewport backgrounds. BgTarget becomes UiColor, and one
color_swatch helper replaces the four hand-built swatches.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_013rGmDUNr1hUf4zo1X3dak8
- Current and New are a 50/50 band across the panel with their names
and values inside; the Color text box is gone.
- The gradient mode button is one gradient across the whole button,
gray until that mode is active. Mode buttons share one width.
- Tab and Shift+Tab move between the panel's own fields.
- Number boxes are 54 px wide, the hex box 74 px.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_013rGmDUNr1hUf4zo1X3dak8
The codec keeps dynamic-block, associative and material records intact on
DWG save (strict readers rejected re-saved drawings at BLOCKSCALEACTION,
associative action bodies and MATERIAL records). A checker material
texture no longer rewrites the native map source, so a map with a
procedural texture counts as content whatever its source says.
Valuable parts of #1671 (with opencadcodec#100): measured on 235 sample
drawings re-saved after an edit, a strict reader now rejects 4 instead
of 7; the remaining 4 are R14/R2000 tables, which the PR codec also
fails.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- ColorEditor: the HSB / RGB / field editor and hex box, now shared by
the Select Color panel and the Options background swatches. Those
swatches open the panel without tabs or index colours, which removes
the iced_aw color_picker feature and the BgPicker messages.
- Sliders are iced's built-in slider and vertical_slider over a
gradient container; the custom GradientSlider widget is gone.
- Nearest index colour comes from opencadcodec's aci_table::nearest_aci.
- Eyedropper: a pipette at the right of the mode row hides the panel,
captures the window with iced's screenshot task and shows an 11x11
magnifier beside a crosshair; a click picks the pixel, Esc backs out.
Not offered on the web build.
- The hex box applies six-digit values while typing.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_013rGmDUNr1hUf4zo1X3dak8
- Gradients: the middle colour sits (horizon - 0.5) viewport widths from the
centre and blends over height viewport heights each side; two colours
blend over a viewport height each side.
- Image backgrounds: offset in device pixels (right, down), negative scale
mirrors, fitted with aspect ratio covers the viewport; the dialog keeps an
image's offset and scale.
- Layouts: a model-space view restored on a sheet asks for the viewport,
enters it and gives it the view's camera, background and visual style;
inside a viewport the view goes there; a paper-space view's background is
kept on the sheet viewport but not drawn; floating viewports draw their
background.
- Background objects no view or viewport uses any more are erased; a view
without a background restored where the viewport already is leaves the
viewport's background.
- The viewport's named visual style (Realistic, ...) shows by name and the
render-mode picker records its style on the viewport record.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
HostSession::update_entity, update_entities_transaction and remove_entity mutate entities and republish the document view, but never rebuild the app-side selection caches the way the built-in edit paths do. A selected entity updated by a plugin kept its cached grip markers at their pre-edit positions until the next real selection event; regen did not help and HostApi::set_selection cannot work around it (it bypasses the grip cache).
Refresh the grip cache for the session tab after a successful update_entity / update_entities_transaction / remove_entity, mirroring the built-in ERASE path and the other editors, so a plugin-driven edit keeps the cached grips on the refreshed geometry. A session targeting a background tab leaves the active tab cache untouched (found by the civil plugin manual QA, 2026-10-12).
An image background not fitted to the screen keeps its own pixel size
(times its scale), centred on the viewport and moved by its offset, as
the reference draws it; tiling repeats it from there.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- Translate the strings the #1717 port cataloged in English only
(CSV picker, plot-preview task, extraction cap, shared-copy warning)
for every locale, and the whole ported web set for lo-LA, which the
contributor branch predates.
- hu-HU carried twelve keys twice after the port's merge; keep main's.
- The STB container/refusal tests exercised parsing this port did not
bring over; the preview-clamp test now only pins the upper bound, since
main's raster path already clamps preview pages further.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Follow-up to the #1717 port:
- The downsample tier holds the decoded RGBA, the resize's vertical-pass
intermediate (width x new height in RGBA f32) and the output at once;
that sum now has to fit the decode budget, or the image is refused up
front instead of overshooting it several times over. The native
budget drops from 4 GiB to 2 GiB.
- A native IMAGE pick decodes from the picked file instead of copying
its bytes into the session registry, so an edited file reloads and the
bytes are not held for the session. The web keeps registering them.
- Reword a plot-preview comment.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
recovered_name reduced only the head to its final path component, so a
stored name like "a.b/c" came back as "a (recovered).b/c". The whole
input is now sanitized first, and the empty-stem fallback uses the
sanitized component, which also restores idempotence for
separator-headed names ("/.dwg" used to alternate across crash cycles).
Ported from 9262d63f (#1717); its integer-precision fix landed with the
d719fb77 port, and the standalone tests/fuzz_round.rs suite (copies of
the functions rather than the code) stays out.
(cherry picked from commit 9262d63f)
Ported from da950460 (#1717).
In-place save:
- A stale retained handle could receive bytes serialized for a
DIFFERENT format (Save As .dwg->.dxf whose re-pick failed wrote DXF
bytes through the .dwg handle) or silently save in place while the
user was told a copy was downloading. The serialized format now
travels to completion (tab-keyed pending map), the write requires a
matching handle extension AND a live tab, and a failed re-pick
drops the old handle before the download fallback.
- Discarding an unsaved tab leaked its handle, and a serialization
still in flight could then overwrite the real file the user just
declined to save. Discard drops the handle; a tab-gone completion
downloads instead of writing.
- Overlapping saves had no guard: a wasm in-flight slot refuses a
second save with the same localized message, cleared on every exit
path.
Paste: pasted_image_name built its timestamp from Date::new(0) — a
CONSTANT name, so every paste overwrote every earlier paste's image.
Real clock, milliseconds, and a collision-suffixed unique name now.
Capture: a web capture in a hidden tab, or one with nothing redrawing,
could hold the automation slot forever. It fails fast when the page is
hidden and gives up after 5 s, on main's frame-parked capture.
The XREF manager reports registry-loaded xrefs with their size on the
web instead of NotFound.
Left out: the always-warm print snapshot (a frames subscription that
emits a message on every frame keeps the app redrawing at the display
rate), so the browser-menu print wiring from the previous commit goes
too — its beforeprint readback printed blank pages on WebGL2. The
merge-residue hunks (re-added XREF bind tests, snap endpoint map and
3D-snap gate) belong to code main already has in its own form.
(cherry picked from commit da950460)
The last high-severity web gap plus a small integration bundle.
In-place save: with the File System Access API (Chromium/Edge), Save
now works like desktop. Plain Save on a tab with a retained handle
writes straight through — no picker, no download, no "(1)" copies.
First save or format change opens showSaveFilePicker AT the confirm
gesture (the worker serialization that follows can outlast the
transient-activation window; retained handles need no activation to
write). Picker cancel cancels the save like desktop; picker or write
failure falls back to the legacy download with a note; Firefox/Safari
keep the download flow untouched (feature-detected). Handles are
keyed by tab id in a thread-local registry (Message must be Send —
JsValue cannot ride in messages or App state); closing a tab drops
its handle; Save As re-picks. Dirty clears only on successful
persistence, same race guard as before.
Image paste: Ctrl+V of a screenshot inserts it as an image reference
— the clipboard reader falls back to image/* items when neither the
entity clipboard nor text has anything (precedence preserved), the
bytes enter the image source registry as pasted-<timestamp>.<ext>,
and attach reuses the IMAGE command's model-creation path verbatim.
Browser print: beforeprint snapshots the WebGL canvas into a print-
only <img> (the capture_canvas technique), so the browser's own print
menu produces the drawing instead of a blank page.
Automation capture on web: the capture op no longer calls the
synchronous GPU-map screenshot (dead on wasm); main's frame-parked
capture waits for a presented frame and reads via capture_canvas — the
same workaround the save-thumbnail path uses — delivering the PNG as a
download.
(cherry picked from commit 6e89b36d)
Ported onto main (#1717 review): the web capture rides main's existing
frame-parked capture (one ControlCaptureFrame path, ignoring the timer
tick on wasm) instead of a second parked-capture state; closing a tab
drops its file handle inside main's on_tab_close.