Commit Graph
424 Commits
Author SHA1 Message Date
Sebastian ed3e064a47 feat(plugins): include rustc version in plugin compatibility gate
OpenCADStudio/plugins#901

- Record the host's rustc --version output in the embedded version_info.json.
- Add rustc_version/rustc_declared/rustc_compatible to plugin manifests and
  release metadata, mirroring the existing acadrust gate.
- Reject API v4+ plugins whose declared rustc differs from the host, with a
  clear one-line error message.
- Propagate the rustc gate through the marketplace, install flow, and UI.
- Update plugin architecture docs to describe the new ABI metadata.
2026-08-29 10:34:47 +02:00
Hakan Seven 3024869de0 fix(i18n): localize linetype inheritance 2026-08-29 10:10:39 +03:00
Hakan Seven 8dd3eb897b fix(properties): embed scrollbar lane 2026-08-28 21:05:26 +03:00
Hakan Seven 6da2685e13 chore(properties): integrate main 2026-08-28 19:48:12 +03:00
Hakan Seven 50e5dcaaac fix(polyline): correct 3D smoothing 2026-08-28 19:46:13 +03:00
Hakan Seven ff1f873633 fix(mleader): repair creation and editing 2026-08-28 19:36:48 +03:00
ramox81 1cc7d3cb67 Prevent properties scrollbar overlap 2026-08-28 11:54:37 +03:00
ramox81 3ca959a34b Fix 3D polyline behavior and properties 2026-08-28 11:21:03 +03:00
Karim Jerbi cc430cdbe9 perf(ui): cache svg::Handle in themed* icon functions
The 9 themed* functions that take &'static [u8] artwork each built a
fresh svg::Handle::from_memory(bytes) on every call, with 113
icons::themed call sites per frame across ribbon/statusbar/popups.
Add a thread_local THEMED_CACHE keyed by the bytes' (address, length)
and a single themed_handle(bytes) helper; the 9 themed* functions all
go through it. The 13 wrapper functions (themed_arrow_*, themed_home,
themed_undo, themed_redo, themed_check_cell, ...) delegate to those 9
and pick up the cache transparently, so all 22 themed* functions (and
the 113 call sites) are deduped.

The cache key is (address, length) -- not pointer alone -- to match the
existing SEMANTIC_CACHE belt-and-braces pattern and guard against any
future call site that passes a sub-slice of a static (e.g. &BYTES[1..]),
which would otherwise alias the wrong handle. Every include_bytes!
produces a unique static slice today, so the length is defensive.

Construction is amortised to once per artwork per process. No semantic
change; visual output is byte-identical.

Measured performance gains (integrated
themed_cache_speedup_over_uncached_handle_creation bench, 20_000
iterations each, same machine):

  |                | dev profile   | release profile |
  |----------------|---------------|-----------------|
  | Before         | 235 ns/iter   | 26 ns/iter      |
  | After (cached) |  78 ns/iter   |  9 ns/iter      |
  | Cold miss      | 900 ns        | 100 ns          |
  | Speedup        | 3.0x          | 2.9x            |

Release-mode warm path: 9 ns/iter. At 60 fps with up to 113 themed*
calls per frame, that is ~1 us/frame of handle work -- a rounding
error against a 16.6 ms frame budget. The cold-miss cost (one parse
+ one insert, 100 ns release) is the one-time price per new artwork
and is amortised to negligible per-frame overhead (it happens at
most once per artwork per process). The speedup is stable at ~3x
across dev and release because the ratio of work is the same:
hashmap-get + Handle clone vs. a full SVG parse + allocation.

test ui::icons::themed_cache_tests::themed_cache_returns_same_handle_for_same_bytes
test ui::icons::themed_cache_tests::themed_cache_caches_distinct_bytes_separately
test ui::icons::themed_cache_tests::themed_cache_speedup_over_uncached_handle_creation
2026-08-28 06:05:31 +01:00
ramox81 cce25be83a Complete multileader creation and property behavior 2026-08-27 17:35:47 +03:00
Karim Jerbi 513ab958a8 perf(grid): cache overlay geometry behind a params-keyed key
Introduce a cache for the grid overlay to prevent redundant geometry
reconstruction on every UI redraw. Extract the pure projection logic
from draw_grid into a renderer-free grid_segments function, and add
GridKey and GridCanvasState to memoize the resulting Geometry.

The cache key includes all per-pane GridParams and the overlay bounds,
preventing stale geometry when camera changes within the same canvas
size. On a params-key miss, the iced cache is cleared before redrawing
to bypass its bounds-only reuse.

Tests: 8 new unit tests cover key invalidation and state handling.
Benchmarks: uncached geometry ~32 us/frame vs cached decision ~90 ns,
a >99% reduction. Verified: 472 passed / 1 pre-existing failure / 4
ignored.

Tests:
  ui::overlay::grid_key_tests::grid_key_matches_identical_params
  ui::overlay::grid_key_tests::grid_key_invalidates_on_changed_fields
  ui::overlay::grid_key_tests::grid_key_invalidates_when_any_pane_changes
  ui::overlay::grid_key_tests::should_reuse_empty
  ui::overlay::grid_key_tests::should_reuse_equal
  ui::overlay::grid_key_tests::should_reuse_changed
  ui::overlay::grid_canvas_state_tests::default_state_has_no_cached_key
  ui::overlay::grid_canvas_state_tests::stored_key_is_recognized_by_should_reuse
2026-08-26 23:53:31 +01: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
ramox81 de294447a9 Complete geometric tolerance workflow and properties 2026-08-26 13:54:20 +03:00
Hakan Seven 8c61d89b1e fix(layout): complete paperspace integration
Apply each layout's embedded page setup and plot style consistently across display, PDF, printing, and multi-layout output.\n\nRefs #844
2026-08-26 08:58:44 +03:00
Karim Jerbi 9c8f841f71 fix(insert): capped interactive block search and CLIPROMPTLINES
INSERT enumerated all block names in a single prompt string, causing unbounded overlay growth and full-screen occlusion in drawings with many blocks. Replace prompt enumeration with a ranked, incrementally filtered suggestion model: BlockPicker maintains precomputed lower/upper caches and usage ranks (MRU/frequency), filters via O(n) substring scan and partial sorting (select_nth_unstable) to O(k), bounded to CLIPROMPTLINES.

Introduce CLIPROMPTLINES (Integer 0-50, Registry, default 3) for the number of temporary prompt lines above the command window; persist in UserSettings, expose via SETVAR and direct entry, enforce in CommandLine::view. Suggestion count is in direct relation to CLIPROMPTLINES (limit = clip, capped to 50).

Make search live without requiring Enter: add CadCommand::on_live_input, implement for INSERT/MINSERT via picker.set_needle, and wire to command-line input paths (Message::CommandInput, on_command_append_char, on_command_backspace). Updates use upper/lower caches and partial sort for <0.2ms per keystroke on 10k blocks, with debounced block-usage persistence (MRU cap 20, freq cap 200) to avoid I/O thrash. Correct tri_count test helper to use display_from_solid.
2026-08-25 23:18:05 +01:00
Hakan Seven 450135960c fix(ui): widen linetype dropdown menus
Refs #782
2026-08-25 23:06:10 +03:00
Hakan Seven 1e1eeea527 feat(plugin): gate dependency mismatches 2026-08-25 08:12:13 +03:00
Sebastian 3d35919656 feat(plugin): gate marketplace and load on acadrust source
- Add host_acadrust_source and source-hash comparison helpers to ocs_plugin_api.

- Track acadrust_source and acadrust_declared in ExternalPlugin/ReleaseInfo.

- Apply repo-level policy: if any release in a repo declares acadrust_source, undeclared v4+ releases from that repo are incompatible.

- Preserve API v2/v3 backwards compatibility: legacy undeclared releases stay installable.

- Update Plugin Manager UI to only offer compatible releases and show 'acadrust mismatch' status.

- Drop acadrust_version from gate logic; only acadrust_source is required.
2026-08-24 19:01:06 +02:00
ramox81 80d7a8741e Route dimension color fields correctly 2026-08-24 15:32:38 +03:00
Karim Jerbi 7215faae44 feat(properties): make inherited properties editable, align dropdowns, and switch transparency to EditChoice
Enable in-place editing of inherited properties in the Properties panel:
- Transparency: Switch to PropValue::EditChoice with ByLayer and ByBlock presets to allow direct typing of any numeric percentage value (0..90%) without combo_box autocomplete interference.
- Select-all-on-click: Integrate EditChoice text inputs into build_field_key_map with dedicated widget IDs, preserving click-to-select-all across editable fields.
- Material: Provide bounded preset choices (ByLayer, ByBlock, Global, and active custom material) to avoid scanning large drawing dictionaries.
- Plot style: Implement mode-aware behavior with interactive Choice in STB mode and ReadOnlyWithTooltip in CTB mode.
- Read-only tooltips: Introduce PropValue::ReadOnlyWithTooltip to provide contextual explanations for locked fields while preserving mouse text selection and clipboard copying.
- Dropdown alignment & unified borders: Fix EditChoice internal input/caret background clipping so the container's 1px neutral/active border surrounds the entire control identically to Lineweight and text inputs.
- Document defaults & aggregation: Synchronize document header defaults when editing with No selection, and aggregate heterogeneous multi-selections (*VARIES*).
2026-08-23 23:20:34 +01:00
ramox81 c5fe70627a Honor point display geometry and sizing 2026-08-22 17:18:30 +03:00
Hakan Seven bd5f9436ea Merge pull request #852 from ramox81/codex/complete-hatch-workflow 2026-08-21 21:02:14 +03:00
Hakan Seven 6c4f769d61 Merge pull request #842 from gianlucafiore/feat/non-printing-layer 2026-08-21 18:58:24 +03:00
Hakan Seven 77e1f1b876 fix(plot): respect non-printing layers
Filter nested block, attribute, table, hatch, and wipeout geometry during export while preserving canvas visibility.
2026-08-21 18:57:23 +03:00
ramox81 f83037271c Complete hatch creation and editing workflow 2026-08-21 12:57:19 +03:00
gianlucafiore 2208076e50 feat(plotstyle): show layer usage by ACI 2026-08-20 23:55:44 -03:00
gianlucafiore eb5cbaeca0 feat(layers): add non-printing layer support 2026-08-20 20:37:27 -03:00
Hakan Seven 44efb8fd53 feat(frame): complete frame controls
Preserve frame geometry for selection while separating viewport and plot visibility across supported reference entities.

Refs #642
2026-08-20 23:37:02 +03:00
Hakan Seven a64a7f7889 fix(ribbon): preserve forced panel levels 2026-08-20 19:58:07 +03:00
Hakan Seven 5e62da464e fix(properties): preserve literal text
Separate numeric fields from names and content so only numeric property values are evaluated as expressions.

Refs #809
2026-08-20 14:58:12 +03:00
Hakan Seven 1c408e3a73 feat(spline): improve fit and CV editing
Keep planar conversions kernel-backed and leave unsupported 3D, CV-to-fit, and closed rebuilds unavailable instead of changing curve data.\n\nRefs #815
2026-08-20 00:18:45 +03:00
Hakan Seven 0f250bb485 feat(ellipse): add complete grip editing 2026-08-19 23:48:13 +03:00
ramox81 0d37d3ff78 Improve spline fit and control vertex grips 2026-08-19 11:02:32 +03:00
ramox81 283e6db324 feat(ellipse): add editable arc endpoint grips 2026-08-19 10:02:38 +03:00
Karim Jerbi 32c3844526 perf(ribbon): stop building and laying out 5 panel renderings per frame
Each ribbon panel was rendered five ways every frame so CollapsePanels could
roll back to any density at layout time: full, compact, button, tight and a
full-size flyout that duplicated the full rendering.
The layout pass ran four full measure-layouts per panel even in forced modes,
and the degradation decision was an untested u8 inline in Ribbon::view.

Now the choice is a pure, unit-tested decide_levels() whose levels are a typed
Level enum (Full/Compact/Collapsed/Tight). Panel carries its four renderings
as elements: [Element; 4] indexed by Level, with a slot(i, level) helper for
the tree stride. Forced modes (Full/Compact/Collapsed) measure nothing at all
and derive the row height from the placed panel. The open flyout draws the
panel's own `full` rendering inside a self-drawn box (identical weakest-bg +
1px neutral-border to the container it replaced), so no duplicate
render_group is built per panel per frame. Level, Panel, Widths, CollapsePanels
and decide_levels are pub(crate); CollapseMode stays pub (re-exported).

Tests (collapse.rs tests module, added RED first):
- forced_modes_pin_every_panel
- auto_huge_width_keeps_everything_full
- auto_degrades_from_the_right_one_panel_at_a_time
- auto_stops_degrading_once_the_row_fits
- auto_escalates_a_single_panel_past_collapsed_to_tight
- auto_cascade_drops_every_collapsed_panel_to_tight
- auto_runs_the_compact_phase_across_the_row_first
- no_panels_stays_empty
- single_panel_walks_the_whole_ladder
- zero_widths_never_overflow
- endless_row_degrades_everything_to_tight
- unbounded_row_keeps_everything_full
- nan_budget_degrades_both_phases_but_no_tight_cascade
- exact_fit_at_full_stays_full
- matches_the_pre_refactor_algorithm: byte-equivalence sweep against the
  verbatim pre-refactor u8 algorithm (280 width/budget combos incl. the
  degenerate 0/0/inf/NaN inputs), proving the decision logic is unchanged.

Measured with the committed #[ignore]d benchmark
(`cargo test --lib --release -- --ignored --nocapture bench_ribbon_view_construction`)
run as one instrument across pre-change / this change: Ribbon::view()
construction ~676 -> ~425 us/frame (~37% of view construction saved); forced
modes run 1 layout call per panel per frame instead of 5.

Verification: cargo build clean; cargo test --lib = 382 passed (367 baseline
+ 15 new ribbon tests) / 2 known pre-existing failures unchanged
(save_then_open_round_trips, tilted_ucs_places_planar_entities_with_the_plane_normal).
2026-08-16 10:23:44 +01: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
Karim Jerbi b4da342f0e refactor(ribbon): derive the Block Palette button highlight from app state
The ribbon kept its own \show_block_palette\ copy that the app had to
manually sync at every mutation point (the BLOCKPALETTE command and the
dock Close handler). Drop the copy and thread the app's authoritative
\show_block_palette\ into \Ribbon::view\ at render time, so the button
highlight can't drift from the panel's true visibility — those two
handlers are now mutate-points only and the ribbon view is the single
place that reads the state back.
2026-08-15 14:42:08 +01:00
Karim Jerbi c3e3132b39 refactor(props): key active fields by a typed enum, not a sentinel string
The \\\x01attr\\x01…\ sentinel-prefixed string was used to tell geometry
fields (\&'static str\ names) apart from block attribute rows (runtime
tags) in \ctive_field\ and \dit_buf\. Replace it with a \FieldKey\
enum (\Geom(&'static str)\ / \Attr(String)\), so the two namespaces can't
collide by construction: \dit_buf\ is now \HashMap<FieldKey, String>\,
\ctive_field\ is \Option<FieldKey>\, and the sentinel decoding in
\ctive_key_id\ is replaced by \FieldKey::widget_id\.

While touching the key plumbing, replace \prop_field_key_for_id\'s
per-event scan of every section x prop (with a \String\ format per
candidate) with an O(1) precomputed \HashMap<iced::widget::Id, FieldKey>\
(\ield_key_by_id\), built once in \
efresh_properties\ alongside the
sections — and rebuilt after \make_sections_read_only\ so rows demoted
to read-only stop mapping. \PropSyncActive\ now does a single map lookup
per event.
2026-08-15 14:29:20 +01:00
Karim Jerbi 41223f09d7 feat(plot): read-only rows use the shared selectable read-only field 2026-08-15 11:59:45 +01:00
Karim Jerbi 8e6b60910c feat(style): align mleader read-only field width with editable fields 2026-08-15 11:59:45 +01:00
Karim Jerbi d5b5f0781f feat(style): dim style read-only fields use the shared read-only field 2026-08-15 11:59:45 +01:00
Karim Jerbi 71d4eb96e5 feat(style): mleader style read-only rows use the shared read-only field 2026-08-15 11:59:45 +01:00
Karim Jerbi 61c3497d7f feat(text-style): read-only rows use the shared selectable read-only field 2026-08-15 11:59:45 +01:00
Karim Jerbi f2c4d389db feat(props): read-only rows use the shared selectable read-only field 2026-08-15 11:59:44 +01:00
Karim Jerbi f2b375ab24 feat(read-only): add shared selectable read-only field helper 2026-08-15 11:59:44 +01:00
Karim Jerbi fbab4c4934 feat(ribbon): Block Palette button reflects panel open state
The Insert tab's Block Palette button now behaves like the View tab toggles
(UCS Icon, View Cube, Ortho, Properties, File Tabs, Layout Tabs): it stays
highlighted while the block palette panel is open and clears when closed.

The Ribbon gains a show_block_palette flag with a set_block_palette setter,
fed into ToggleState and mapped in is_active_tool for the BLOCKPALETTE tool
id. show_block_palette on the app remains the single source of truth; both
mutation paths now mirror it onto the ribbon:

- BLOCKPALETTE / BLOCKSPALETTE command toggles ribbon state alongside the
  panel (src/app/commands/blocks.rs).
- Closing the panel via the dock rail close button clears the ribbon state,
  matching how Close(Properties) already syncs (src/app/update/dialog.rs).
2026-08-14 23:33:45 +01:00
Karim Jerbi 28e5deb222 fix(dock): don't panic clamping panel width when window is minimized 2026-08-14 23:06:53 +01:00
Karim Jerbi df940ad58e fix(dock): size drag preview by visible panels, not stack size
The drag preview divided the edge height by the raw panel count on the target
side. Closed panels keep their stack slot, so a hidden panel (e.g. the block
palette tucked away while Properties stays open) split the ghost and the drop
index as if it were rendering — leaving a half-height preview on that edge.
Size slots from the panels actually rendered on the edge instead, via a shared
visibility helper used by both the drop-index math and the ghost/edge-tint
geometry.
2026-08-14 22:24:57 +01:00
Karim Jerbi 9f68034a60 feat(props): select whole value when a property field is clicked
Clicking an editable property value field now selects its entire value instead
of only placing the caret, so typing immediately replaces it. The click is
observed through a global left-press subscription (PropPointerPressed); a focus
sweep reports which widget took focus and PropSyncActive maps it back to the
active property row, then issues a select-all operation only when focus landed
on a different field.

The active row stays highlighted while its text input holds keyboard focus and
clears when focus moves elsewhere.
2026-08-14 21:21:40 +01:00
Karim Jerbi bd33600ee7 fix(dock): single-panel drag preview is one full-height slot
drop_index previously mapped a pointer position inside a lone full-height panel
to a top/bottom insertion split, offering an impossible drop. Clamp to the
single slot when the edge holds one panel, and cover the multi-slot mapping with
tests.
2026-08-14 21:21:40 +01:00