Solids (meshes) now tint on rollover and selection: hover washes orange,
selection washes blue, with selection winning when both apply. Meshes
re-upload their tint when the highlight set changes, and the rollover pick
now also hits shaded solid bodies and hatches (mirroring the click-select
order: wire → hatch → block hatch → solid).
Wires get the same scheme: the selection xray overlay recolours selected
entities blue and the hovered entity orange (previously both blue). Drops
the now-unused highlight_handles union helper and its cache.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3D solid meshes inside block definitions were drawn once at block-local
coordinates without the INSERT transform, so a block authored in mm and
placed into a metre drawing at a 0.001 INSERT scale rendered ~1000x too
large. Split solid meshes into top-level (layout-owned, offset and drawn
flat) and block-definition (kept block-local) sets, and emit a transformed
instance of each block-def mesh per model-space INSERT, accumulating nested
transforms. Layout blocks are identified from the Layout objects'
block_record handles, since BlockRecord::is_layout() also flags ordinary
blocks in some files. (#123)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When drawing with Ortho (or Polar) on, a point that snapped to an object
(e.g. an Endpoint) was still pulled onto the ortho axis, so you couldn't
close a shape exactly on an existing vertex. Skip the ortho/polar
constraint while an object snap is locked, in both the preview and commit
paths. Grid snap is positional rather than an object snap, so it still
combines with ortho/polar. (#132)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Make grid-snap a per-drawing view setting like grid visibility: drop it
from the persisted OSNAP set (settings.txt) and round-trip it through each
viewport's VPort SNAPMODE instead.
Grid display and grid-snap are now stored per model tile (like render
mode), written to and read from each tile's *Active VPort entry. The live
toggles mirror the active tile, and grid/snap are adopted from the active
viewport whenever it changes — on file load, tab switch, model-tile
activation, and new drawing — so they follow the active viewport rather
than a global preference.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Grid on/off now round-trips through the drawing's *Active VPort, so it is a
per-file view setting like every other CAD app treats it. Drop show_grid
from UserSettings (and its settings.txt key) and instead adopt the opened
file's saved grid_on on load. A legacy "grid=" line in an old settings file
is harmlessly ignored.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Mirror the in-app grid and grid-snap toggles (plus the adaptive grid
spacing) onto the *Active VPort entries so a saved DWG/DXF reflects what
the user actually has on, instead of always writing VPort defaults. The
flags are stamped when the toggles change and again before a save, and
carried across the camera-sync VPort rebuild so they survive an orbit/pan.
Only grid_on, snap_on and grid_spacing have an OCS counterpart; the other
VPort view fields (view twist, clip planes, lens length, etc.) have no
in-app source and stay at their faithful defaults.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Neutralise vendor references in comments to plain behavioural descriptions
(object acquisition / object snap). Remove the PICK_HIGHLIGHT and
PICK_HIGHLIGHT_DIM wire colours: their only consumer was the stripped
storm-sewer add-on, and the retained plugin acquisition pathway draws the
ObjectPick snap glyph and per-command hover previews instead, so the
constants are dead.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Remove accidental dead code (unused imports, the superseded paste tool()
helper, a never-read TORIENT handles field, a duplicate let binding, an
unneeded mut) and mark intentional-but-unwired scaffolding with
allow(dead_code): the web-only save_to_bytes, the plugin loadable() probe,
the planned PICK_HIGHLIGHT colours, the DynFieldSpec label, and the layout
ribbon helpers. Native build is now warning-clean apart from the upstream
nom/quick-xml future-incompat notices.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Object snaps were chosen purely by nearest screen distance, so the
"continuous" snaps that sit on the geometry (Nearest, Perpendicular)
were almost always closer than a discrete Endpoint/Midpoint/Center and
silently suppressed every other enabled snap. Selection is now
priority-then-distance: a higher-priority snap inside the aperture wins,
distance only breaks ties within a priority. (#118)
The aperture gate is `!(d2 < radius2)` rather than `d2 >= radius2` so a
NaN distance from degenerate geometry is rejected instead of passing the
gate and being chosen on rank alone (which fed a NaN point to the fill
tessellator and panicked).
Perpendicular now drops its foot from the command's drawing origin
(`Snapper::from_point`, set before each snap) instead of the cursor, so
the new segment is genuinely perpendicular to the target line rather than
just the nearest point on it.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Modals blocked the mouse but not the keyboard: keystrokes from the global
key subscription leaked past an open dialog into the command line and
fired as commands once it closed. Guard `update()` so that while a modal
is open, Escape closes it and every other keystroke-derived message
(command-line input/edit, F-key toggles, edit shortcuts, undo/redo) is
swallowed. A modal's own text fields keep working — they emit their own
messages, which are not in the blocked set. (#126)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The web save path discarded the chosen format version (`let _ = version`)
and `save_to_bytes` never applied it, so every browser download was
written at the document's default version (AC1032 / DWG 2018) regardless
of the format picked in the Save dialog. (#122)
Thread `version` into `save_to_bytes` and set `doc.version` there, the
byte-buffer counterpart to `save_as_version`. Saving as DWG 2013 now
writes an AC1027 file.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add an Uninstall button on installed external plugin cards that deletes
the package folder (effective next restart — the library stays resident
for the session). Reinstalling or upgrading via the marketplace now
clears any stale native library with a different name before writing the
new one, so the loader always picks the freshly installed binary.
Part of the #100 extensibility epic (phase 2).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a Plugin Manager marketplace that installs external add-ons from a
GitHub repo's Releases. A curated registry (plugins/registry.json in this
repo, extended by PR) lists discoverable plugins; the host fetches it,
shows each entry with a release dropdown, and on install downloads the
asset matching the user's platform plus plugin.toml into the plugins
folder (API-version checked first). Users can also link arbitrary
owner/repo manually. Linked repos persist in settings.
Desktop only (ureq + serde_json); the install path reuses the phase-2
loader. Part of the #100 extensibility epic (phase 2).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phase-2 groundwork: scan <config>/OpenCADStudio/plugins/<id>/plugin.toml,
parse the documented keys (no new TOML dep), check for a platform native
library beside it, and gate on the API version. The Plugin Manager gains
an "External (plugins folder)" section showing each package with a
status pill (Ready to load / API incompatible / No library). Discovery
only — actually loading the cdylib is the next step.
Part of the #100 extensibility epic (phase 2).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Points could only render as a 1px dot — no way to change PDMODE/PDSIZE.
- PDMODE / PDSIZE commands set the header value and rebuild the glyphs.
- DDPTYPE opens a Point Style dialog: a 5x4 grid of rendered glyphs
(shape x enclosure), a size field, and relative/absolute radios.
- Relative (<=0) PDSIZE now sizes from the current world-per-pixel so the
marker stays a roughly constant on-screen size across zoom, rebuilding
on zoom via the existing camera-generation cache key. Absolute (>0)
scales with the world as before. Switching to absolute with a zero size
seeds a positive value from the on-screen size so it stays representable.
Part of the #100 extensibility epic (point style surfaced in #106).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add-ons had no way to request a file picker — only the host's own Open
dialog. PluginFileDialog lets a plugin tool ask the host to open a native
picker; on selection the host dispatches "<command> <path>" back to the
plugin with original case preserved (bypassing the command-line
upper-casing that mangles case-sensitive paths on Linux/macOS). The demo
plugin gains an Import tool exercising it.
Part of the #100 extensibility epic (surfaced in #106).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The manager was a read-only inventory. Each plugin now has an
Enable/Disable toggle: a disabled plugin keeps its manifest listed but
drops its ribbon tab and command dispatch. The disabled-id set persists
in settings.txt (disabled_plugins=) and the ribbon rebuilds on toggle.
Part of the #100 extensibility epic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
In-canvas modals were locked to screen-centre. Add a green move-arrow
grip beside the ✕ that drags the dialog; positioning is an offset from
centre applied via asymmetric padding. The drag offset is clamped to the
window so a dialog stops at the edge instead of being squeezed, and the
offset resets when the dialog closes so each opens centred.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Tracking and polar pickup used the full crosshair-arm radius (60 px)
while OSNAP used the smaller ¼ radius, so the vector-intersection lock
grabbed over a much larger area than object snaps. Collapse to a single
osnap_radius_px shared by OSNAP, tracking, polar and extension; the
crosshair keeps following the cursor and the point snaps on contact.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When two tracking vectors were active, clicking placed a free point on
one of them instead of their crossing. otrack_snap now builds the active
rays (OTRACK rays from tracking points, plus POLAR rays from the last
point) and, when the cursor is near the crossing of two vectors from
different origins, locks onto that intersection. Covers OTRACK×OTRACK and
POLAR×OTRACK; single-vector alignment is unchanged when no crossing is
near.
Closes#111Closes#112
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A tracking point was created as soon as a snap point was highlighted,
so moving the mouse across the view spawned accidental tracking points.
Acquisition now requires the cursor to rest near the snap point for
250ms (wall-clock) rather than a fixed number of move events.
Closes#110
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
On the web, Save and Save As now open the in-app Save dialog (filename +
format) like the desktop, and the unsaved-changes prompt's Save routes
through it too — instead of an immediate rfd download. Confirm serializes
to bytes and downloads directly via a Blob + hidden `<a download>` click
(a user gesture), so there is no intermediate "click to download" link.
Replaces the rfd-based download_web / WebSaveResult round-trip with a
synchronous sys::download_bytes. Native save is unchanged.
Native + web build; 75 tests pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
With every dialog now an in-canvas modal, the per-dialog
`*_window: Option<window::Id>` fields are never set. Remove all 17
fields and their inits, trim `OsWindowClosed` to just the main window
(exit), drop the dialog branches from the window `title` closure, the
leftover `gain_focus` guards in the open handlers, and the now-unused
`iced::window` import in update.rs. Page-setup apply clears
`active_modal` instead of closing a window.
No behaviour change; native + web build, 75 tests pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Move the unsaved-changes prompt, the Save As browser and the
file-association prompt onto `active_modal` / `modal_content`, reusing
their existing open/close helpers (now just set/clear active_modal). The
daemon `view` no longer dispatches per window — every dialog is an
in-canvas modal, so the app is single-window on native and web alike.
`close_active_modal` handles their ✕: the unsaved prompt cancels the
pending close, the association prompt records that it was answered.
Native + web build; 75 tests pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Closing a dialog as an OS window used to discard a style editor's
un-applied (staged) changes and de-highlight the ribbon tool that
launched it. The in-canvas modal ✕ (CloseModal) lost that, so a Cancel
kept staged edits and left the button stuck active.
Add `close_active_modal`: it runs `style_stage_discard` for the style
editors and deactivates the matching ribbon tool by ModalKind, then
clears `active_modal`. CloseModal and the dialog close handlers route
through it. Apply still commits (re-baselined), so Apply-then-✕ keeps
the change.
Native + web build; 75 tests pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Move Table Style, MLeader Style and Dimension Style editors off OS
windows onto `active_modal` / `modal_content`, and turn the colour
picker into a nested modal that stacks over whichever dialog requested
it (`color_pick_target.is_some()` drives the overlay; CloseColorPicker
dismisses it). OpenColorWindow/ColorWindowPick no longer open/close an
OS window.
All thirteen former pop-up windows now render as in-canvas modals on
both native and web; only the native-only assoc/unsaved/save dialogs
remain as OS windows. Native + web build; 75 tests pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Move the Text Style and Multiline Style editors off OS windows onto the
shared `active_modal` / `modal_content` path. Their open handlers set
`active_modal` (keeping the existing buffer/stage setup); construction
moves from the daemon `view` dispatch into `modal_content`.
The remaining style editors (Table, MLeader, Dimension) still use OS
windows for now — they carry a nested colour picker that needs its own
handling. Native + web build; 75 tests pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Consolidate all migrated dialogs into a single `modal_content` builder
matched on `active_modal`, rendered by view_main. Move Layers, Page
Setup, Layout Manager and Plot Style off OS windows onto it (their
construction relocated from the daemon `view` dispatch, which now only
handles the not-yet-migrated dialogs). Each keeps a bounded size.
Open/close handlers set `active_modal` instead of opening/closing
windows; ToggleLayers toggles it.
Native + web build; 75 tests pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the per-dialog open flag with a single `active_modal:
Option<ModalKind>` and one `CloseModal` message. view_main matches it to
render the open dialog as an in-canvas modal.
Migrated off OS windows: About (content-sized), Shortcuts, Plugin
Manager and the Update notice (the latter three keep a bounded size via
a wrapping container, since their lists/scroll need defined bounds).
Native + web build; 75 tests pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Start Plan B — replace the daemon's pop-up OS windows with in-canvas
modal overlays so dialogs work identically on native (one main window)
and web (canvas only).
- New `ui::modal::modal(base, content, on_close)`: dims the view behind
a backdrop that blocks clicks but does NOT dismiss, frames the content,
and adds a top-right ✕ as the sole close affordance. Sizes to the
content (no `Fill`), so dialogs shrink to fit.
- view_main stacks the active modal on top, so both run paths show it.
- Migrate About as the pilot: `about_open` bool + AboutOpen/AboutClose
instead of an OS window; About's view shrinks to content.
Remaining dialogs migrate to the same pattern next. Native + 75 tests OK.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wire up save for the web build:
- `io::save_to_bytes`: serialize a document to a byte buffer (DXF via
write_to_vec, DWG via write_to_writer over a Cursor).
- `io::download_web`: browser save dialog → write bytes (triggers a
download).
- Save and Save As on wasm both go through a new `web_save` helper:
serialize the active tab and offer a download (no persistent path or
in-app save dialog on the web). Format follows the tab name's
extension, defaulting to DWG. Result reported via `WebSaveResult`.
Native build + 75 tests unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wire up file open for the web build:
- `io::pick_and_load_web`: browser file picker → read bytes → parse via
`load_bytes` → build caches, returning the same payload as the native
loader so it feeds the shared `Message::FileOpened` handler.
- `Message::OpenFile` cfg-dispatches: native picks a path then loads on a
worker thread; web picks + parses in one async step. The web branch also
sets `opening` (the handler drops results when no open is in progress).
- Disable the cosmic-text shaping / fallback paths on wasm: the web has no
system fonts, so cosmic-text panics with "no default font found". LFF
stroke fonts (embedded) still render; LFF-missing glyphs are skipped.
Native build + 75 tests unchanged. Hatches still don't draw on the web.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
With the wasm bundle building, get it rendering in a browser:
- Enable iced `webgl` so wgpu uses WebGL2 where WebGPU is absent
(e.g. Firefox by default), and `fira-sans` so UI text has an
embedded font (the web has no system fonts) — without it every
label was blank.
- Replace `std::time::Instant` with `iced::time::Instant` (web-safe);
`Instant::now()` panics ("time not implemented") on wasm and crashed
at boot via the command line.
- Skip the batched-hatch pipeline on wasm: its bind group needs
vertex-stage storage buffers, which WebGL2 lacks (validation error
on "New drawing"). Fields become Option; upload/draw guard on them.
Hatches don't draw on the web for now; everything else does.
- Fix the trunk feature flag (`data-cargo-no-default-features`) and
ignore the `dist/` build output.
Native build + 75 tests unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
First step toward a web build: the library now compiles for
wasm32-unknown-unknown with --no-default-features.
- Add a `solid3d` feature (default on) gating truck-meshalgo /
truck-shapeops / lzma-sys. These pull vtkio → xz2 → lzma-sys (C),
which cannot cross-compile to wasm. Without the feature the 3-D
solid path (tessellation, booleans, ACIS import) becomes no-ops;
2-D CAD is unaffected. Native builds keep the feature by default.
- Move native-only crates (ureq, open) to non-wasm target deps and
add cfg shims: update-check is a no-op on wasm, and a new `sys`
module provides open_url + handle_path (rfd FileHandle → PathBuf;
synthesizes a name-based path on the web).
Native build + all 75 tests unchanged. Browser runtime wiring
(entry point, trunk, real file I/O) is a follow-up.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Home tab was already titled "Draw"; rename the module to match:
folder modules/home → modules/draw, HomeModule → DrawModule, id "home"
→ "draw". The inner draw/ command folder is kept (modules::draw::draw).
Update build.rs PRIORITY ("home" → "draw") so the tab returns to the
front of the ribbon; registry.rs regenerates accordingly.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Move the 30 flat modules under src/scene/ into six folders by role:
convert (entity→geometry), text (fonts/shaping), model (render models),
pick (hit-test/selection/grips/xclip), view (camera/transform/render),
cache (block + property caches). pipeline/ is unchanged.
Full path migration — all `scene::X` references become
`scene::<group>::X`, no compatibility re-exports. render.rs items that
were `pub(super)` (parent was scene) become `pub(in crate::scene)` to
keep their original scene-wide visibility.
No behavior change; build and tests are green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Properties color dropdown stayed open after deselecting, reselecting, or
clicking into the viewport. refresh_properties carried color_picker_open across
rebuilds, so a selection change never closed it; and a viewport press starts a
box selection, so it only closed on the second click. Stop preserving the
picker state across rebuilds and dismiss both color dropdowns on the first
viewport press.
Fixes#104.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Make angle display, sign and the polar guide reference part of the
common infrastructure instead of per-command logic:
- DynSpec.ref_point now drives the polar guide's reference direction, so
the arc and value box can sweep from a command-defined reference (not
just +X). LINE etc. keep +X by leaving it None.
- `dyn_display_angle_deg` is the single rule for how an angle reads: the
unsigned magnitude of the short angle, so clockwise (cursor on the CW
side of the reference) shows positive. Used by the generic value box,
ROTATE and ARC.
- A typed angle committed via on_text_input is re-signed by the cursor's
side relative to the reference (`dyn_sign_angle_text`), so commands
receive an already-signed value and don't each implement it.
ROTATE: drop its own sweep arc (the polar guide draws it now, anchored at
the centre and swept from the reference); the rotation commits in the
dragged direction whether picked, typed or accepted with Enter.
ARC SCA/CSA: reference the start angle so the arc, sign and readout are
all relative to it.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- ARC: per-step DynSpec for the typed-value modes — included-angle (SCA,
CSA), chord length (SCL, CSL) and radius (SER) show their box and route
the typed value through on_text_input via a new dyn_commit_as_text()
hook; the angle box previews the live span. Point/3P/SCE steps stay on
the legacy polar path.
- ARC + ellipse-arc: derive the previewed sweep direction from the swept
angle with a ~10° tolerance and advance the reference only on a clear
move, so small cursor jitter no longer flips the arc. Ellipse uses the
parametric angle so a flat ellipse isn't sticky.
- Ellipse arc: the start-angle step now shows a line from the centre to
the cursor (it only picks the start angle) instead of a misleading arc.
- Ellipse arc: use the same minor-axis direction (Z x major) as the
rendered entity so the committed arc matches the preview instead of
mirroring across the major axis.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Extend the DynSpec coverage and add the guides those commands need:
- Rectangle: two-corner enters width/height as unsigned magnitudes (the
sign follows the cursor side, like the angle entry), drawn with the
rectangle as the guide; rotated rectangle's height is a single distance
measured square to the base edge (dimension line offset off the edge
with extension lines); centre rectangle shows half-width/half-height.
- Polygon: inscribed/circumscribed vertex step shows radius + rotation.
- Ellipse: minor-axis step (all three modes) measures the half-length
perpendicular to the major axis and draws that semi-axis from the
centre; angle steps anchor their arc at the centre.
Adds DynGuide::Perp / PerpDim (perpendicular measure to a reference line)
and DynSpec::ref_point, plus Width/Height role handling (unsigned display,
cursor-signed commit) and diameter value scaling.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Introduce a per-command, per-step dynamic-input description so each step
shows the value boxes and guide geometry that make sense for it, instead
of a one-size-fits-all distance/angle pair.
- command: add DynSpec{anchor, fields(role+label), guide}, DynRole
(X/Y/Z/Distance/Angle/Radius/Diameter/Width/Height/Factor/Count with
label + value scaling), DynGuide(None/Polar/AxisDelta/Radius/RectSides)
and DynAnchor. New dyn_spec() trait method defaults to None so commands
on the legacy dyn_field() path are unchanged.
- overlay: redraw the dynamic-input layer by guide — polar reference line
+ angle arc, single radius line, dotted axis legs, or a cursor row —
and place each box by its role. Angle reads as the unsigned magnitude
of the short arc; a typed polar angle takes the sign of the cursor side.
- app: route fields/guide/anchor through the tab; resolve points from the
step anchor and de-scale typed values by role (typed diameter -> radius).
- circle: CR shows a radius value on one line to the cursor; CD shows the
diameter (twice the cursor radius). No angle arc on radius steps.
- pline/mline: keep polar dynamic input on vertex steps that also accept
keyword letters via point_step_accepts_keywords().
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
While DYN fields were showing, a leading @ or # landed in the command
line but the following digits were captured by the focused DYN field,
splitting the value so the prefix never resolved. Treat a leading @ / #
as an escape: route the whole coordinate (digits and separators) to the
command line so the relative (@) / absolute (#) prefix works the same
whether DYN is on or off.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A bare command-line coordinate followed the DYN setting, so with dynamic
input on it was treated as relative to the last point. AutoCAD keeps the
command line absolute regardless of DYN; only the DYN tooltip defaults to
relative. Resolve a bare command-line value as absolute and reserve
relative for the explicit `@` prefix (`#` still forces absolute). The DYN
tooltip path keeps its own relative-by-default behaviour.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Read-only inventory of the add-ons compiled into the build — name, version,
id, API level, description, command prefixes — built from the extracted
`ocs_plugin_api` manifest. Opened with the `PLUGINS` / `PLUGINMANAGER` command
or the Start-page "Plugins" button.
Adds `installed_manifests()` to the plugin registry and wires the window the
same way as the existing About / Shortcuts windows.
Part of #100 (phase-1 plugin manager UI stub).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Unfocused keyboard input routes through the global subscription: typed
characters reach the command line, but Enter went to CommandFinalize, which
only forwarded the buffer to CommandSubmit while a command was already active.
At startup (before the window grabs focus) typing a command name and pressing
Enter did nothing until the user clicked into the command line. Forward any
non-empty buffer to CommandSubmit regardless of active-command state.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the bespoke colour pickers with the shared color_selector / color_list
widget so colour selection is identical everywhere:
- left properties panel (selected entities' colour)
- ribbon Home-tab colour dropdown
- layer manager per-layer colour
Each routes its "More…" entry to the standalone Select Color window via a
ColorPickTarget (Properties / Ribbon / Layer). Extracted color_list as the
shared popup content; the colour selector button now fills its container.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The colour selector popup now lists only the named colours (ByLayer,
ByBlock, ACI 1-9) plus a "More…" entry. "More…" opens a standalone
"Select Color" window with the full 256-colour grid; picking there applies
to the originating field (dimension/multileader/table style) and closes
the window. Routed via a ColorPickTarget so one window serves every field.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>