Introduce would_create_cycle in block_service to prevent recursive block definitions with case-insensitive matching across block records. Enforce cycle refusal at INSERT commit in BEDIT and during block redefinition (redefine_block_raw) with a shared error message. Ensure render-time recursion truncates safely with a one-time session warning on cyclic documents.
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.
Session recovery: more than one pending snapshot adds a 'Restore all'
button to the Recover Drawing modal — every snapshot goes through the
exact same guarded path as a single restore (deleted only after its
own open succeeds). The open-tab list and active tab persist to
localStorage on web (open/save/close) and the remembered tab is
re-selected once a restore batch settles; the modal notes how many
drawings the session had. Nothing is auto-opened.
Deep links: ?file=<url|name> opens a drawing at boot. http(s) URLs
must end in .dwg/.dxf and are fetched with the same streaming,
64 MB-capped, 8 s-deadline reader the remote images use, then go
through the guarded open queue; cross-origin opens announce the
origin first. Bare names resolve from the browser's recent storage,
with a hint when absent. Everything else (file:, javascript:, drive
letters, non-drawing tails) is refused with a console note.
Offline PWA: the app was installable (manifest) but downloaded its
55 MB bundle on every visit. A cache-first service worker now serves
the whole bundle from Cache Storage after one online visit —
navigations network-first so updates land, hashed assets
stale-while-revalidate, non-GET/cross-origin passthrough, old caches
cleaned on activate, and the waiting-worker update flow documented.
The Pages workflow stamps the cache version from the bundle hash and
fails if sw.js is missing or unstamped.
(cherry picked from commit 5f9b345e)
Ported onto main (#1717 review): the cache-version stamp moved from the
skipped build-web.sh wrapper into the Pages workflow; the tab-close hook
lands in main's on_tab_close, which also cancels pinned control requests.
A fresh web-vs-desktop audit found the remaining gaps; this closes the
top ten.
Raster images: IMAGE attach read the picked file via image::open on a
name-only path (dead on wasm), and raster references inside opened
drawings decoded the same way. Picked bytes are now registered in a
name-keyed store (the PDF-underlay pattern), decoding runs from memory,
and dropped .png/.jpg/.bmp/.tif files register by name so unresolved
references in opened drawings re-resolve and render (and export to PDF).
Command gaps fixed with the bytes-at-pick / download_bytes pattern:
- WBLOCK now downloads the extracted block instead of failing on fs.
- SAVEALL saves every dirty tab instead of printing a no-op message.
- SCRIPT picks and reads the .scr file through the browser picker.
- CENTERLTYPEFILE picks the .lin/.dnl file and populates from text.
- Custom .ctb tables picked on web now parse, register for the
session, appear in the plot dialog list, and apply (load_named
previously returned builtin-only on wasm).
- LAYTRANS 'Load standards file' parses the picked drawing on the
existing web worker.
- INSERT 'from file' in the block palette parses picked bytes and
feeds the shared import path.
Text fidelity: TTF-named styles rendered as thin stroke glyphs on web
because the font database was empty. Fetched Noto faces are now
registered into it with a family alias table (Arial, Times New Roman,
Cambria, ... -> Noto Sans/Serif), so those styles render filled, shape,
export to PDF as vector fills, and appear in the style picker.
ViewCube labels request semibold again on web.
docs/native-vs-web.md: the hatch and font rows were stale (both work on
web now), the GPU row claimed WebGPU is used where available (the
backend is pinned to OpenGL), and the parallelism row missed the parse
worker — all corrected.
(cherry picked from commit c643a10e)
Ported onto main (#1717 review): conflicts resolved toward main — the
font-file lookup, PDF searchable text, purge service, block palette and
plot-style copy stay as they are on main; the web pieces (font registry
and aliases, session plot styles, SCRIPT/SAVEALL/CENTERLTYPEFILE picks,
block-from-file on the web) are layered on top.
Three attack agents (state-machine tracing, executable input fuzzing,
browser-API review) found two breaks and several degradations; all are
fixed here, with the fuzzer's 22 tests kept as regression suites.
Breaks:
- Untitled tabs (and dot-names like ".dxf") autosave as DXF, but the
restore name defaulted to .dwg — the worker dispatches its reader by
the name's extension, so the only copy of unsaved work could never
be restored and was re-offered forever. recovered_name now uses the
worker's rsplit('.') convention and defaults extensionless names to
dxf, matching new-document serialization.
- " (recovered)" stacked one suffix per crash-restore cycle; the
marker is now stripped idempotently before appending.
- Opening a file (Ctrl+O / OPEN / automation) while a web open was in
flight clobbered it: the in-flight open's bytes were lost and every
queued restore prompt stalled for the session. on_open_file now
refuses while an open is running (web only).
Hardening:
- Escape on the Recover Drawing modal defers instead of deleting the
only snapshot; the Discard button still removes it.
- A cap-rejected restore enqueue warns in-app and re-offers the next
snapshot; the drop queue cap rises to 32.
- Exiting mid-round no longer races snapshot removals (deferrals are
drained, the sweep skips while a round is in flight); closing a tab
with "Don't save" now also removes its web snapshot; the snapshot
key derivation is shared via web_autosave_key.
- The plot-preview overlay no longer leaks a full PDF copy per preview
(the Download closure lives in a replaceable registry).
- A corrupt autosave index.json fails the round/boot instead of
silently resetting every row; plot pages with non-finite or non-
positive sheet dimensions error instead of emitting a NaN MediaBox.
- The drop filter accepts dot-less "DWG"/"dxf" names the worker
parses (same rsplit convention on both gates).
(cherry picked from commit 3eab00bfbf)
Ported onto main (#1717 review): the five standalone tests/*_redteam.rs
files stay out — no test target builds them and they exercised copies of
the functions rather than the code itself.
Resolved conflicts:
- app/update/mod.rs: keep the dock's dock_open_at_default for the XREF
manager; take main's stable xref row identity (row_index(&id)).
- app/view/mod.rs: keep the edge layout from app::dock; drop main's copy
of the old edge-stack code (main only reformatted it there).
- ui/window/layers.rs: keep main's new visible_indices next to the
branch's view_panel.
Files the branch only reformatted take main's version. Adds the Lao
(lo-LA) translations for the new dock texts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013RyGiMXcsf9HTKfqDxaQGt
XrefManagerSelect/RightClick/RowOp/PathPick/FindReplace carried positional display indices that rescans could retarget. New XrefRowId{key,saved_path} (the identity refresh uses) in all 5 variants, resolved via row_index at dispatch with graceful no-op; panel selection stays index-based. display.index remains for view-time styling/lookup only. Tests: xref_rowop_unknown_id_is_noop (RED first); existing dialog/simulator/click tests migrated to ids. Full suite: 2044 passed / 0 failed / 25 ignored.
- The dock's state handling and drawing move out of update/dialog.rs and
view/mod.rs into src/app/dock/ (update.rs, view.rs, tests.rs). The
overlay view_main stacked over the workspace (floating pallets, + menu,
drag preview, pointer capture) becomes one `dock_overlay` call. Code is
moved, not changed.
- Strip icons no longer show the pallet name on hover.
- Tests trimmed where they overlapped: the top / bottom half drops are
one test, the two hover tests are one, and the in-group icon reorder,
raise-float, empty floating title bar and slot-span tests are dropped
(each is covered by another test).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013RyGiMXcsf9HTKfqDxaQGt
No intended behaviour change except the fixes noted below.
- One drag state: a single `DockDrag` (move pallet / group, width,
splitter, floating size, Layer Manager column) replaces nine fields
(dragging, dragging group, resizing, split, float resizing, origin,
target, grab offset, layer column flag) that had to be kept in sync.
- `dock_expanded` becomes `dock_peek` with one meaning: a pallet revealed
although it auto-hides. `dock_reveal(id)` shows a pallet's group and
peeks it; commands that open a pallet now use it, so opening a pallet
also switches its edge to that pallet's group.
- Each group stores its own width (falling back to its widest pallet's
width until first sized), instead of every pallet's width being kept in
sync. Pallet widths now only size floating pallets and new groups.
- One `dock_set_open(id, open)` holds every pallet's open and close path.
- Leftovers removed: the tab-group config format that never reached main,
the multi-tab drag ghost header, the separate icon-hover message.
Fixes:
- Hovering a strip icon reveals an auto-hiding edge again (the icons had
lost the hover message in the option B strip). Covered by a test that
moves the pointer over the icon in the real view.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013RyGiMXcsf9HTKfqDxaQGt
- Double-clicking a docked or floating title bar did nothing. iced keeps
a widget's state (including the first click of a double-click) only
while the widget tree keeps its shape, and pressing a title bar starts
a possible drag that wrapped the workspace in an extra pointer-capture
layer, rebuilding the title bar and forgetting the first click. The
workspace is now always inside the same stack and mouse area; the
capture handlers are only attached while dragging. A test drives the
real view through iced's UserInterface (keeping its cache between
events, as the runtime does) and fails without this fix.
- The shown group's edge bar was shorter than its row (the tooltip
around the edge band sized it to its content), so it looked off
centre. Band and bar now have explicit heights.
- Picking a pallet in the + menu closes the menu.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013RyGiMXcsf9HTKfqDxaQGt
The icon strip follows the agreed "option B" concept:
- Flat 40 px strip; groups are rows of their pallets' icons separated by
hairline dividers, with no grip lines. The + button is a small round
button under the last group.
- The shown group is marked by a blue bar on its window-side edge and
brighter icons. That 8 px edge band is the group grip: hovering it
widens and lights the bar, tints the row, shows a grab cursor and a
"Drag to move group" tooltip; dragging it moves the whole group.
- Icons still drag single pallets: within their group or into another
group (between its icons), or onto a divider to start a new group.
Hovering an icon gives it a soft background and shows its name.
- Drop feedback: a short accent line with end caps between icons means
"join this group here" (the target group's bar lights up); a
full-width line on a divider with a "+ New group" badge means "new
group here". The dragged icon (or whole group) fades in place and a
ghost follows the pointer.
- The zone around a divider (a few px into the icons on either side)
starts a new group; the lower half of a group's last icon still joins
that group at the end.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013RyGiMXcsf9HTKfqDxaQGt
Dragging a pallet over its own group (e.g. the block palette, alone on
the right edge) targeted joining that group. Joining first detached the
pallet, which removed the now-empty group, then indexed it: an
out-of-bounds panic, hit already by the live drop preview. A pallet alone
in the group it would join now just stays put.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013RyGiMXcsf9HTKfqDxaQGt
- Auto-hide is a setting of the left / right edge rather than of each
pallet: the pin on any docked pallet turns it on for its whole edge,
which then shows only its icon strip until an icon is hovered. A
floating pallet keeps its own auto-hide.
- Hovering the strip icons only shows a group while the edge auto-hides;
otherwise groups switch on click.
- The + menu checks only the pallets open on that edge. Picking one that
is open elsewhere (the other edge, floating) moves it to this edge;
unchecking a checked one hides it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013RyGiMXcsf9HTKfqDxaQGt
A group is now a set of pallets shown together, stacked top to bottom,
and an edge shows one group at a time. The icon strip lists every group
(grip bar over its pallets' icons, the shown group on a grey
background); clicking any icon of a group shows that whole group.
- Dropping a pallet on the top / bottom half of a pallet in the shown
group stacks it above / below; dropping on the strip's icons places it
in that group at that spot, and on a grip or below the last group
starts a new group (which is then shown).
- Splitters set the heights of the pallets within a group; double-click
makes them equal again.
- Each group keeps its own width; the divider sizes only the group it
belongs to, and switching groups switches width.
- A group whose pallets all auto-hide shows only while hovered.
- Layouts saved by older builds load with their stacked pallets as one
group.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013RyGiMXcsf9HTKfqDxaQGt
Each dock edge now has a vertical icon strip along the window edge in
place of the horizontal tab row and the auto-hide rail.
- Every shown group gets a grip bar with its pallets' icons below it;
the shown pallet's icon has a grey background. Clicking an icon shows
that pallet in its group; hovering the icons of an auto-hiding group
opens it.
- Dragging an icon within the strip reorders it or moves it into
another group; dropping it on a grip or below the last group makes it
a new group there. On the pallet area, the top / bottom bands still
start a new group above / below and the middle joins the group.
- Dragging a grip moves the whole group to another position or edge.
- A + button under the groups opens the pallet menu (icon, name, check
mark). Opening a pallet from it docks it on that edge as a new group,
or in its existing group there; a checked pallet is hidden.
- Groups keep their width when moved; the divider against the viewport
sizes the whole edge column.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013RyGiMXcsf9HTKfqDxaQGt
- The tab row shows only when at least two pallets share a slot.
- Tab icons are larger (18 px). The shown tab gets the grey background
without a border; other tabs have no background.
- The pallet menu was drawn by a dropdown that squeezed it to one icon
wide. It is now its own overlay hanging under the menu button, laid
out like the right-click menu: pallet icon, name, and a check mark on
the right for the pallets shown in this slot. Any click outside closes
it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013RyGiMXcsf9HTKfqDxaQGt
- LAYERS opens the Layer Manager as a dockable pallet instead of a modal
dialog with a darkened backdrop. It floats over the middle of the
drawing the first time and keeps its place after that; LAYERS again
(or its close button) hides it. The table keeps its natural width and
scrolls sideways when the pallet is narrower, and the Name column
divider now drags through the dock's pointer capture. Ctrl+A selects
all layers while a layer row is selected in the open pallet.
- The Layer Manager is listed in the tab-row pallet menu like the others.
- Double-clicking a docked title bar floats the panel beside its edge;
double-clicking a floating title bar docks the panel on the side its
title bar faces.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013RyGiMXcsf9HTKfqDxaQGt
- Dropping on a docked pallet now stacks it above or below over the top
and bottom 35 % of the slot; only the middle 30 % makes it a tab.
- Tabs show the pallet icon (title as tooltip), aligned to the left.
Every docked slot has the tab row, so its menu is always reachable.
- A menu button at the right of the tab row lists every pallet with a
check mark for those shown in this slot. Checking one opens it as a
tab here (moving it from wherever it was docked); unchecking hides it
and keeps its tab for next time.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013RyGiMXcsf9HTKfqDxaQGt
- Dragging a tab along its slot's tab strip moves it to the gap under the
pointer; the drop preview shows the new tab order. A panel dragged from
elsewhere onto a tab strip lands at that position too. A drop in the
middle of the slot still appends a newcomer and leaves a member where
it is.
- All tabs of a slot share one width: a joining panel takes the slot's
width, resizing or resetting one tab sizes them all, and slots sized
apart by older configs render at their widest tab. Switching tabs no
longer changes the column width.
- Hovering an auto-hide rail raises its slot without turning the rail
blue.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013RyGiMXcsf9HTKfqDxaQGt
- Docked title bars show the pin and close only while the pointer is
over the bar; the bar keeps a fixed height so nothing jumps.
- Auto-hide on is drawn as a 30° tilted pin (new pin_active.svg)
instead of a coloured button background, on docked and floating bars.
- A floating panel's resize grip sits in the bottom corner away from its
title bar. With the bar on the right it is bottom-left (new mirrored
resize_left.svg) and resizing from there keeps the right edge fixed.
The bar stays on its side while resizing.
- While a floating panel is dragged, only the drop preview shows it; the
panel no longer stays drawn at its old place.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013RyGiMXcsf9HTKfqDxaQGt
Panels can now float over the workspace, share a docked slot as tabs,
and split an edge at heights the user picks.
- Dropping a panel away from the edges floats it at the pointer. Floating
panels move by their title bar, resize from a corner grip, and come to
the front when clicked.
- Dropping in the top or bottom quarter of a docked slot docks the panel
above or below it, as before. Dropping in the middle adds the panel as
a tab of that slot. Pressing a tab shows that panel; dragging a tab
pulls the panel out again.
- While dragging, a blue ghost previews the result: the new slot, the
slot with its future tab row, or the floating window.
- A splitter between stacked slots sets their heights; double-click it
to make the slots equal again. Heights persist.
- A press only becomes a drag after 5 px of movement, so clicking a title
bar or tab never moves the panel.
The persisted layout now stores each edge as tab groups with a height
weight, plus a list of floating panels. Configs with the old
bare-panel-id stacks still load as single-panel slots.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013RyGiMXcsf9HTKfqDxaQGt
SelectionState now holds view, gesture, input, menu and orbit sub-structs; all 450 access sites updated mechanically with no behavior change. Deletes three write-only dead fields (box_last, box_last_crossing, poly_last_crossing). Hand-rolled clears in commands and dim_viewport route through the new clear methods; bench fixture updated. Full suite: 1976 passed / 0 failed / 25 ignored.
A command started from the command line, an alias, a shortcut or Repeat
now lights the same ribbon button a click on it would, so users learn
where the tools they type live. A button inside a dropdown lights the
dropdown, a slide-out tool lights its group title, a tool hidden in a
collapsed panel lights the panel's opener, and a button on another tab
lights that tab's header. Always on, in the existing active-button blue.
Buttons are matched by the command they dispatch, so ids that differ
from their command still resolve; the tab on show wins when a command
appears twice, and a clicked button keeps its highlight. Transparent
commands leave the running command lit. The #355 rule that a highlight
lasts only while something runs moves from the ribbon click handler into
dispatch_command, so typed commands are cleared the same way.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rebuild the Blocks palette with Current Drawing / Recent / Favorites /
Libraries tabs, wildcard filter, six views, insertion options, click and
drop insertion and a block context menu. INSERT opens the palette, the
new -INSERT runs on the command line, the ribbon Insert button becomes a
block gallery, and BLOCKMRULIST, BLOCKREDEFINEMODE, BLOCKNAVIGATE,
BLOCKSTATE, EXPLMODE and INSNAME are added.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Point clouds: drawn from their scans, attached, cropped and stylized.
The fork's codec patch is resolved to the upstream pins: the point cloud
records landed in opencadcodec 5ef9376, carried through opencadkernel
68eeda1 and opencadgraph e204966.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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().
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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
DWFATTACH / -DWFATTACH and DGNATTACH / -DGNATTACH attach a DWF / DWFx sheet
or a DGN (V7 or V8) model with the reference's prompts; ATTACH routes the
file types to them. The sheet or model is read into vector paths, drawn as a
transparent underlay raster (contrast, fade, monochrome and background
adjustment apply) and offers its geometry to object snaps. Definitions are
kept in ACAD_DWFDEFINITIONS / ACAD_DGNDEFINITIONS, the External References
palette lists and reloads them, and image textures get line-keeping mip
levels so thin underlay lines stay visible when zoomed out.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The node library leaves the overlay and becomes a dock panel (PanelId::
NodeGraph), docked on the right the first time the graph opens and movable
like any other panel; closing it closes the graph. It follows the block
palette's layout: title bar, then a search box with New / Open / Save
beside it, then the categories, of which only Objects starts open. Entries
are clicked (placed at the canvas top-left) or dragged onto the canvas.
The title bar (title, auto-collapse pin, close, drag to re-dock), the
toolbar icon button and the panel frame were copied across Properties,
Block Palette, External References and Browser; they are now
dock::title_bar, dock::tool_button and dock::frame, used by all of them.
Block Palette's two tool buttons gain tooltips, and External References'
Auto / Close tooltips are now translated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The codec and kernel repositories and crates are renamed opencadcodec and
opencadkernel. Inside OCS they are depended on under short keys, so code
reads `codec::`, `kernel::` and the new `graph::` (opencadgraph, the node
graph engine). File-format and protocol spellings stay as they were: the
ACIS product id `acadrust`, the ACADRUST_* switches and the plugin
version keys `acadrust_version` / `acadrust_source`.
A button left of MCP on the command line opens a translucent node graph
over the viewport, with a palette of object, input, math, logic, list,
text and geometry nodes added by click or drag. An object node owns
drawing entities and its rows are the Properties panel's own rows for
them: the single-object page is extracted from refresh_properties as
entity_property_sections, values use the control protocol's
property_json, and writes go through set_property_value with a property
target override, so the panel, MCP and the graph share one read/write
path. A list reaching an object row makes one entity per element. Each
graph action is one undo step; the canvas pans and zooms.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A docked outline of the drawing: the three origin planes, the open
sketch, and every solid labelled with the operation that made it,
recovered through primitive_property_operation. Rows run ordinary
commands, so clicking a plane starts a sketch on it and clicking a
body selects it.
It is a table of contents, not a feature tree. The DWG history records
how each solid was built but nothing re-runs it, so an earlier feature
cannot be edited to rebuild the ones after it. Presenting the list as
editable history would promise exactly that, so the panel states what
it is instead.
Off by default and opened with BROWSER, leaving the existing dock
layout alone for anyone who does not want it.
The Change Path Type row shows a right-triangle affordance and reveals the Make Absolute / Make Relative / Remove Path submenu beside it. The flyout opens on hover only and a shared submenu-row style removes dead code from the earlier iterations.
Row right-click menu is exactly Open, Attach..., Unload, Reload, Detach, Change Path Type group with Make Absolute, Make Relative, Remove Path, Select New Path, Find and Replace. Change Path Type renders as indented group. Open uses OCS for DWG and OS default for images and PDFs. Preview decodes on selection change, not only at refresh. Details shows placed display width and height for image instances.
Detach and Bind on nested rows report the CLI-worded per-entry error instead of skipping silently. Adds GUI-path regression tests for every row operation on direct and nested rows, plus Reload All, Overlay, Change Path, and Find and Replace coverage.
Right-click actions bound to the row under the cursor, split-button toolbar groups with shared borders and hover states, and a notice when an operation needs a selection first.