Commit Graph
372 Commits
Author SHA1 Message Date
Hakan SevenandClaude Opus 4.8 195415af84 feat(modal): drag dialogs by a title-bar grip, clamped to the window
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 <[email protected]>
2026-06-17 09:59:17 +03:00
Hakan SevenandClaude Opus 4.8 ec3161f7f7 fix(snap): unify snap aperture so OTRACK catch matches OSNAP
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 <[email protected]>
2026-06-17 05:25:11 +03:00
Hakan SevenandClaude Opus 4.8 58e4d696ba feat(otrack): lock onto the intersection of two active vectors
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 #111
Closes #112

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-17 05:01:41 +03:00
Hakan SevenandClaude Opus 4.8 ca8d249173 fix(otrack): add 250ms dwell timeout before acquiring a tracking point
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 <[email protected]>
2026-06-17 04:59:06 +03:00
Hakan SevenandClaude Opus 4.8 17f7924e77 feat(ui): OCS Web button, prefilled feedback, drop Start About button
- Add an "OCS Web" button (bright ribbon-blue, desktop only) to the Start
  page that opens the web build at hakanseven12.github.io/OpenCADStudio.
- Send Feedback now opens the GitHub issue with the body pre-filled:
  app version + platform (OS/arch on desktop, browser user-agent on web),
  via sys::platform_info + sys::percent_encode.
- Remove the redundant About button from the Start page (ABOUT command /
  menu still opens it).

Native + web build; 75 tests pass.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-17 01:34:13 +03:00
Hakan SevenandClaude Opus 4.8 fefae274d4 feat(wasm): web Save/Save As go through the dialog with a direct download
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 <[email protected]>
2026-06-17 00:52:20 +03:00
Hakan SevenandClaude Opus 4.8 93d136c5db fix: Donate button (Start tab gate + web window.open)
- Add DONATE to the Start-tab command allowlist; it was blocked there
  with "No drawing open" while REPORT/CHANGELOG/ABOUT were allowed, so
  the Donate button did nothing on the welcome screen.
- Implement sys::open_url on wasm via web_sys window.open(_blank) — it
  was a no-op stub, so Donate, Release Notes and Send Feedback opened
  nothing on the web. The click is a user gesture, so the pop-up blocker
  allows it.

Native + web build.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-17 00:27:33 +03:00
Hakan SevenandClaude Opus 4.8 3dd1276b6a refactor(ui): remove dead OS-window machinery after Plan B
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 <[email protected]>
2026-06-17 00:09:04 +03:00
Hakan SevenandClaude Opus 4.8 94b906d95b feat(ui): migrate the last dialogs to modals — fully single-window (Plan B)
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 <[email protected]>
2026-06-16 23:59:38 +03:00
Hakan SevenandClaude Opus 4.8 c24c6885d0 fix(ui): modal close discards staged edits and clears the ribbon tool
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 <[email protected]>
2026-06-16 23:52:21 +03:00
Hakan SevenandClaude Opus 4.8 6c593eeb1c fix(ui): widen Text Style modal to fit the TTF font panel
The Text Style editor gained a system-font (TTF) column in the Plan B /
web-fonts work, so its content no longer fits the old 620px width. Size
the modal to 860×480 so all panels show.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-16 23:45:40 +03:00
Hakan SevenandClaude Opus 4.8 28232f1247 feat(ui): migrate style editors + nested colour picker (Plan B)
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 <[email protected]>
2026-06-16 23:42:14 +03:00
Hakan SevenandClaude Opus 4.8 7cd938443e feat(ui): migrate Text Style and Multiline Style to modals (Plan B)
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 <[email protected]>
2026-06-16 23:29:50 +03:00
Hakan SevenandClaude Opus 4.8 b21d10f6e8 feat(ui): migrate 4 more dialogs to in-canvas modals (Plan B)
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 <[email protected]>
2026-06-16 23:22:54 +03:00
Hakan SevenandClaude Opus 4.8 6a3663b3f1 feat(ui): unified modal dispatch; migrate 4 dialogs (Plan B)
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 <[email protected]>
2026-06-16 19:07:25 +03:00
Hakan SevenandClaude Opus 4.8 e6213eafbb feat(ui): shared in-canvas modal overlay; migrate About (Plan B)
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 <[email protected]>
2026-06-16 18:59:13 +03:00
Hakan SevenandClaude Opus 4.8 9d378a0533 feat(wasm): save files from the browser
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 <[email protected]>
2026-06-16 18:20:22 +03:00
Hakan Seven 1ab892c635 Merge pull request #105 from KarimJerbi/rectang-suppress-ortho
Temporarily suppress Ortho mode during RECTANG commands
2026-06-16 18:17:18 +03:00
Hakan SevenandClaude Opus 4.8 6996343e84 feat(wasm): open files in the browser
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 <[email protected]>
2026-06-16 18:08:57 +03:00
Hakan SevenandClaude Opus 4.8 c49ad887b8 fix(wasm): make the web build actually run in the browser
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 <[email protected]>
2026-06-16 17:53:56 +03:00
Hakan SevenandClaude Opus 4.8 157d54a4d4 feat(wasm): single-window web entry + trunk harness (issue #45)
The binary now compiles for wasm32 as a runnable web target:

- Split the main-window render into `view_main`, bypassing the
  multi-window id dispatch the browser cannot use.
- Add `run_web` / `boot_web`: a single-window `iced::application`
  entry for wasm (the browser canvas is the only window). Native
  keeps the multi-window `iced::daemon` via cfg-split in `main`.
- Add a `console_error_panic_hook` (wasm-only) for readable panics.
- Add `index.html` + `Trunk.toml` so `trunk serve` builds the wasm
  bundle (no-default-features → no solid3d) with COOP/COEP headers.

Native build and all 75 tests unchanged. Browser runtime (rayon
threads, canvas attach, real file I/O) is the next iteration.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-16 13:16:35 +03:00
Hakan SevenandClaude Opus 4.8 b2e20c95fe feat(wasm): compile the library for wasm32 (issue #45 groundwork)
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 <[email protected]>
2026-06-16 13:01:19 +03:00
Karim Jerbi a431e02378 Temporarily suppress Ortho mode during RECTANG commands 2026-06-16 09:53:32 +01:00
Hakan SevenandClaude Opus 4.8 3666c7fa69 refactor(modules): rename home module to draw, restore first-tab order
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 <[email protected]>
2026-06-16 11:49:14 +03:00
Hakan SevenandClaude Opus 4.8 c6152ef705 refactor(scene): rename model_solid to solid_model
Match the `*_model` naming of the sibling render models (wire_model,
hatch_model, mesh_model, image_model). Also realign the related Scene
API for consistency: model_solids → solid_models, add_model_solid →
add_solid_model, register_model_solid → register_solid_model.

No behavior change; build and tests are green.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-16 11:39:31 +03:00
Hakan SevenandClaude Opus 4.8 7fe7e0230d refactor(scene): group modules into role-based subfolders
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 <[email protected]>
2026-06-16 11:33:46 +03:00
Hakan SevenandClaude Opus 4.8 cd048165a9 feat(fonts): system TrueType support with shaping and fallback
Add a TrueType/OpenType render path alongside the LFF stroke engine so
users can pick fonts installed on their machine for text and MText.

- Discover system families via fontdb (sysfont) and list them in the
  text-style picker.
- Extract glyph outlines with ttf-parser, flattened into wire strokes
  normalized to the same 9-unit cap-height space as LFF glyphs.
- Shape runs with cosmic-text (ligatures, Arabic joining, kerning, bidi)
  plus automatic font fallback: a glyph missing from the chosen family
  is taken from another installed font.
- Unify both sources behind a single Face type; a shared run tokenizer
  drives the stroke (per-glyph) and shaped (TTF) renderers identically.
- Stroke fonts now fall back to a system TTF (cosmic-text picks it) for
  characters no LFF font covers, replacing the unicode/iso3098 chain.

Also trim the embedded LFF set from 47 to 26, keeping the AutoCAD-SHX
equivalents and dropping LibreCAD-only fonts now covered by TrueType.

LFF builtins keep priority over same-named system fonts so DXF text
renders identically across machines.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-16 11:18:17 +03:00
Hakan SevenandClaude Opus 4.8 6376080336 fix(properties): close the color dropdown on selection change and viewport click
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 <[email protected]>
2026-06-16 04:02:46 +03:00
Hakan SevenandClaude Opus 4.8 e2be8197b4 feat(stretch): live ghost preview while dragging the new point
STRETCH now shows a live ghost where vertices inside the crossing window
follow the cursor and the rest stay anchored, instead of only a rubber-band
line. Add WireModel::stretched and pass the selection's wires into the
command.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-15 23:43:07 +03:00
Hakan SevenandClaude Opus 4.8 e3e69ec152 feat(mirror): add erase-source prompt (keep mirrored copy by default)
MIRROR now asks "Erase source objects? [Yes/No] <No>" after the second
mirror-line point. No (default) keeps the original and adds a mirrored
copy; Yes flips the original in place. Make the BatchCopy command-line
message label-aware and singular/plural so MIRROR reports "1 copy".

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-15 23:42:54 +03:00
Hakan SevenandClaude Opus 4.8 1a9844b134 feat(dynamic-input): shared angle reference + sign handling
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 <[email protected]>
2026-06-15 23:03:08 +03:00
Hakan SevenandClaude Opus 4.8 5128d94b2b feat(dynamic-input): ARC specs + ellipse-arc preview fixes
- 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 <[email protected]>
2026-06-15 22:03:04 +03:00
Hakan SevenandClaude Opus 4.8 e31dbcb5c4 feat(dynamic-input): per-step specs for rectangle, polygon and ellipse
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 <[email protected]>
2026-06-15 18:46:34 +03:00
Hakan SevenandClaude Opus 4.8 b0ee1c39bd feat(dynamic-input): per-step DynSpec framework + guide overhaul
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 <[email protected]>
2026-06-15 17:12:08 +03:00
Hakan Seven 18f1688486 Revert "fix(input): honor @ / # coordinate prefixes during dynamic input"
This reverts commit df616482e2.
2026-06-15 13:46:27 +03:00
Hakan SevenandClaude Opus 4.8 df616482e2 fix(input): honor @ / # coordinate prefixes during dynamic input
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 <[email protected]>
2026-06-15 13:31:25 +03:00
Hakan SevenandClaude Opus 4.8 fbac39462e fix(input): command line coordinates are always absolute
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 <[email protected]>
2026-06-15 13:27:30 +03:00
Hakan SevenandClaude Opus 4.8 ec865a95aa feat(plugin): add a Plugin Manager window listing installed add-ons
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 <[email protected]>
2026-06-15 01:46:09 +03:00
Hakan SevenandClaude Opus 4.8 3522637f65 fix(start): enable Send Feedback / Release Notes on the start tab
The start (welcome) tab blocks any command that isn't NEW/OPEN/EXIT/QUIT,
so the Contribute and Release Notes buttons did nothing but print
"No drawing open". REPORT and CHANGELOG (and ABOUT) only open a URL or a
dialog and need no drawing, so allow them through.

Also rename the "Contribute" button to "Send Feedback" (#44) and update
the REPORT status line to match.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-15 01:12:29 +03:00
Hakan SevenandClaude Opus 4.8 036ac7dd86 fix(command): submit typed command on Enter without focusing the input (#99)
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 <[email protected]>
2026-06-15 00:35:03 +03:00
Hakan SevenandClaude Opus 4.8 9d823363f1 feat(scene): render 3D surface entities (loft/sweep/extrude/etc.)
ACAD_SURFACE entities now load and render. Their NURBS faces are
tessellated by building a truck BSplineSurface from the ACIS `spline-surface`
control net and knot vectors, then sampling its parametric grid — reusing the
same NURBS kernel the Model tab already builds on. Analytic faces
(plane/cone/sphere/torus) continue through the existing tessellator.

- New spline_tess.rs: parse the ACIS `nubs` block (degrees, knots with the
  clamped end-multiplicity fix, control grid) into a truck BSplineSurface and
  mesh it.
- tessellate_surface + Surface wired through the mesh-build, fallback-wire,
  and entity-classification paths.
- Bump acadrust to the commit that adds the Surface entity.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-14 20:11:44 +03:00
Hakan SevenandClaude Opus 4.8 f7734b698f feat(ui): use the shared colour selector in properties, ribbon and layers
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 <[email protected]>
2026-06-14 14:22:24 +03:00
Hakan SevenandClaude Opus 4.8 3d050fe1ab feat(ui): move full ACI palette to a separate "Select Color" window (#94)
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 <[email protected]>
2026-06-14 11:14:14 +03:00
Hakan SevenandClaude Opus 4.8 3a87a18f32 fix(ui): float the colour-selector popup instead of pushing the form (#94)
The colour list/palette was laid out inline, shifting everything below it
down when opened. Wrap it in a small custom widget (Floating) that renders
the button inline and the popup as an iced overlay anchored below it, so it
floats over the UI like a real dropdown. Picking a colour now also closes
the popup (the style editors clear their open-field state on edit).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-14 11:00:43 +03:00
Hakan SevenandClaude Opus 4.8 8cdb17a6b4 feat(ui): shared colour selector used across all style editors (#94)
Add a reusable color_selector widget (ui/color_select.rs): a main-colour
dropdown plus a "more colours" button that expands the full 255-entry ACI
palette — the same design the properties panel uses. Replace the flat ACI
swatch rows added earlier in the dimension, multileader and table style
editors with it, wired through each editor's existing colour edit message
(the chosen colour is encoded as an ACI string) plus a per-field
palette-open toggle.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-14 10:42:39 +03:00
Hakan SevenandClaude Opus 4.8 5f6c4029e9 fix(mleader): new multileader inherits the current MLeader style (#94)
A new multileader was built from defaults and ignored the current
MLeaderStyle, so the style editor's settings had no effect on it (the
renderer reads the entity's own fields, not the style). commit_entity_handle
now copies the style's content/path type, text attachment/alignment/angle,
colours, sizes, scale and landing into the new entity (enum types
round-trip through i16 since the entity and style enums are distinct).

Text styles already render correctly and tables already resolve their
style live via table_style_handle, so no change was needed there.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-14 09:59:12 +03:00
Hakan SevenandClaude Opus 4.8 68a3efa83d fix(style): new mleader/table use the current style (#92)
Follow-up to the text/dimension fix: MultiLeader and Table were created
at their default style too. Their styles live in the objects dictionary,
so commit_entity_handle now resolves header.current_mleader_style_name /
current_table_style_name to the matching style object's handle and
assigns it, unless the command already set one or no such style exists.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-14 01:19:00 +03:00
Hakan SevenandClaude Opus 4.8 92e32243eb fix(style): new text/mtext/dimension use the current style (#92)
New annotation entities were created at the entity "Standard" default
instead of the document's current style, so making a style current had
no effect on creation.

- Text / MText: seed the new entity (and the MText editor preview) from
  header.current_text_style_name.
- Dimension: commit_entity_handle fills the new dimension's style_name
  from header.current_dimstyle_name, covering every dimension command
  centrally.

Applied only when the entity is still at its default so an explicitly
styled entity is preserved. MLeader and Table styles need their style
tables modelled first and are left for follow-up.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-14 01:15:52 +03:00
Hakan SevenandClaude Opus 4.8 29ccfcfc46 fix(command): accept typed points/Enter in keyword steps when DYN is off (#97)
A command step that takes both a point and option keywords (PLINE,
MLINE) returns true from wants_text_input to receive the keywords, but
CommandSubmit then routed the text only to on_text_input and, on a None
(unhandled) result, re-prompted instead of falling through. So with
dynamic input off, typed coordinates and a bare Enter were swallowed —
points couldn't be added by keyboard and the command couldn't be ended.

Fall through to the existing Enter / coordinate handling when
on_text_input doesn't consume the text. Coordinates (including @ and #
prefixes) and Enter now work in these steps with DYN both on and off;
commands that fully handle their text step still return Some and are
unaffected.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-14 00:59:52 +03:00
Hakan SevenandClaude Opus 4.8 e141fb6df3 fix(command): block drawing commands on the Start tab (#96)
The Start (welcome) tab has no drawing, so commands like LINE dispatched
from the command line or ribbon ran as silent no-ops. Allow only the
commands that make sense there (NEW / OPEN / EXIT / QUIT; OPEN_RECENT is
handled earlier) and inform the user otherwise.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-14 00:42:02 +03:00