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]>
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]>
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]>
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]>
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]>
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]>
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]>
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]>
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]>
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]>
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]>
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]>
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]>
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]>
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]>
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]>
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]>
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]>
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]>
- 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]>
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]>
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]>
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]>
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]>
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]>
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]>
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]>
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]>
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]>
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]>
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]>
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]>
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]>
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]>
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]>
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]>
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]>
STRETCH edited geometry directly via document.get_entity_mut, which the
scene's tessellation cache doesn't observe, so the viewport kept showing
the pre-stretch shape until an unrelated redraw. Mark each stretched
entity dirty and bump geometry on completion, matching MOVE's
transform_entities path.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Making a layer current via the layer manager (LayerSetCurrent) or
LAYMCUR updated only the per-tab active_layer, not the document
header's current_layer_name/handle. The no-selection ribbon refresh
reads the header layer, so after the change an Esc (deselect ->
refresh_properties) snapped the active-layer dropdown back to the
stale header value ("0") even though new entities still used the
correct active layer. Mirror the change into the header in both paths,
matching the ribbon dropdown's own set-current path.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Starting FILLET/CHAMFER from the ribbon left the command-line field
unfocused, so its Enter arrived as CommandFinalize and discarded a
typed option keyword ("R"/"D"), cancelling the command. Several
related dynamic-input issues surfaced from the same flow:
- CommandFinalize now forwards a non-empty command-line buffer through
CommandSubmit, so a typed keyword is handled the same whether the
command was started from the ribbon or the command line.
- sync_dyn_fields shows no coordinate box during an object-pick step
(needs_entity_pick / needs_structure_point_pick) — there is no point
to enter there, so typed keywords reach the command line cleanly.
- apply_cmd_result rebuilds the dynamic-input fields on NeedPoint, so a
step change (object pick -> radius entry) shows the matching box
immediately and typed digits land in it instead of the command line.
- Object-pick steps now draw a prompt-only hint pill at the cursor
(no input box).
- FILLET/CHAMFER remember the first pick across a mid-selection radius
or distance entry, resuming the second pick instead of restarting
selection.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Renaming a layout tab left keyboard focus on the command line, so typed
characters landed there until the user clicked the tab. Give the inline
rename text_input a stable id and return a focus task when the rename
starts so the user types straight into the field. (issue #86)
Co-Authored-By: Claude Opus 4.8 <[email protected]>
OTRACK acquired points only ever cleared when a command ended, so a
multi-point command (LINE, etc.) accumulated tracking points across
every input, swamping the user with alignment options. Clear the
acquired points and live alignment vector each time a point is committed
to the active command, at all five point-accept paths. (issue #85)
Co-Authored-By: Claude Opus 4.8 <[email protected]>
An empty box or lasso drag called deselect_all, discarding a selection
the user may have built up over several picks. Drop the empty-clear from
the lasso and two-click box paths so a stray empty drag leaves the
selection untouched; Esc remains the deliberate clear. (issue #83)
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Installed builds already register a handler (MSI ProgIDs, the Linux
.desktop MimeType, the macOS Info.plist), but the portable .exe and the
AppImage register nothing, so they never appear in the OS "Open with"
list. Add a best-effort, idempotent runtime registration that runs
silently on a detached thread at startup:
* Windows — writes HKCU\Software\Classes\Applications\OpenCADStudio.exe
(open command + FriendlyAppName + SupportedTypes .dwg/.dxf), which is
what lists the exe in "Open with → Choose another app".
* Linux — writes a user-level .desktop pointing at the running binary
($APPIMAGE when set) and refreshes the desktop database. Skipped when
a system package already provides the desktop entry, so we never
shadow a distro install.
* macOS — no-op; .app bundles auto-register with LaunchServices.
Unlike the default-app prompt this changes no defaults and shows no UI;
it only makes the binary discoverable as a handler.
https://claude.ai/code/session_01DFVSKgViFaxEjZD2KsTdtt
On the first launch that hasn't answered it yet, the app shows a small
dialog offering to become the default handler for .dwg and .dxf files.
The choice is persisted (default_assoc_prompted) so the prompt never
reappears.
The platform plumbing lives in io/file_association.rs:
* Windows — opens the OS's own per-app default-programs dialog via
IApplicationAssociationRegistrationUI::LaunchAdvancedAssociationUI,
passing the "Open CAD Studio" RegisteredApplications name the MSI
registers. Modern Windows guards the actual default behind a
UserChoice hash, so this is the supported consent path.
* Linux — xdg-mime default against the installed .desktop file, whose
MimeType entries already cover image/vnd.dwg and image/vnd.dxf.
* macOS — LSSetDefaultRoleHandlerForContentType binds the DWG/DXF UTIs
declared in Info.plist to the app bundle id.
The work runs on a dedicated thread (the Windows dialog is modal) with
the result delivered back through a oneshot channel and surfaced in the
command line.
https://claude.ai/code/session_01DFVSKgViFaxEjZD2KsTdtt
Switching to a paper layout used to hijack the ribbon with a contextual
"Layout" tab. Replace that with a reusable right-edge vertical toolbar:
a single centred column of icon buttons floated over the canvas, built
from a flat ToolDef list and dispatching through the existing
RibbonToolClick path (ui::side_toolbar). Paper space now shows Viewport /
Page Setup / Export PDF there and leaves the ribbon on the user's tab;
the contextual layout tab and its three auto-switch hooks are removed.
The toolbar is generic infrastructure for future context action sets.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
The Annotate style dropdowns (text / dimension / multileader / table) and
the layer dropdown were hard-coded to "Standard" / "0" and never synced
from the opened document, so they showed placeholder values even with no
file open and stale values after closing one.
- Start the ribbon's style and layer lists empty instead of with a
placeholder entry.
- Sync the style dropdowns from the document on file open and on tab
new/switch/close (the layer sync already ran there).
- Guard sync_ribbon_layers / sync_ribbon_styles / sync_ribbon_from_selection
for the Start (welcome) tab so all three dropdowns stay empty there.
- When a style family is absent (e.g. no table/multileader styles), fall
back to empty rather than a non-existent "Standard".
Co-Authored-By: Claude Opus 4.8 <[email protected]>