Replace the click-to-instantly-copy read-only cell with a non-editable but
selectable text field: the user selects the value (drag / double-click /
Ctrl+A) and copies with Ctrl+C, the normal expected gesture. The field
carries the full un-truncated value, styled flat (no input box or focus
ring) so it still reads as read-only but with a visible selection colour.
Keystrokes route to Noop, so it can be selected/copied but never edited.
Removes the PropCopyValue message + handler and the mouse_area copy hook.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
A read-only value cell is now click-to-copy: clicking writes the full,
un-elided value to the system clipboard (copy cursor on hover). One
change in render_ro_row, so it covers every read-only row for every
entity. Adds Message::PropCopyValue routed through iced::clipboard::write.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Change CadModule::ribbon_groups() from -> Vec<RibbonGroup> to -> &[RibbonGroup],
backed by a per-module OnceLock cache. The ribbon group tree is static data
(&'static str, Copy enums) that was being fully reconstructed — Vecs, String
clones, enum discriminants — on every call, including all three per-frame
call sites (view(), dropdown_overlay(), style_combo_overlay()).
- 7 built-in modules + 2 plugin templates cache via function-local OnceLock.
Safe because each is a unit struct with exactly one instance per process
(plugins run in isolated child processes; see PluginProcess::spawn()).
- SharedCadModule avoids the static pattern entirely, storing groups in an
instance field (owned.rs).
- IPC runner converts &RibbonGroup -> OwnedRibbonGroup directly via new
From<&T> impls, avoiding an intermediate clone.
- render_small/render_large and the two make_tool_row closures now borrow
(&RibbonItem, &[ToolDef]) instead of taking ownership, so the view loop
doesn't need to clone items to satisfy the old by-value signatures.
- Various match-ergonomics deref fixes (*id, *default, *icon, etc.) from
the &RibbonItem pattern change.
The Properties panel color picker is a custom bool-driven dropdown, while
Layer/Linetype/Lineweight are combo_boxes. Combo_boxes close each other on
blur, but nothing closed the color picker — so opening the Layer dropdown
while the color picker was open left both showing and overlapping.
Give every panel combo_box an on_open handler that collapses the color
picker, so at most one panel dropdown is open at a time.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
The Extension object snap worked but drew no tracking path, so it was
easy to miss — users had to keep the cursor exactly on the invisible
extension line without any visual cue.
When an Extension snap is active, draw a dashed guide line from the
endpoint it extends from, through the snap point and a little beyond,
and orient the three-dot marker along that direction. SnapResult carries
the endpoint screen position (filled once the winning snap is known);
the overlay renders the guide.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
The command-line history dropdown was display-only, so users had to
screenshot and OCR the log to report output. Make it copyable:
- Render the whole backlog in one read-only text_editor so a single
mouse drag selects across lines and Ctrl+C copies the span.
- Add Copy (whole log to clipboard) and Clear buttons to the dropdown.
- Gate the Ctrl+C/X/V accelerators on event Status::Ignored so a focused
text widget's copy/cut/paste wins instead of firing COPYCLIP/CUTCLIP;
the drawing's clipboard shortcuts still work when it has focus.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
The Layer Manager only allowed one layer at a time. Now:
- Ctrl/Cmd-click toggles a layer in the selection; Shift-click selects
the range from the anchor; a plain click selects just one.
- Colour, linetype and lineweight changes apply to every selected layer.
- Delete removes all selected layers at once (reusing the #237 non-empty
warning, now pluralised); layer "0" and the current layer are skipped.
Selection is stored as row indices but re-resolved by name across sorts
and layer-table rebuilds, so it never points at the wrong rows. Modifier
state is tracked via a new SetModifiers message (shift + ctrl/cmd).
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Locked-layer objects were fully editable. Now they stay visible and
snappable but are otherwise protected:
- Not selectable: the five interactive pick paths (box, lasso, single
click, cycling, box-release) plus SELECTALL and QSELECT skip locked
entities.
- Not modifiable (defense-in-depth even if a handle slips through):
transform_entities, erase_entities, apply_grip and the Properties
commit all skip locked entities.
- Faded: entities on a locked layer render dimmed toward the background
(render_style + wire tessellation); toggling lock re-tessellates.
- Snap still works: locked entities are intentionally kept in the wire
set (visibility_ok untouched), so object snap keeps finding them.
- Feedback: a small padlock is drawn by the crosshair when hovering a
locked object, and clicking one prints "Object is on locked layer …".
New Scene::is_layer_locked / locked_layer_name helpers centralise the
check (mirroring layer_hidden).
Co-Authored-By: Claude Opus 4.8 <[email protected]>
The selection-filter popup now has the same Select All / Clear All header
as the OSNAP popup: Select All clears every exclusion (all types
selectable), Clear All excludes every present type. Each button disables
when it would be a no-op.
Also remove the "Object Snap Settings…" footer from the OSNAP popup.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
The bottom status bar was hard to read: size-10 text labels in a 26px
strip. Make it legible and consistent with the drawing tab bar:
- Height 26 → 30 to match the document tab bar; the command-line input
pill matches too, so the three strips line up.
- Replace the drafting-aid toggle labels (ORTHO/POLAR/OSNAP/OTRACK/DYN/
LWT and the OCS pills ISO/QP/FILTER/SC/CLEAN/TPY) with 17px SVG icons;
the name stays in each pill's tooltip. Twelve new icons under
assets/icons/status, drawn in the existing 24×24 stroke style.
- Vertically centre every element via center_y, and bump all pill text to
size 12 with uniform padding plus larger chrome icons (menu, scroll
arrows, osnap dropdown) so nothing reads smaller than the toggle icons.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Grow the attribute editor into a three-tab enhanced editor:
- Attribute: the tag/prompt/value list; click a row to select it, edit
its value below.
- Text Options: the selected attribute's Text Style, Justification,
Height, Rotation, Width Factor, Oblique Angle, Backwards, Upside down.
- Properties: the selected attribute's Layer, Linetype, Color,
Lineweight (the colour list also surfaces a non-standard current
colour so it displays and round-trips).
Applying writes value, text-formatting and common-property edits back to
each attribute positionally (guarded on block name + count), with undo
and a repaint. OK/Cancel are replaced by a single Apply button in a top
toolbar that commits but keeps the dialog open, matching the other modal
windows — the frame ✕ closes and discards un-applied edits. The frame
adopts the shared style-window palette, tab styling and sizing.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Each row in the attribute editor now shows the attribute's prompt — the
text defined on the block's ATTDEF — falling back to the tag when the
block defines no prompt. The prompt is read from the block definition,
since attribute instances carry only tag + value.
Bumps the acadrust pin to pull the matching reader fix: its DWG object
reader was discarding the ATTDEF prompt, so every prompt came back empty.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Double-clicking a block reference that has attributes, or running ATTEDIT
on one, now opens an editor dialog listing every attribute (tag + editable
value) with OK / Cancel. OK writes the values back to the block, with undo
and a repaint; Cancel discards. A block with no attributes still enters
in-place block edit (REFEDIT) on double-click as before. (#192)
ATTEDIT opens the dialog directly when a suitable block is already
selected; otherwise it runs the pick command and the dialog opens once a
block is chosen. This replaces the earlier command-line, per-attribute
prompt flow, which is removed along with its __ATTEDIT__ sentinel path and
the now-unused attedit_set_attrs trait hook; the ATTEDIT command is reduced
to a plain block picker.
The dialog is a tab-scoped in-canvas modal (Plan B): its working copy holds
a document-local handle, so closing that tab or switching away dismisses the
editor rather than risk applying edits to another tab's document.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Selecting an INSERT with attributes now adds an editable "Attributes"
section to the Properties panel — each tag is a row whose value can be
changed inline (Enter commits, undo works, the attribute text repaints).
Previously attribute values could only be reached through ATTEDIT, which
didn't let them be changed (#192).
Attribute tags are runtime strings, so they can't ride the geometry
edit path whose field key is `&'static str`; a dedicated PropValue::
AttrText variant plus PropAttrInput/PropAttrCommit messages carry the
tag. Values are stored verbatim (no expression evaluation, since
attribute text is free-form).
Also guard the panel's in-progress edit buffer: it now only carries
across a rebuild when the selection is unchanged (a commit-triggered
refresh), so a typed-but-uncommitted value can't display or commit onto
a different entity — e.g. two title blocks sharing a REV1 tag. This
closes a latent stale-buffer leak that affected geometry fields too.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
The ribbon layer dropdown grew unbounded so a long layer list ran off the
bottom of the screen with no way to scroll. Cap the panel height and make
the list scrollable; short lists still shrink to fit.
While a dropdown was open, the 3D viewport beneath it kept tracking the
cursor over the panel's empty areas, and the OS cursor vanished there. In
iced 0.14 mouse_area/opaque only capture button presses, never CursorMoved,
so a higher stack layer can't swallow motion — gate it state-side instead:
- ignore viewport motion in on_viewport_move while ribbon.open_dropdown is set
- suppress the crosshair overlay's Hidden cursor (and crosshair draw) so the
normal OS cursor shows over an open dropdown
- route all ribbon dropdown backdrops through a shared dropdown_backdrop()
Co-Authored-By: Claude Opus 4.8 <[email protected]>
The preview box rendered a hardcoded `text("AaBbCc 0123")` that ignored
every edited property. Replace it with a canvas that tessellates the
sample string through the same stroke path the text entities use
(`lff::tessellate_text_ex`), so font, width factor, oblique angle and the
backward/upside-down flags are reflected live as fields change. Effective
font follows the entity rule (TrueType name wins, else stroke font file);
the result is fit to the preview box with Y flipped.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
PR #213 introduced rustfmt drift in tip_style / swatch indentation and an
inline if-expression in two previously fmt-clean files. Format pass only;
no behaviour change.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Consolidates 21 occurrences of identical Color literals scattered across widgets.rs and mod.rs into 9 named constants (COMBO_BG, COMBO_HOVER_BG, COMBO_OPEN_BG, COMBO_BORDER, COMBO_ACTIVE_BORDER, COMBO_ARROW, SWATCH_BORDER, TIP_BG, HIST_INACTIVE_BG). -85 net lines, zero behaviour change.
Section labels (Draw, Modify, Annotation, …) were left-aligned with a small padding. Set align_x(Center) on the parent column so the label container — still sized to its text width — is centred within the column, which itself remains sized by the tool buttons above. Sections keep their natural content-based width, no wasted space.
Keyboard accelerators (copy/paste/save/undo/new/open) were gated on
`Modifiers::control()`, which on macOS matches only the physical Control
key. Mac users pressing Cmd+C/V/S/Z got nothing, contrary to platform
convention and every other Mac app.
Switch the gate to `Modifiers::command()`, which iced resolves to the
Logo (Cmd) key on macOS and Ctrl on Windows/Linux. On non-mac targets
this expands to exactly `control()`, so behavior there is unchanged.
Also make the Keyboard Shortcuts reference window display the
platform-correct modifier name (Cmd vs Ctrl) so the help matches the
keys the user actually presses.
Closes#202
Co-authored-by: Claude Opus 4.8 <[email protected]>
Adds a configurable right-mouse-button behavior without disturbing orbit
or the context menu.
- New persisted setting `rmb_enter` (command `RMBENTER [ON|OFF]`, default
off): when on, a right-click while a command is active acts as Enter
(commit/close); when idle it still opens the context menu. Right-drag
always orbits, so nothing is lost.
- Right-click vs orbit is now time-based: orbit engages only after the
button is held >=150ms and the pointer moves, so a quick right-click is
no longer swallowed by slight pointer jitter (was an 8px distance gate).
- The right-click Repeat menu now lists commands from every source
(command line, ribbon, context menu, shortcuts) via a unified
recents list recorded at the dispatch choke point, instead of only
typed commands.
Closes#185
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Typing a partial command (e.g. `bac`) and pressing Enter with no active
command ran into an "Unknown command" error. Now the top autocomplete
match is highlighted and Enter runs it (`BACKGROUND`), matching standard
DWG command-line behavior.
The fallback only fires when no command family matches the typed verb, so
complete aliases that resolve through a dispatch arm but are absent from
the autocomplete registry (`LT`, `ZO`, …) still run as typed. Programmatic
dispatch (ribbon, plugins, headless automation) is unaffected.
Closes#198
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Keep the right-side status-bar tools from being pushed off the bar when a
drawing has many layouts: the layout tabs now live in a horizontal scrollable
that only takes the space left between the menu and the tools, with ‹ › arrows
(and the mouse wheel) to scroll them.
Repurpose the leftmost hamburger — which previously fired a no-op MENU command
— to open a dropdown listing Model and every layout (scrollable, current
highlighted); picking one switches to it, an outside click dismisses it.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Replace the custom model_tiles tiling with iced 0.14 pane_grid as the layout
source of truth, while paper-space floating viewports are left untouched (they
overlap/free-place, which pane_grid cannot model).
Each Model pane is its own shader widget rendering its camera into the pane
bounds; the renderer Primitive carries a base_slot so several shader widgets
share the type-keyed MultiPipeline storage on disjoint slots. A second pane_grid
of transparent mouse_areas (plus on_resize) layers above the crosshair overlay
to receive input — the overlay's Hidden cursor interaction otherwise levitates
the cursor and starves any layer beneath it. The active pane follows the cursor
(hover-activate), and grid / crosshair / UCS icon render per pane while the
ViewCube and render-mode picker stay on the active pane only.
Split / close / VPORTS presets and the VPort table round-trip now drive
pane_grid (guillotine reconstruction on load). Adds drag-to-swap via a controls
bar handle with a ghost-card preview, visible pane dividers from the pane_grid
split regions, a default model background of RGB(33,40,48), and removes all the
superseded custom-tile code.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
The UCS icon now anchors to the projected UCS origin when it is on-screen
(falling back to the bottom-left corner), and becomes a selectable object:
hover highlights it, a click shows draggable grips, and dragging the origin
grip relocates the UCS while the X/Y tip grips rotate it. Drags snap to
geometry/grid through the existing snapper, and the new UCS is persisted on
release. The icon stays selectable while parked in the corner.
Also make the ViewCube compass (N/E/S/W) world-fixed: the cube body still
reorients with the active UCS, but the cardinals project through a camera-only
rotation and snap in world frame, so the directions no longer spin with the UCS.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Continue tidying the flat ui/ root:
- ui/statusbar/: statusbar.rs becomes statusbar/mod.rs (its
crate::ui::statusbar::* paths are preserved) and absorbs
statusbar_config.rs + statusbar_menu.rs.
- ui/window/: the standalone manager/dialog windows move here —
about, layout_manager, page_setup, plugin_manager, shortcuts,
layers, update_notice, open_progress. The crate::ui::LayerPanel
re-export is repointed so callers are unchanged.
Call sites repointed to the new paths; no compat re-exports beyond the
existing LayerPanel one. ui/ root drops from 21 to 10 flat files with
five subdirs (ribbon, style, popup, statusbar, window).
Co-Authored-By: Claude Opus 4.8 <[email protected]>
- Group the flat src/ui/ dir: move the 9 style editors into ui/style/
(dimstyle, mleaderstyle, mlstyle, plotstyle, textstyle, tablestyle,
point_style, style_list, style_manager) and the 6 popups into
ui/popup/ (cycle, isolate, scale, selection_filter, snap, units).
Call sites repointed to crate::ui::style::/crate::ui::popup::; no
compat re-exports. ui/ root drops from 36 to 21 files.
- Rename app/cmd_result.rs -> app/command_driver.rs: the file is the
StepInput->feed_command driver, not the CmdResult type (which lives
in command.rs). Name now matches contents.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Build the ViewCube out into a full navigation widget:
- Compass ring painted in the cube's ground plane (z = base) so it
reads as a ground compass and foreshortens with the view.
- N/E/S/W cardinals painted flat onto the ring band; clicking one snaps
to that side elevation. Added S/W glyphs to the bitmap atlas.
- Home button (top-down view), roll arrows (90° roll about the view
axis), and four nudge triangles (90° tip/spin) as SVG iced controls
laid out around the cube. New home.svg.
- WCS / named-UCS selector under the cube, switching the active UCS.
The cube hit area now shares one stack with the control buttons, so a
click on a button is caught by the button while a click on the cube
falls through. Face clicks snap to the highlighted region rather than a
cursor position the viewport's move handler can overwrite.
Camera gains home_view / roll_by / nudge_90; floating viewports drive
the same ops through mutate_active_viewport_camera, which re-encodes the
result to the stored view direction + twist.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Text elements previously rendered as hollow wireframe outlines and sometimes
fell back to the wrong system font due to case-sensitive exact matching. Also,
the viewport background grid drew on top of solid entities.
Case-insensitive and partial-name fallback matching (e.g., mapping "arialn"
to "Arial Narrow") is now added to `sysfont::canonical_family_name`, ensuring
text resolves to the correct installed system font.
TrueType font contours are now triangulated into solid fills (`fill_tris`)
using `lyon_tessellation` in `ttf_glyph.rs`. To prevent the GPU from misclassifying
this 2D text as a 3D mesh (which broke flat shading), text tessellation splits
the output into separate outline and fill `WireModel`s.
The background grid is extracted from `SelectionCanvas` into `GridCanvas` and
moved beneath the 3D viewport to ensure proper layering. Finally, `DepthBiasState`
is enabled on all face/fill pipelines to apply a polygon offset, perfectly
resolving Z-fighting between coplanar wireframe lines and solid faces.
The text / dimension / multileader / table style combos rendered their
open list inline inside the ribbon row, so the popup was clipped to the
fixed ribbon-row height and got cut off even with only two entries. The
Draw-tab dropdowns and the layer combo avoid this by rendering as
floating overlays.
Render the style list as an overlay too: the combo now draws only its
button inline, and a new `style_combo_overlay` builds the list as a
floating panel that grows to fit its entries, positioned with the
existing `compute_dropdown_left` (which already accounts for
StyleComboGroup widths) and mirroring `layer_combo_overlay`.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
The OSNAP settings tooltip read "Click ▾"; the ▾ (U+25BE) is absent from the
web font and showed as a tofu box. Tooltips are plain text and can't embed
SVG, so add a rich-body tooltip variant (tip_node) and use the existing SVG
caret (icons::arrow_down) — consistent with the SVG-icon approach the rest of
the chrome already uses for the web.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
The PAN command/tool dispatched a command nobody handled, so it did
nothing. Combined with no middle mouse button on a trackpad/web client,
there was no way to pan at all (issue 164).
PAN (and the "P" alias) now enters a pan mode: a left-button drag pans the
view, reusing the existing middle-button pan path. Exit with Esc or by
starting another command. While active the crosshair is hidden and the
cursor becomes a hand (open when hovering, closed while dragging). PAN/P
are also added to command-line autocomplete.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Route the remaining absolute-coordinate CPU projection through the
relative-to-eye path so it no longer multiplies by a view_proj matrix
carrying a ~1e7 translation (which cancels catastrophically in f32 after
world_offset removal):
- camera pick_on_plane / pick_on_target_plane delegate to
unproject_on_plane_f64 (rotation-only inverse + f64 eye)
- snapper (world_to_screen), hit_test (click_hit / mesh / hatch), the
scene mesh/solid hit methods, grid + UCS axes, and polar_constrain_near
all take view_rot + eye and project relative-to-eye
- compute_grid_step measures the per-unit scale via the rotation-only
matrix (no eye term needed)
view_proj remains only for conservative CPU frustum-cull / scissor and the
screen-space viewcube / UCS-icon directions. NOTE: SnapResult.world and
the command-point chain are still Vec3 (f32) — a ~0.5 m quantization at
extreme UTM zoom remains; the command layer goes f64 next.
Co-Authored-By: Claude Opus 4.7 <[email protected]>
Route the on-screen chrome that projects absolute world coordinates
through the f64 relative-to-eye helpers so it stays glued/jitter-free at
UTM scale after the world_offset removal: grips (grips_to_screen) and
grip hit-testing (find_hit_grip) now project from the f64 GripDef.world
via Camera::project_f64; the model grid and its UCS axes carry the camera
eye (DVec3) + rotation-only matrix and a f64 origin, projecting each line
relative to the eye. Snap-marker / OST-point / compute_grid_step still use
the f32 path (single points / spacing — negligible) and can follow.
Co-Authored-By: Claude Opus 4.7 <[email protected]>
Two leftovers from the UCS grid work:
- The coloured X/Y/Z axis lines (draw_axes) were drawn at the world
origin along world axes; they now run through the UCS origin along the
UCS axes, matching the grid lines.
- Grid snap rounded the cursor on the world XYZ lattice; it now rounds in
the UCS grid frame (to-UCS, round, back-to-world) so it lands on the
grid the user sees. The UCS origin (wire space) + rotation reach the
snapper via DocumentTab::ucs_grid_basis; identity outside model space.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
The grid ruled along world X/Y/Z through the world origin. It now rules
along the active UCS axes through the UCS origin (passed in render/wire
space), so it lines up with the user's coordinate system. Paper space
stays plain WCS; identity UCS reproduces the old world-aligned grid.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Introduce one bridge between WCS (how geometry and the file are stored)
and the active UCS (the coordinate system the user works in), so every
UCS-aware system routes through it instead of re-deriving axis math.
- UcsXform (app/helpers.rs): the converter — to_wcs/to_ucs (points),
vec_to_wcs/vec_to_ucs (directions), axes(), is_identity(). Orthonormal
axes, so the inverse is the transpose. The old ucs_to_wcs / ucs_rotate_vec
free fns now delegate to it (one implementation).
- DocumentTab::ucs_xform() builds it from the tab's active_ucs (None = WCS).
- DocumentTab::adopt_active_ucs_from_header() loads the document's saved
current UCS (header model-space UCS) into active_ucs on open, so the file's
coordinate system is live immediately; called from both load paths.
- Status-bar coordinate readout reports the cursor in the active UCS.
- UCS icon tripod projects the active UCS axes, so it rotates to the UCS
instead of always showing world X/Y/Z.
First slice of routing the app's coordinate systems through the converter;
input/picking, grid, ViewCube, and the remaining readouts follow.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Grid (and grid-snap) state was shared through the global show_grid flag, so
toggling it in model space also turned it on in paper space, and a floating
viewport's grid painted across the whole paper — and beyond the 3-D view when
zoomed in.
- Store grid/snap per view: a model tile in model space, the active floating
viewport or the layout's sheet viewport in paper space, round-tripped via the
viewport status flags.
- adopt_view_display on layout switch and viewport enter/exit so the live
toggle follows the view you are in.
- Render one grid per view (paper_viewport_grid_views): the sheet over the
paper, each floating viewport clipped to its screen rectangle.
- Clamp the grid clip to the widget so an overflowing viewport rectangle never
paints grid outside the 3-D view.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
The object-snap menu drew each mode's marker as a Unicode geometric glyph
(◻ △ ◯ …). The web build bundles only Fira Sans, which lacks them, so they
rendered as tofu boxes (#138). Add an SVG per snap mode under
assets/icons/osnap, map them via icons::osnap, and draw them with the same
tinted-SVG path the rest of the chrome uses — font-independent on every
platform.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Ribbon tooltips were centered below the button, so the cursor resting on
the button covered the tip text. Anchor them to the right instead, clear
of the cursor. (#143)
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Ribbon layer dropdown rows were a single button, so a click on the
visible/freeze/lock icon fell through to the make-active handler instead
of toggling that state. Split the row: each icon is its own toggle
button, only the swatch+label selects the active layer. Toggle handlers
now resync the ribbon so the icon updates while the dropdown stays open.
Layer Manager columns are now sortable: clicking a header sorts by that
column, clicking again flips direction (SVG arrow indicator). Sort is
reapplied on every panel sync so it survives edits, preserves the
selection by name, and keeps the ribbon dropdown order in step.
Fixes#133
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Move grid and grid-snap toggles off the status bar (and its customization
menu) into the top-left viewport chip, now a single container holding the
horizontal/vertical split buttons, render-mode picker, and grid/snap
toggles, sized to the render-mode picker. Add grid + magnet (snap) icons.
Render the grid per model tile from each tile's own grid_on, in its own
bounds and camera, instead of a single grid in the active tile. This stops
the grid flickering on/off as the cursor crosses panes or leaves the
viewport, and lets each pane show its own grid independently.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
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 <[email protected]>
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 <[email protected]>
The DYN angle box was placed at full cursor radius along the mid-angle
direction. With a near-zero sweep that direction collapses onto the
cursor ray, planting the box on top of the cursor / snap point and
hiding it. Pull the box back along the ray and offset it to the side
opposite the distance box so the snap point stays visible. (#124)
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Dynamic-input value boxes, the prompt pill and the UCS axis labels are
drawn with `frame.fill_text`, whose `canvas::Text` defaults to
`Shaping::Auto`. Auto picks Basic shaping for ASCII-only strings, which
renders on desktop but draws nothing on the web (wgpu/webgl) backend, so
the all-digit distance/coordinate boxes and X/Y/Z labels were blank
(the angle box rendered only because its `°` upgraded it to Advanced).
Typed values landed correctly but were invisible. (#117)
Set `shaping: Advanced` on every canvas fill_text in the overlay.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
UI chrome (dropdown carets, undo/redo, checkmarks, close/menu/osnap,
layer/file-menu icons) and ribbon tool icons were drawn as Unicode
glyphs. Desktop fills missing glyphs via system-font fallback, but the
web build bundles only Fira Sans with no fallback, so they rendered as
empty tofu boxes (issue #116).
Add a shared src/ui/icons.rs (SVG-backed, tintable) plus bundled assets,
and switch every glyph site and IconKind::Glyph tool def to SVG.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
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 <[email protected]>