fix(blocks): one block definition service for delete, rename and purge on every path

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.
This commit is contained in:
Karim Jerbi committed 2026-10-12 07:25:11 +01:00
1 parent 5dfa41c032
commit 2b50b6c858
16 files changed
+2701 -262

No files matched your search

File diff suppressed because it is too large. Load diff
+25 -14
View File
@@ -491,20 +491,31 @@ impl OpenCADStudio {
Tab::Current => {
let name = item.name();
let i = self.active_tab;
let referenced = self.tabs[i].scene.document.entities().any(|e| {
matches!(e, codec::EntityType::Insert(ins) if ins.block_name.eq_ignore_ascii_case(&name))
});
if referenced {
self.command_line.push_error(&crate::tf!(
"Block \"{name}\" is referenced in the drawing and cannot be removed."
));
} else {
self.push_undo_snapshot(i, "PURGE");
crate::app::control::erase_block_definition(&mut self.tabs[i].scene, &name);
self.tabs[i].dirty = true;
self.command_line
.push_output(&crate::tf!("Block \"{name}\" removed."));
self.refresh_block_palette();
let open_edits: Vec<codec::Handle> = self.tabs[i]
.block_edits
.iter()
.map(|session| session.br_handle)
.collect();
self.push_undo_snapshot(i, format!("Delete block {name}"));
let outcome = crate::scene::block_service::delete(
&mut self.tabs[i].scene,
&name,
crate::scene::block_service::DeleteOptions {
force: false,
open_edits: &open_edits,
},
);
match outcome {
Ok(_) => {
self.tabs[i].dirty = true;
self.command_line
.push_output(&crate::tf!("Block \"{name}\" removed."));
self.refresh_block_palette();
}
Err(error) => {
self.discard_last_undo_entry(i);
self.command_line.push_error(&error.to_string());
}
}
}
Tab::Recent | Tab::Favorites => {
+2
View File
@@ -93,6 +93,8 @@ fn reorder_insertion_index(from: usize, to: usize, after: bool, len: usize) -> O
(insertion != from).then_some(insertion)
}
#[cfg(test)]
mod block_ops_tests;
mod blocks_palette;
mod command;
mod context_menu;