Commit Graph
1681 Commits
Author SHA1 Message Date
Hakan SevenandClaude Fable 5 ba73c48aec feat(start): list tutorial videos newest first
The playlist page lists oldest-first; reverse before persisting so the
cached listing and offline launches share the order.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-22 16:24:34 +03:00
Hakan SevenandClaude Fable 5 c879b4294a fix(dxf): building axis lines still hidden — LAYER off misread (#314)
Bump acadrust to a87435f: DXF encodes an off layer as a negative
code-62 colour index; the reader took 70 bit 2 ("frozen by default in
new viewports") as off instead. The AEC sample's GRIDLINES layer —
carrying that bit while fully visible in the DWG — imported hidden, so
every building axis line on it disappeared; genuinely off layers
imported visible. Second and final cause of #314, after the LTYPE
74/75 swap.

Closes #314

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-22 16:20:11 +03:00
Hakan SevenandClaude Fable 5 a7558eb75f fix(dxf): grid lines missing when loading DXF (#314)
Bump acadrust to 6455610: the DXF LTYPE reader treated element code 74
as the shape number instead of the element-type flags and materialized
complex data on every plain dash — dashed linetypes like the AEC
sample's GRIDLINE turned "complex" with a null Shape{0} on each
element and rendered nothing. The DXF writer's swapped 74/75 emission
is fixed with it.

Closes #314

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-22 16:08:38 +03:00
Hakan SevenandClaude Fable 5 ae83b82c96 fix(circle): TTT with concentric circles (#318)
The TTT solver's circle-pair difference equation loses its cx/cy terms
when the two circles are CONCENTRIC (a ring + a line through it, the
issue's drawing): lx = ly = 0, the equation only pins r, and the 2x2
center solve hit a zero determinant and returned no candidates.

The degenerate branch now recovers the solution directly: r comes from
the pinning equation (for a ring exactly (R_outer - R_inner) / 2), and
the centers from intersecting the remaining equation — a line in cx/cy
once r is known — with the first circle's distance constraint
|C - P| = |r + eps*cr|. Concentric pairs inside three-circle picks ride
the same branch. Candidate selection is unchanged, so the circle
nearest the three pick points wins.

Closes #318

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-22 16:02:31 +03:00
Hakan SevenandClaude Fable 5 9b28170d4b fix(undo): grip-drag history entry + mid-command Ctrl+Z steps (#332)
Grip drags mutated the document without ever pushing history — dragging
a polyline vertex (or any grip) could not be undone. The release-commit
now swaps the pre-drag backup in, snapshots that PRE state as a "GRIP"
entry, swaps the dragged result back and clears the redo stack; the
Esc-cancel path is unchanged and a no-move engaging click records
nothing.

Ctrl+Z during a drawing command steps the command instead of the
document undo swallowing the in-progress object (which also killed the
active command): new CadCommand::on_undo_step hook, consulted before
undo_active_tab. PLINE pops its last vertex (shared with the U option),
LINE reverts the last committed segment and keeps drawing from the
previous point (rides its existing U/UndoDocument path), and ARC
Continue reverts the last arc and resumes from the prior anchor with
the correct tangent via a new anchor history. With nothing placed yet
each falls through to the document undo.

Also drops the two warnings the #336 work left behind (unused Entity
import, dead Snapper::override_active).

Closes #332

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-22 15:48:22 +03:00
Hakan SevenandClaude Fable 5 9b40881ab1 feat(trim): TRIM/EXTEND options — edges, fence, crossing, edge, erase (#336)
Both commands gain a shared option mode machine:

- Cutting/Boundary edges (T/B): pick which objects act as the boundary
  (picked edges highlight yellow, the hovered candidate half-strength);
  Enter rebuilds the boundary set, empty = everything (quick mode).
- Fence (F): draw a dashed fence; every object crossing it is trimmed /
  extended at each crossing, re-applied to the surviving pieces so a
  fence across both overhanging ends clears both. Live red/cyan result
  preview while placing points.
- Crossing (C): crossing-selection semantics — the dashed green window
  picks objects crossing its edges AND wholly inside; runs through the
  fence machinery with a synthesized inside click.
- Edge (E): boundaries extrapolate (lines run past their ends, arcs
  close to full circles). Cuts that land on an implied extension draw a
  dashed yellow guide from the boundary's drawn end to the cut point,
  so the user sees which edge causes the cut. Extend previews also show
  the added tail as a bright dashed slice of the true curve.
- Erase (R, TRIM): picks delete without needing an intersection, with a
  red hover preview.
- Shift+click swaps trim<->extend per pick (new set_shift plumbing into
  the entity-pick and hover paths); the hover preview follows the held
  state. Project is out of scope for the 2D pipeline.

The per-type pick bodies moved to shared free fns (pick_trim_at /
pick_extend_at) used by direct picks, hover previews and the fence
pass; EXTEND's snapshot bookkeeping unified with TRIM's placeholder
flow. Option keywords now take precedence over the hex-handle reading
in feed_active_cmd ("F"/"C"/"E" are valid hex), and fence geos carry
sentinel handles — probing with NULL against NULL-handle geos excluded
the whole fence. Regression test: fence_trims_x_arm.

Closes #336

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-22 14:53:50 +03:00
Hakan SevenandClaude Fable 5 5f08b77ebc feat(web): copy/paste in text fields via the browser clipboard (#346)
iced's clipboard rides window_clipboard, which has no wasm backend — a
focused text_input captured Ctrl+C/V on the web and did nothing.

Ctrl+V (captured, wasm only): read the async browser clipboard (the
keypress is a user gesture, so it's permitted) and replay the text as
synthetic KeyboardEvents on the winit canvas — the only route into a
focused text_input, since iced exposes no insert-text operation.
Control characters are dropped and the length is capped. The MText and
inline-TEXT editors keep their dedicated paste paths (no double feed),
and an unfocused Ctrl+V still runs PASTECLIP.

Ctrl+C (captured, wasm only): a widget operation pairs each text_input
with its focus state (iced fires both callbacks back-to-back on the
same widget) to collect the focused field's visible text, then writes
it with navigator.clipboard.writeText. Whole-field text, not the
selection — iced doesn't expose the selected range.

Native behaviour is unchanged.

Closes #346

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-22 12:47:19 +03:00
Hakan SevenandClaude Fable 5 dd40e1f632 feat(snap): Shift+RMB one-shot snap override menu (#337)
Shift+right-click in the viewport opens a cursor-anchored grid of snap
icons (names as hover tooltips). Picking one engages a one-shot
override: only that snap applies — even with running osnap off — until
the next point pick commits, which restores the saved configuration.
Esc drops an unconsumed override; an outside click closes the menu.
The status-bar snap toggles are untouched — this is a separate,
temporary mechanism.

Covers items 1 and 2 of #337 (mid-between-two-points still pending).

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-22 12:27:27 +03:00
Hakan SevenandClaude Fable 5 d2be0c9bea fix(pline): Convert to Arc follows the cursor and seats on click (#339)
Three fixes to the polyline arc-segment grip:

- bulge_from_midpoint keyed the sign off which chord side the midpoint
  lies on — the opposite convention of BulgeArc::from_bulge, so every
  grip-drag frame flipped the arc to the other side and the radius
  jumped wildly ("snaps to a restricted range of radii"). It now
  derives sign and sweep from the p0 -> mid -> p1 traversal
  orientation, verified as the exact inverse of BulgeArc's midpoint.

- The arc segment's mid grip dragged in Translate mode, feeding
  per-frame deltas through the recomputed arc midpoint — the drag
  deadlocked the moment the cursor crossed the chord. Arc grips now
  drag in Absolute mode: the arc re-fits through the cursor itself.
  Straight-segment grips keep the Translate stretch.

- Picking Convert to Arc in the grip menu now engages that segment
  grip (the Add Leader placement mechanic), so the new arc follows the
  mouse immediately and the next click seats it.

Closes #339

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-22 12:15:36 +03:00
Hakan SevenandClaude Fable 5 41f7e2becf fix(extrim): polyline cutting boundaries and polyline targets (#340)
Boundary: a picked polyline explodes into per-segment Line geos, and
extend_line_geos then blew every one of them up to +-TRIM_EXTENT — a
closed rectangle boundary became a grid of infinite lines and the
parity side-test broke. Only an actual LINE entity boundary is treated
as infinite now.

Targets: the sampled trim path sampled polylines at their vertices
only, so a straight segment crossing the boundary was cut at the wrong
place — or not at all when both endpoints were on the kept side.
insert_boundary_crossings splices the exact line_seg_ts intersections
into the sample list and extrim_sampled classifies per segment
midpoint (a vertex exactly on the boundary can't flip the parity
test). Ellipse and spline targets gain the exact cut points too. The
removal preview subdivides straight polyline segments so the red/blue
split follows the boundary instead of jumping at vertices.

Closes #340

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-22 12:04:30 +03:00
Hakan SevenandClaude Fable 5 95fcabaacb feat(open): drag & drop files onto the window (#344)
Dropping a supported file (dwg / dxf / bak / sv$, case-insensitive)
opens it through the existing open flow: an already-open drawing
switches to its tab, a load in progress queues the drop behind it
(multi-file drops arrive one event each), anything unsupported reports
on the command line instead of failing silently in the parser.

Platform support follows winit 0.30: Windows, macOS and Linux/X11
(incl. XWayland) deliver FileDropped; the Wayland backend does not
implement file drag & drop yet, so drops are inert there until winit
gains it.

Closes #344

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-22 11:58:39 +03:00
Hakan SevenandClaude Fable 5 4c99a5c70d fix(dxf): multileaders invisible when loading DXF
Bump acadrust to c5a579f: the DXF MULTILEADER reader now parses the
nested CONTEXT_DATA / LEADER / LEADER_LINE sections (leader lines and
text content used to come back empty, so DXF multileaders never
rendered), and the entity-level code map matches AutoCAD's real output
on both read and write (170 path type vs 172 content type).

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-22 11:46:46 +03:00
Hakan SevenandClaude Fable 5 4792760a69 fix(dxf): MTEXT position wrong in R2018 DXF (#345)
Bump acadrust to 1e79519: the DXF reader now skips the R2018+
"101 / Embedded Object" section of MTEXT (and multiline ATTRIB/ATTDEF)
instead of letting its group codes overwrite the entity's already-parsed
insertion point, rotation, attachment and line spacing.

Closes #345

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-22 11:30:37 +03:00
Hakan SevenandClaude Fable 5 1870b8420f feat(pline): Undo option removes the last segment (#352)
U / UNDO (typed or via the prompt button) pops the last placed vertex
without ending the command, repeatably down to and past the start
point. The exit tangent of the remaining last segment is recomputed
from its bulge so a following Arc segment stays tangent-continuous.
New CmdResult::RemoveLiveEntity takes the live polyline out of the
document when Undo drops it below two vertices; the next point
re-creates it under the same undo unit.

Closes #352

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-22 11:17:01 +03:00
Hakan SevenandClaude Fable 5 e009d2d7a3 fix(rename): BLOCK type, stepped prompts, table re-keying
- RENAME gains the BLOCK type (stepped button + inline arm) via the
  shared Scene::rename_block.
- Stepped command front-ends (Rename, UserReg, Keyword, TwoValue,
  SelectThenTwoValue) returned None after consuming a typed token, and
  the driver treats None as "not consumed" — the same text was offered
  again and advanced two steps at once (RENAME dispatched old==new,
  SETVAR "X" became "SETVAR X X"). Consumed-but-prompting inputs now
  return NeedPoint.
- Symbol renames re-key the name-keyed Table (remove -> rename -> add)
  instead of mutating .name in place, which left the map keyed by the
  old name so later lookups silently failed. References (entity
  layer/linetype/style fields, layer linetypes, header current-* names,
  dim style refs on Dimension/Leader/Tolerance) are chased
  case-insensitively; layer 0/Defpoints and the built-in linetypes are
  protected; the undo snapshot is taken before the mutation so undo
  actually restores the old name.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-22 11:11:20 +03:00
Hakan SevenandClaude Fable 5 b5dbef5776 feat(props): editable block Name row + collapsible XYZ groups
- Block reference Name (Misc) is editable: the dropdown re-points the
  insert to another definition; typing a new name renames the definition
  everywhere (record re-key, Block marker, every INSERT reference).
  Anonymous (*) and xref blocks stay read-only, with the same guards
  added to Scene::rename_block.
- Custom single-control dropdown (text field + caret) built on the
  shared floating_below overlay so the list always opens downward,
  unlike iced's space-based menu placement.
- Consecutive "<Base> X/Y/Z" rows (Position, Scale, Start, Center, ...)
  collapse into one expandable summary row across all entities; the
  expand state persists across rebuilds and selection changes.
- The BEDIT block tab no longer offers the layout Rename/Delete
  context menu.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-22 11:11:10 +03:00
Hakan Seven 47f3409588 chore(release): v0.8.6 v0.8.6 2026-07-22 00:16:43 +03:00
Hakan SevenandClaude Fable 5 b7457ba09d fix(ribbon): highlight follows each tool's mechanism; start-tab dimming (#355)
Tool highlights used to stick: every click set active_tool, but only
interactive commands and a hand-maintained per-modal list ever cleared
it, so one-shots (Cleanup/AUDIT, view actions) and unlisted dialogs
(Customization/ALIASEDIT, CUI, Plugin Manager…) stayed blue forever.

Now the rule is mechanical: state toggles light from state only (the
click highlight drops immediately), interactive commands stay lit
while running, dialog openers stay lit until the modal closes (generic
clear replaces the per-modal list), and one-shots never keep it — the
dispatch site turns the highlight off when nothing stayed running.

On the Start tab, tools whose command the start gate refuses now render
dimmed (grey icon + label) across all button styles, dropdowns and the
quick-access strip; start_allowed() is the single shared authority.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-22 00:13:48 +03:00
Hakan SevenandClaude Fable 5 7a93ba82b3 feat(dyn): locked input fields constrain the preview and the click (#356)
A typed dynamic-input value (TAB-locked angle or distance) now shapes
the rubber-band: the cursor point is routed through dyn_resolve_point —
the same resolution Enter commits — in the mouse-move preview and the
click commit, so a locked 30° pins the line's direction while the free
field keeps tracking the cursor. The lock wins over osnap/ortho/polar.
TAB (and the comma advance) reshape the preview immediately instead of
waiting for the next mouse move.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-21 23:58:05 +03:00
Hakan SevenandClaude Fable 5 3dd27c6789 feat(layers): search boxes in the layer dropdown and Layer Manager (#343)
The ribbon layer dropdown gets a live search field above the list
(cleared when the dropdown closes, panel height tracks the filtered
count), and the Layer Manager toolbar gets one too. Both filter by
case-insensitive substring while toggle/rename messages keep the
original row indices, so acting on a filtered row hits the right layer.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-21 23:41:27 +03:00
Hakan SevenandClaude Fable 5 87f40e7544 feat(layers): draggable Name-column width in the Layer Manager (#359)
A thin divider after the Name header resizes the column by drag
(60-640 px, session-persistent), riding the shared modal drag flow.
The name elide budget follows the column width, so widening the
column reveals long layer names; the hover tooltip stays for overflow.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-21 23:34:02 +03:00
Hakan SevenandClaude Fable 5 261b699dee feat(edit): MATCHPROP stays active for repeated targets (#362)
Applying to one target used to end the command. The apply now clears
the consumed selection and keeps the command in its target phase, so
properties can be painted onto object after object; Enter or Escape
finishes (prompt updated accordingly).

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-21 23:22:27 +03:00
Hakan SevenandClaude Fable 5 3207cb391b feat(view): bare ZOOM prompts with mode options
ZOOM alone used to jump straight into window zoom. It now offers
Window / Extents / All / In / Out / Scale as clickable keywords
(Scale collects the factor in a second step); ZW/ZE/… shortcuts and
the spelled sub-forms still run directly.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-21 23:18:18 +03:00
Hakan SevenandClaude Fable 5 9690d37d03 fix(view): window corner picks exempt from Ortho/Polar (#363)
ZOOM WINDOW's opposite corner was pinned to an axis with Ortho on,
collapsing the window to a line. Extend the #291 window_corner_pick
exemption to ZOOM WINDOW, PLOTWINDOW and WIPEOUT's rectangular mode
(polygon mode keeps the constraints — axis-aligned edges are a
legitimate use).

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-21 23:14:08 +03:00
Hakan SevenandClaude Fable 5 ea860cab72 fix(view): ZOOM Extents fills the window at the real aspect (#364)
fit_to_bounds fitted the bounding box's circumscribed DIAGONAL into the
viewport height regardless of shape, so a wide drawing filled only a
small central patch. Fit against the actual viewport aspect instead —
required half-height = max(|y|, |x|/aspect) over the view-space corners
— and trim the border margin from 20% to 10%. ZOOM EXTENTS/ALL and
ZOOM WINDOW both pass the live pane aspect.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-21 23:09:37 +03:00
Hakan SevenandClaude Fable 5 ac14e0041d fix(styles): re-seed missing Standard styles on open; titled modals (#366)
Deleting/renaming Standard was already blocked in the five style
managers, but a file saved without the built-ins (foreign or damaged)
opened with broken style dropdowns and nothing to reference — re-seed
missing Standard text/dim/table/mleader/mline styles on every open.

Every modal dialog now shows its name in the title bar: centred at
15px across the dialog width, with the move grip and close button
overlaid at the right edge. The bar takes an explicit width from the
caller — a Fill child inside the Shrink frame would blow the dialog
out to the full screen.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-21 23:00:36 +03:00
Hakan SevenandClaude Fable 5 bd27ee31d0 fix(trim): arc-arc intersections tested against wrong circle (#370)
cc_angles returns angles on the TARGET circle, but the arc-boundary arm
of arc_seg_ts filtered them against the BOUNDARY arc's angular span —
wrong circle, so arc×arc (and circle×arc, EXTEND-to-arc, bulge-polyline
segment) intersections were dropped and TRIM neither highlighted nor
cut. Re-express the intersection point as an angle on the boundary
circle before the span test, matching line_seg_ts/ellipse_seg_ts.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-21 22:45:28 +03:00
Hakan SevenandClaude Fable 5 dc10c5dfba feat(edit): STRETCH crossing window selects objects implicitly (#338)
Bare STRETCH used to need a separate object-selection round before the
crossing window. Now the window itself picks the objects: with no prior
selection the command emits StretchWindow, the host gathers entities
whose world AABB touches the window (locked layers excluded) and
restarts at the base-point step. Over-selection is harmless since only
points inside the window move. The noun-verb flow (select first, then
STRETCH) is unchanged.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-21 22:35:35 +03:00
Hakan SevenandClaude Fable 5 5383c64d5d fix(edit): STRETCH moves dimension definition points (#372)
The STRETCH apply loop had no Dimension arm, so dimensions fell into
the generic skip — points never moved, and a reopened file's stale
baked *D block made the result snap back visually. Move every
definition point inside the window (grip-equivalent set plus pinned
text, sparing the zero auto-placement sentinel), refresh the stored
measurement, and drop the stale *D block so the stretch survives
render and save.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-21 22:24:20 +03:00
Hakan SevenandClaude Fable 5 3205f7a2b9 feat(purge): nested fixpoint purge + option prompt (#375)
PURGE now repeats until a pass removes nothing, so definitions freed by
a purged block (nested blocks, layers, linetypes, styles) go in one run.
Reference scan keeps layer linetypes, ATTDEF/ATTRIB text styles, and
dimstyle DIMTXSTY + arrow blocks; the undo snapshot moves before the
first removal so one UNDO restores everything. Bare PURGE prompts with
clickable category options (aliases B/LA/LT/ST run directly), and
pinned prompts drop their "[A / B / …]" listing when the same options
render as buttons.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-21 22:15:33 +03:00
Hakan SevenandClaude Fable 5 5ba8928675 chore(deps): pull acadrust DXF $OSMODE/$LWDISPLAY header fix
Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-21 22:02:15 +03:00
Hakan SevenandClaude Fable 5 ccd1086f9f fix(app): running OSNAP set is app-level, not per-drawing
Modern DWG (R2000+) has no file slot for OSMODE — reading a file left
the header at 0, and adopt_header_sysvars wiped the user's running snap
set on every open. Persist the set (as an $OSMODE-style bitmask) in
settings.json, save it on every snap toggle, and only let a file
override it when it actually carries a nonzero mask (R13/R14 DWG, DXF).

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-21 22:00:55 +03:00
Hakan SevenandClaude Fable 5 5ea6a9ac8e fix(edit): grip edits drop a dimension's stale baked *D block (#398)
A reopened file renders dimensions from the *D block baked on save, so
grip drags only moved definition points while the stale block kept
drawing — grips worked on fresh dimensions but died after a roundtrip.
Route the grip drag, grip-menu actions and typed grip-menu values
through invalidate_dim_block (#181) so tessellation falls back to live
geometry and the next save re-bakes.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-21 19:56:23 +03:00
Hakan SevenandClaude Fable 5 7811add666 feat(ui): persistent literal-space toggle on the command line
'>' button after the Command: label turns the leading-'>' literal-space
behavior into a mode: while on, Space stays in the line instead of
submitting, until toggled off. State persists in settings.json; a
hand-typed leading '>' still works for a single line.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-21 19:43:11 +03:00
Hakan SevenandClaude Fable 5 c0499a1d3c fix(render): SDF text reuse is per wire source (#403)
Paper layouts gather glyphs for the sheet and each content viewport in
one frame; the single-slot last_sdf_text handed the first source's
glyphs to the rest, hiding every model-space text entity inside
viewports (regression in aea1fd2). Key the reuse by render source, and
invalidate on Tolerance/ATTDEF edits and complex-linetype hosts too.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-21 19:26:30 +03:00
Hakan SevenandClaude Fable 5 1cf64161d3 fix(render): hatch dpdx in uniform control flow (#414)
Tint (Chrome/Edge WebGPU) rejects derivative builtins reached after the
family loop's non-uniform early return, invalidating hatch_web/wipeout
shaders on Windows. Take dpdx/dpdy(xz) once at fs_main top and rotate
into the family frame in check_family — same math, uniform flow.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-21 18:06:01 +03:00
Hakan SevenandClaude Fable 5 87049967a2 fix(render): close remaining OCS/WCS gaps for mirrored entities
Follow-up to the LINE/POINT WCS fix - the same bug class elsewhere:

- 2D SOLID rendered its OCS corners raw in both the wire path and the
  shared fill builder (solid_hatch_model); SHAPE placed its marker at
  the raw OCS insertion. Both now map through the arbitrary axis.
- Grips: central wrap in view/dispatch.rs shows planar entities' grips
  at their world position (OCS→WCS) and converts drags back (WCS→OCS
  via the new wcs_point_to_ocs) - per-entity grip code stays in raw
  field space, INSERT keeps handling its own OCS.
- Pulls acadrust 81ebfee: OCS-aware bounding boxes, translate,
  transform and mirror for the ten planar entity types, so MOVE /
  ROTATE / MIRROR and culling work on mirrored entities too.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-21 17:58:09 +03:00
Hakan SevenandClaude Fable 5 59a0f5239c chore(deps): pull acadrust poisoned model-space handle fix
Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-21 17:30:00 +03:00
Hakan SevenandClaude Fable 5 eb0073781f fix(render): LINE/POINT endpoints are WCS, not OCS
DXF/DWG store LINE endpoints and POINT locations in world coordinates -
the extrusion normal only orients the thickness sweep / glyph. Running
them through the arbitrary-axis OCS mirrored every line and point
carried over from a MIRROR (normal 0,0,-1) onto the wrong side of the
drawing: bolt shafts of a mirrored detail block landed in the adjacent
panel while its nut arcs (correctly OCS-mapped) stayed put.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-21 17:27:51 +03:00
Hakan SevenandClaude Fable 5 427db6cd17 feat(web): copyable on-page banner for renderer errors (#414)
wgpu/naga report pipeline and shader failures through the log facade,
then leave the canvas empty - with no logger installed on wasm the
message was lost, so a broken GPU path looked like "draws nothing" with
a clean console. Install a web logger that mirrors Error-level records
to the browser console and into a fixed DOM overlay with selectable
text, a Copy button and repeat-collapsing; panics land in the same
banner via a chained hook. Windows Chrome/Edge reporters can now paste
the exact ANGLE/D3D11 error instead of a screenshot of a blank canvas.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-21 16:43:35 +03:00
Hakan SevenandClaude Fable 5 77535f7c68 fix(dwg): pull dangling-owned-handle writer fix for empty-file saves (#410)
Bumps acadrust to 1b0a362: block headers no longer list handles of
deleted entities, so DWG saves from a session that removed entities
open with full content in TrueView/AutoCAD instead of empty.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-21 16:24:56 +03:00
Hakan SevenandClaude Fable 5 0cd991e42f fix(xref): show merged xref layers in Layer Manager on attach (#407)
XATTACH / XRELOAD merge the xref's layer and linetype tables into the
document, but the Layers panel cache only rebuilds via
sync_with_viewports - which nothing triggered until the file was saved
and reopened. Refresh the layer panel (and ribbon mirrors) right after
the merge in both paths.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-21 15:59:26 +03:00
Hakan SevenandClaude Fable 5 e177f92ff5 fix(xref): blocks respect their internal draw order
Xref merge now inserts entities in the source block's chain order and
carries SortEntitiesTable objects across (remapped to merged handles).
draw_depth_map values become [depth, half] so exploded block/xref fills
and wide-polyline band wires both compose child depth as
insert_depth + in-block rank x half, recursing into nested inserts with
the same divisor - bands and fills from one block interleave by the
block's internal order instead of stacking at the insert's single depth.
Band batches split per rank via WireModel.depth_override; thin wires
stay merged. The wire arena falls back to a full rebuild when a slab
carries mixed depths so refreshes don't flatten band overrides.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-21 15:53:28 +03:00
Hakan SevenandClaude Fable 5 1546c1dc30 fix(polyline): spline-fit 2D polylines draw the curve, not the frame
A curve-/spline-fit heavy polyline stores both the fitted curve points
(vertex flag 8 / 1) and the spline-frame control points (flag 16),
which only SPLFRAME is meant to show. Chaining every stored vertex
drew spokes from the curve to the control frame — stray points on the
curve and, on wide polylines, a blobbed band (#408). drawn_vertices2d
now drops the control points whenever fit data is present (falling
back to the stored vertices when it is not) and feeds the wire, the
wide-band fills and the tapered band alike, so snapping no longer
lands on frame points either.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-21 15:12:52 +03:00
Hakan SevenandClaude Fable 5 8ff7b6b25c feat(extend): arcs extend to their boundaries
EXTEND's pick dispatcher covered lines, ellipses, polylines and
splines but never arcs, though TRIM already handled them (#409).
extend_arc gathers every boundary crossing around the arc's full
circle (reusing arc_seg_ts against all boundary kinds) and walks the
clicked end to the nearest one — CCW for the end angle, CW for the
start. The hover preview draws the extended result like the other
types, and the replace-snapshot patches the new Arc's handle.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-21 14:58:56 +03:00
Hakan SevenandClaude Fable 5 4efdeceffd fix(ribbon): tooltips show again — forward child overlays
Ribbon tooltips died the day the panels moved into CollapsePanels
(v0.7.6): its overlay() started with `self.open?`, so with no flyout
open — the normal state — it returned None and swallowed every child
overlay, and iced tooltips ARE overlays (#411). Buttons still hovered
and clicked, which is why only the tips were missing. With no flyout
the widget now collects the shown variants' overlays into a Group,
borrows split per panel slot.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-21 14:46:02 +03:00
Hakan SevenandClaude Fable 5 a6d4b30dca fix(web): pack wire instance attributes under the 16-attribute cap
The web wire pipeline (fat-instance wire.wgsl) had grown to 17 vertex
attributes as the width-band and taper fields were added; WebGL2 and
WebGPU cap vertex attributes at 16, so pipeline creation failed and
the web viewport drew no lines at all — UI and SDF text still worked,
which is why it looked like "the app stopped working" rather than a
crash (#414). Scalars now ride in packed vec4/vec2 attributes (10
locations total); the instance struct's memory layout is unchanged.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-21 13:06:09 +03:00
Hakan SevenandClaude Fable 5 930d8c51ea feat(gradient): selectable gradient types + working creation
GRADIENT silently degraded to a plain hatch: add_hatch rebuilt the
render model from the DXF entity but never encoded the gradient on it,
so the committed fill lost its gradient entirely. The entity now
carries the full gradient record (name, angle, colour stops,
solid-fill flag), and the standard shape set became selectable:
Linear / Cylinder / Spherical / Hemispherical / Curved plus an Invert
toggle, offered as prompt buttons on the GRADIENT command and as a
Gradient type dropdown + Invert box in the properties panel. All nine
standard DXF names round-trip (inverted linear persists by swapping
the colour stops), and the shaders shape each kind distinctly —
mirrored cylinder, eased curve, dome-profiled hemisphere — on both the
native and web pipelines without changing the GPU struct sizes.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-21 12:51:08 +03:00
Hakan SevenandClaude Fable 5 9bfcad18b9 fix(hatch): pattern-scale edits take effect + background on/off
Property edits on a hatch were invisible: fills render from the
prebuilt model cache, which move/copy refreshed but the properties
path never did — so a pattern-scale change (which must also rescale
the stored final pattern lines by the ratio) changed nothing on
screen (#415). invalidate_property_targets now rebuilds the edited
fill's cached model via Scene::refresh_fill_model.

The background row gains an on/off box: off removes the
HATCHBACKGROUNDCOLOR record entirely (the colour could never be
reverted before), on seeds ByLayer. The codec covers the full
colour-method range (ByLayer / ByBlock / RGB / ACI) and the backdrop
resolves ByLayer / ByBlock through the style chain. "More Colors…" on
the background picker now opens the palette window targeting the
background colour — it used to just close the picker.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-21 12:22:15 +03:00
Hakan SevenandClaude Fable 5 a7fd9ecb45 fix(dyn): an explicit minus keeps its sign in the angle field
The dynamic-input angle field stripped the typed sign and re-signed
the value from the cursor's side of the X axis, so a negative angle
could not be entered at all — 330 had to stand in for -30 (#417). An
explicit -/+ prefix is now taken literally; unsigned entries keep the
cursor-side behaviour.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-21 11:55:17 +03:00