Commit Graph
24 Commits
Author SHA1 Message Date
Karim Jerbi b1f4047f9c fix(plugins): degrade ensure_plugin_state instead of panicking
Bug:
The typed ensure_plugin_state helper and the object-safe trait method
it wraps turned two recoverable conditions into process deaths. On the
in-process host, the helper downcast the stored state with
.expect("plugin state type mismatch for plugin_id"), so registering
state under one plugin_id as one type and later asking for another
(e.g. a reloaded plugin version changes the type while the per-tab
state map survives) panicked the host. On out-of-process hosts
(ipc/client.rs, ipc/v4/client.rs) and on NullHost, the trait method
itself was implemented as a bare panic!("ensure_plugin_state is not
supported for out-of-process plugins..."), so any out-of-process
plugin calling the documented helper crashed its own runner process -
even though the sibling plugin_state_any and plugin_state_any_mut
methods on those same implementations already degrade to None
gracefully. The helper also used .expect("init called once") on the
initialiser closure.

Reproduce:
1. Out-of-process: a plugin whose runner calls ensure_plugin_state
   (&mut host, "my.id", ...).
2. Type mismatch: in an in-process plugin, ensure state under one id
   as u32, then ensure a different type under the same id.
3. Before: step 1 kills the plugin runner at the trait panic; step 2
   kills the host at the downcast expect. After: both get None, keep
   running, and existing state is untouched.

Fix:
Give the trait method a failure channel: ensure_plugin_state_any now
returns Option<&mut (dyn Any + Send + Sync)>, mirroring the sibling
plugin_state_any_mut on the same implementations, and the typed helper
returns Option<&mut T> (any?.downcast_mut). The panicking
implementations (ipc v3, ipc v4, NullHost, and the three never-called
test dummies) return None; the in-process app host wraps its real
storage in Some with unchanged insert-or-return semantics. The init
closure is now guarded by take() with a unit-Any fallback, so even an
implementation invoking it twice fails the typed downcast instead of
panicking. Object safety, argument types and vtable slot are
unchanged; external plugin sources get a compile error at the helper
and adopt Option handling on rebuild. Regression tests use catch_unwind
so they compile against both signatures: one per panic site (out-of-
process, NullHost, type mismatch), each additionally asserting the
None result and that pre-existing state survived.
2026-09-30 19:15:50 +01:00
Hakan SevenandClaude Opus 5.5 f537aff75c feat: node graph overlay; move to opencadcodec/opencadkernel/opencadgraph
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>
2026-09-25 07:34:23 +03:00
Hakan SevenandClaude Opus 5 41ecc0c458 Merge pull request #1423 from felixriestra:pr/python-host-model
The bundled `opencad.python` plugin drives OCS through plugin API v7: all
43 canvas entity kinds, the drawing tables, and OCS's own commands run
step by step. RustPython stays in the plugin process — the plugin crate is
outside the workspace with its own lock file, so the application's own
dependencies are unchanged.

Reconciled with the plugin command work that landed as #1425, which the
branch was written before:

- Both added `impl Drop for PluginProcessInteractiveAdapter`, which git
  merges silently into two impls that do not compile. One body now
  cancels the command and then drops it from the runner, in that order: a
  plugin waiting on a step has to be released before the runner forgets
  the command.
- `execute_command` (#1425) and `run_command` (#1423) are both routes from
  a script to the command line, and only the second was guarded. The
  SCRIPTCOMMANDS switch and the denylist are shared now, so neither route
  is the unguarded one.
- `run_command` dispatched with plugin dispatch still enabled, so a script
  naming a plugin command re-entered the plugin that was blocked waiting
  for it. It sets the suppression flag for the length of the dispatch, as
  `execute_command` does.

The denylist read the command as typed, while the dispatcher resolves
aliases: a user-defined `Q` for `QUIT` (ALIASEDIT writes those) walked
past it. It resolves the alias first.

Also: the active-command comparison ran on every message, a mouse move
included, allocating two strings for names that are already `&'static
str`; SCRIPTCOMMANDS' own messages go through the translation macros; and
`docs/.DS_Store` (now ignored) and an unrelated RTF note about constraint
commands are out of the tree.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 23:26:30 +03:00
yztink 270b6102de fix(plugin): drive headless tasks, invalidate layer cache, and handle cancel event 2026-09-21 16:29:01 -07:00
Felix RiestraandClaude Sonnet 5 1b1785eefb feat(python): command runner and modify wrappers (doc.command, doc.modify)
HostApi::run_command drives the real OCS command through the same primitives
the automation channel uses, synchronously on the host thread: run a line,
start a command and answer point/text/token/entity/selection/enter prompts, or
cancel (closing anything the command opened). Refused while another command is
active, for a denylist (QUIT, NEW, OPEN, SAVE*, PY_* ...) and off the active tab.
Python: doc.command, doc.start_command and doc.modify (offset, trim, extend,
fillet, move, copy, rotate, scale, mirror, erase). Real-IPC audit checks exact
geometry for every wrapper plus guards and undo; IPC and Python unit tests; docs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-21 17:25:05 +02:00
Felix RiestraandClaude Sonnet 5 795aa922b4 feat(python): linetypes and layouts (doc.linetypes, doc.layouts)
TableOperation gains simple-linetype create/modify/rename/delete (standard names
taken, rename follows layers and entities, guarded delete) and paper-space
layout create/rename/delete/set-current/set-page (default page setup and sheet
viewport, the application's own layout switch, Model protected). CTAB now reports
the current layout. Real-IPC audit with DWG/DXF persistence and undo, IPC and
Python unit tests, docs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-21 17:05:48 +02:00
Felix RiestraandClaude Sonnet 5 b1edc9f578 feat(python): block contents (create_entity(..., block=))
BlockEntityAdd adds a validated entity to a block definition (owner set by the
host, same validation as a model-space entity, self-nesting inserts refused),
reusing the scene's block-editor routing. Deleting a block member now also
drops its handle from the block record. Real-IPC audit with an attribute
definition inside a block, DWG/DXF persistence and undo; IPC and Python unit
tests; docs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-21 16:54:30 +02:00
Felix RiestraandClaude Sonnet 5 8d20d280fe feat(python): block definitions (doc.blocks)
TableOperation gains BlockCreate (copy entities into a definition shifted by the
base point, optionally erasing originals), BlockModify, BlockRename (inserts
follow) and BlockDelete (refused while referenced; removes members, markers and
orphaned draw-order tables). Real-IPC audit with DWG/DXF persistence and undo,
IPC and Python unit tests, docs; the DXF loss of the block description is pinned
by a canary.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-21 16:28:03 +02:00
Felix RiestraandClaude Sonnet 5 aaea15e4ef feat(python): text and dimension styles (doc.text_styles, doc.dim_styles)
Extends TableOperation with text style create/modify, dimension style
create/modify (DimStyle fields as a validated JSON object), rename, delete and
set-current. Reuses OCS's style in-use scan and reference-following rename
(four style_ops methods widened to pub(super)); a renamed text style also
updates dimension styles that name it. Real-IPC audit with DWG/DXF persistence
and undo, IPC and Python unit tests, docs. Four cadcodec findings are pinned by
canaries.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-21 16:18:22 +02:00
Felix RiestraandClaude Sonnet 5 b7b6e2cef5 feat(python): validated, undoable layer table (doc.layers)
Adds the additive v7 TableOperation request and HostApi::table_operation for
layer create/modify/rename/delete/set-current. Refusals are decided before the
undo step is recorded. Python wrapper, real-IPC audit with DWG/DXF persistence,
IPC round-trip test, Python unit test and docs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-21 16:00:26 +02:00
Felix RiestraandClaude Sonnet 5 6b8056cfeb Merge origin/main into plugin/host-model-api
Conflicts were additive: main's AddLayer/ModifyLayer (LayerConfig, OptHandle)
alongside this branch's v7 system-variable, transaction, selection and solid
operations. Main's variants keep their positions in the IPC enums; the v7
additions follow them. src/io/mod.rs takes main's reworded Shape-rotation note.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-21 15:12:58 +02:00
yztink fbedb3736e feat(plugin): AutoCAD-compatible PAUSE, ENTER keyword, and command streaming 2026-09-20 21:03:12 -07:00
Felix RiestraandClaude Sonnet 5 5e6a830782 Complete Ole2Frame: embed a picture file from a script
EmbedPicture builds the frame with OCS's own EmbeddedImage and
build_embedded_ole, so the opaque storage is never scripted. PNG, JPEG and
BMP are stored as read and other formats are re-encoded as PNG. The lifecycle
test now creates through Python; a new test covers formats, aspect, origin,
layer and refusals.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-20 18:12:31 +02:00
Felix RiestraandClaude Sonnet 5 5a39052397 Add kernel booleans: union, subtract and intersect for Solid3D
SolidOperation::Boolean runs synchronously, surfaces the kernel's refusal
with a plain reason and leaves the operands untouched, consumes them on
success unless keep_operands, and records one undo step. The lifecycle test
uses planar boxes with exact volumes and covers chaining, refusals, both
formats and undo; an ignored test shows a curved refusal reaching the script.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-20 17:58:38 +02:00
Felix RiestraandClaude Sonnet 5 9cd6653a58 Complete Surface: plane surfaces, extrusions, isolines and moves
Adds SurfaceFromProfile and Extrude kernel operations (doc.solids.surface and
doc.solids.extrude), accepts surfaces in Transform, and maps Surface with
editable isolines. Fixes a real bug found by the test: the surface kind field
overwrote the entity-type kind key in Python dicts, so it is exposed as
surface_kind, with a catalog test guarding against name collisions.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-20 17:47:22 +02:00
Felix RiestraandClaude Sonnet 5 c731d3a9a4 Complete Region: create from a profile, plus tuple and int friendliness
RegionFromProfile reuses the REGION command's kernel calls; doc.solids.region
exposes it with layer and delete_source options. The lifecycle test builds
regions from real circle and polyline entities and covers refusals, a move,
both formats and undo. create_entity now accepts coordinate tuples for point
properties and every generated float setter accepts Python ints.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-20 17:37:10 +02:00
Felix RiestraandClaude Sonnet 5 04cd9e462d Add kernel solid operations and complete Solid3D (create and rigid move)
An additive HostApi::solid_operation builds cadkernel primitives and applies
rigid transforms, verifying each result lifts back losslessly and refusing a
payload it cannot lift. Exposed as doc.solids in Python; Solid3D, Body and
Region are mapped read-only with the opaque payload preserved. The lifecycle
test passes through the real runner with analytic volumes and exact extents as
oracles, DWG and DXF reopen with re-edit, and undo restores exactly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-20 17:29:06 +02:00
yztink 929f185ba5 feat(plugin): add layer creation and modification host API
- Add LayerConfig struct and add_layer / modify_layer methods to HostApi trait
- Implement IPC request/response serialization for AddLayer and ModifyLayer
- Implement HostSession methods with CAD property defaults and selective modification
- Refresh layer panel UI and invalidate scene layer dependencies upon layer changes
- Add unit tests for add_layer with defaults and partial modify_layer
2026-09-19 16:40:41 -07:00
Felix Riestra 11d5614991 Add versioned scripting host model and entity coverage 2026-09-19 01:13:24 +02:00
Felix Riestra ec2271fc9c Add versioned host settings API for plugins 2026-09-18 15:13:06 +02:00
Sebastian 09ba67bfd1 feat(plugins): expose document path to plugins via HostApi
OpenCADStudio/plugins#901 (comment)

- Add HostApi::document_path(tab_id) returning Option<PathBuf>.
- Add PluginRequest::DocumentPath and PluginResponse::DocumentPath for
  out-of-process plugins.
- Implement the request in the IPC server and in both V3/V4 plugin-side
  HostApi proxies.
- Implement HostSession::document_path by looking up the tab by id and
  returning its current_path.
2026-08-29 11:09:31 +02:00
Hakan Seven 81e7add1dd fix(tolerance): complete workflow integration
Sync current main and companion codec changes before preserving unsupported frame rows, rendering in the entity plane, and sharing final layout metrics with placement preview.
2026-08-27 00:14:08 +03:00
Hakan Seven f247d15f6e feat(plugin): harden V4 host integration
Preserve legacy wire indices and route concurrent V4 responses and background requests by correlation and stable tab ID.
2026-08-15 20:17:43 +03:00
Sebastian 29c41a4041 Snapshot: V4 plugin API and host integration 2026-08-12 21:01:39 +02:00