Commit Graph
910 Commits
Author SHA1 Message Date
Hakan SevenandClaude Opus 5 2ea3bca093 feat(properties): stop offering a height the style has fixed
A text style with a non-zero height fixes the size of everything drawn in
it — the CAD that writes the file skips the height prompt for such a
style, and shows the height without letting it be changed. OCS offered
the field either way, so a drawing could be edited into disagreeing with
its own style.

The height row is now read-only when the style fixes it, for TEXT, MTEXT
and both attribute kinds. `num_prop` already draws a numeric row either
way, so the value still reads the same, just grey.

The panel builders are handed an entity, not the document it came from,
and widening that trait would touch every entity type. The lookup rides
beside the unit context instead, seeded from the same place, since it
answers the same kind of question: something the drawing settles that a
row needs in order to render itself.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-08 02:45:46 +03:00
Hakan SevenandClaude Opus 5 188d1a9ccb fix(dim): draw a dimension from the picture it carries
A DIMENSION references the block "that contains the entities that make up
the dimension picture", and that block is the picture: AutoCAD requires
it and draws it, BricsCAD draws it when present and only renders from the
dimension variables when it is missing. OCS re-derived the picture from
DIMVARS every time and ignored the block entirely.

That works while a style agrees with what the drawing actually drew. A
Revit-exported schedule here does not: its style stores DIMTXT already in
drawing units beside a DIMSCALE meant to scale it, so the text came out
three hundred times too tall, and each dimension carries a DIMFXL
override that is likewise already in drawing units, so the extension
lines ran nearly a million units past the points they measure.

Neither is guesswork about units now. The block holds the geometry the
drawing was authored with, so it is drawn: the contents are already in
world space and the block's base point is the origin, and the identity
instance that carries them exists only to reuse the scene graph's
ByBlock / layer-0 colour resolution. Every wire answers to the dimension,
so a pick still selects the dimension rather than one of its pieces.
Re-deriving stays as the fallback for a dimension with no block.

Annotative dimensions keep the old path: their several representations
are separate blocks and choosing between them is what the annotation
machinery already does, while the doctrine above assumes one picture.

A picture is only good while its inputs hold, so it is dropped when they
change: editing a dimension's properties, and saving the dimension style
it is drawn with. Entity edits already dropped it.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-08 02:44:30 +03:00
Hakan SevenandClaude Opus 5 039f703d7f fix(input): stop drafting-aid keys from cancelling the command
F8 reached ORTHO as an ordinary command, and dispatching a command tears
down whatever is running first: the active command dropped, its preview
cleared, its base point forgotten. So pressing F8 partway through a LINE
abandoned the line, and during a MOVE it took the dragged ghost with it.

A function key gets pressed mid-command — that is what it is for.
Toggles that only flip a flag now skip the teardown: ORTHO, GRID, SNAP,
POLAR, and the drafting-settings popup. None of them starts a command,
opens a document or reads geometry, so there is nothing for the teardown
to protect.

F7, F9 and F10 came through the same path and were broken the same way;
they are fixed with F8. F3, F11 and F12 already had messages of their
own and were never affected.

Closes #677

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-08 00:48:25 +03:00
Hakan SevenandClaude Opus 5 eb513ac5f8 fix(stretch): let the selection stage behave like every other one
STRETCH ended itself when a crossing window caught nothing, so a missed
aim meant restarting the command to try again. A missed aim is not a
decision to stop: say what happened and ask for the corner afresh, the
way a selection that picks nothing leaves MOVE still asking.

Enter now ends the selection stage too. With objects already picked the
window is only there to narrow them, so there is nothing left to choose
— Enter takes a window containing all of them, which stretches every
vertex, which is the whole selection moving. That is what a
pick-selected stretch means. With nothing picked there is nothing to
end, so Enter still cancels. The prompt says so.

Not changed: with a prior selection, a window that misses every vertex
still goes on to ask for the base point. The command holds wires, not
entities, and the entity rules do not follow the wires — a small window
inside a circle catches its centre, and so the whole circle, while no
tessellated vertex is inside. Guarding on the wires would refuse that
stretch.

Closes #676

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-08 00:42:16 +03:00
Hakan SevenandClaude Opus 5 a4698c5b6b fix(units): leave the sheet viewport alone when converting
DWGUNITS reframed every viewport by the conversion factor, on the
reasoning that a viewport's framing is measured in model units and has to
follow them. That holds for a window onto the model; it does not hold for
a layout's sheet viewport, whose view is the paper itself, in paper
units. Scaling it left the sheet the size it was while the view of it
moved by a thousand.

Only a paper-space viewport can be a sheet — the model-space scale
reached everything else, and what it reached frames the model by
definition. Among the rest, the layout names its sheet outright, and a
file arriving without that link still gives itself away by sitting at the
paper origin, where only the sheet sits.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-08 00:11:57 +03:00
Hakan SevenandClaude Opus 5 62dfbfe2be fix(paper): give the sheet back its desk and its edge
A paper layout painted its whole canvas in the page colour, so the sheet
had no visible edge — zooming out never reached one, because the surround
was the same colour as the page. Floating viewports then painted
themselves black over it.

Both come from the same idea, that a viewport's canvas is a colour it can
be handed. What lies behind a viewport depends on what the viewport is:

- The full-canvas sheet shows the desk. `paper_bg_color` is the page, and
  the page is a bounded rectangle `paper_sheet_fill` already draws; using
  it for the canvas too erased the boundary between them. The desk is now
  its own colour, defined once in the scene and read by both the widget
  and the renderer, so the two cannot drift apart.
- A floating viewport sits on that sheet and must paint no canvas at all.
  Handing it a transparent one does not work: the background shader
  returns an opaque colour whatever alpha it is given, so alpha zero came
  out black. Skipping the pass is the only way to ask for see-through, so
  `skip_background` now suppresses both the clear and the pass. It is part
  of the frame signature too — without that, a cached frame would keep
  showing the canvas the flag was just turned off for.

Shading and fog still blend toward what is behind a floating viewport,
which is the page, so the uniform keeps that colour even though nothing
paints it.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-08 00:11:47 +03:00
Hakan SevenandClaude Opus 5 6cbec232bb feat(units): read back every form the drawing writes
The readout learned to write lengths as 5'-6 1/2" and angles as
N 45d0'0" E, but typed input was still a bare parse::<f64>() — a
coordinate could be displayed and not entered. Give the formatters
their inverses.

parse_length takes feet and inches separated by a dash, a space, or
nothing, with the closing quote optional, and whole-plus-fraction in
any of those forms: 5', 60", 5'-9", 5'9-1/2, 9-1/2, 1/2. A leading
minus is a sign; the one inside 5'-9" is a separator, since after feet
there is nothing left to subtract from.

parse_angle reads the mark that says which convention an angle is in —
g for grads, r for radians, d/'/" for degrees-minutes-seconds, compass
letters for a surveyor's bearing — and reads a bare number in whichever
convention the drawing is set to.

ANGBASE and ANGDIR now apply, but only where they mean something. Which
way a thing points is measured from the drawing's zero and runs the way
it says; how wide an arc opens is the same number whatever zero is
counted from. format_direction and parse_direction are the pair for
directions, and the polar readout and polar input both go through them,
so the bearing shown is the bearing accepted. format_angle stays for
sizes, where applying a base angle would corrupt the value.

The command prompts follow the same rule, each classified rather than
swept: OFFSET distance, FILLET radius, CHAMFER distances, ARRAY and
MINSERT spacing, TRACE width, HELIX, and EXTRUDE height read lengths;
the four dimension text-rotation prompts, ARRAY's total angle and
INSERT's rotation read angles. MLINE's scale and INSERT's scale factor
are left alone — a multiplier has no unit to be written in. INSERT read
both through one parse; they are now separate.

The typed variants also take a decimal comma, which a single-value
prompt has no other use for. parse_length itself does not: inside a
coordinate the comma separates the axes.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-07 23:46:49 +03:00
Hakan SevenandClaude Opus 5 523b85f42b fix(units): make the units selector reach what it names
The status-bar units button wrote INSUNITS, which nothing on screen reads.
INSUNITS only tells the drawing how to scale content arriving from
elsewhere, so picking a unit relabelled the drawing and changed nothing
visible — the selector looked broken because the variable it moved was
invisible.

Point the button at LUNITS instead, the setting that decides how every
coordinate and distance is written, and give the three questions the word
"units" runs together a place each:

- The button offers the five length formats, each shown beside a sample of
  itself, so Engineering and Architectural are told apart by sight.
- UNITS / DDUNITS opens a real dialog: length and angle format and
  precision, the direction angle zero points, whether positive angles run
  clockwise, and the insertion unit. Its sample comes from the same
  formatters the drawing uses, so it cannot drift from the readout.
- DWGUNITS converts. Model geometry, viewport framing, the header's length
  defaults and each dimension style's scale all move together in one undo
  step; paper space is left alone, since the sheet is still the same sheet.
  Locked layers are converted too — a half-converted drawing would be at
  two scales at once.

The coordinate readout formatted at four decimal places whatever the
drawing asked for; it now goes through the same format_length the
properties panel uses. That made the angle formats reachable in turn, so
degrees-minutes-seconds now carries the d/'/" marks that say which
convention it is, and surveyor's units are written at all.

Typed input is still plain decimal: a length the readout writes as
5'-6 1/2" cannot yet be typed back. ANGBASE and ANGDIR are stored but not
yet applied to displayed directions.

Closes #668

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-07 19:59:09 +03:00
Hakan SevenandClaude Opus 5 26b4f29a13 feat(options): offer the file association in Options
Registering as the .dwg/.dxf handler was reachable only by typing
FILEASSOC, which is also the only thing that installs the icons the
desktop shows — so a user with no reason to know the command had no way
to arrive at either.

The checkbox makes the same two calls the command does, so the stored
setting and the actual registration cannot drift apart. A registration
the system refuses puts the checkbox back rather than leaving it showing
a state that is not true.

The line under it says the icons come with it, since that is what sends
people looking for this in the first place.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-07 17:51:06 +03:00
Hakan SevenandClaude Opus 5 7c8271c716 fix(icon): draw every icon from the logo file
Two places still showed the old mark after the logo was redrawn.

The window icon was built by drawing the letterforms stroke by stroke in
code, background colour and all, which made it the one icon in the
application not coming from assets/logo.svg. It now rasterises that file,
fitted to the square without distorting whatever aspect the logo has. It
returns nothing when the logo cannot be rendered, so the window falls back
to the platform default instead of to something invented here -- which is
how it ended up stale in the first place.

The icon installed into the icon theme was only ever written from inside
the desktop-entry write, behind that write's own "nothing changed" early
return. The entry's text does not change when the logo does, so once it
had been written the icon was never refreshed again. The two change for
different reasons; the icon is now checked first and on its own terms. It
already compares bytes, so an unchanged logo still costs only a read.

Worth knowing on Wayland: the compositor ignores the window icon entirely
and takes the dock icon from the desktop entry, so there the installed
theme file is the only one that matters -- and it refreshes when the file
associations are next set up, not at launch.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-07 17:37:59 +03:00
Hakan SevenandClaude Opus 5 36e328f7d7 feat(layers): add LAYTRANS to translate layers onto a standard
Drawings arrive from elsewhere carrying someone else's layer names, and
folding them into a set of house layers meant renaming and merging by
hand, one layer at a time.

LAYTRANS reads a layer set out of another drawing and moves this one onto
it. Given a file it maps every name the two already share and translates
at once, which is all most incoming drawings need. Bare, it opens a
translator: the drawing's layers on one side, the loaded set on the
other, mappings built up between them and applied only on Translate, so a
mapping can be reconsidered and the whole translation is a single undo
step. Mappings can be saved and read back, objects can be forced to
ByLayer so the target layer's properties are what actually shows, and the
result can be written out as a log.

The move itself is the one LAYMRG already made for a single pair, so
LAYMRG now goes through it too rather than keeping its own copy.

Three things fell out for free or nearly so. A standards file is a
drawing, so .dws and .dwt need nothing but a place in the file filter.
The entity list is flat and holds block definitions, so objects inside
blocks travel with everything else instead of being stranded on a layer
that no longer exists. And a target layer that this drawing already has
is left alone: the drawing has an opinion about it, and restyling it
would reach past what was asked for -- only newly created targets take
the standard's properties.

Mappings are saved beside the drawing rather than inside a DWS. That is
where AutoCAD keeps them, but putting them there would mean inventing a
place in the drawing that nothing else reads back.

Closes #624

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-07 16:51:15 +03:00
Hakan SevenandClaude Opus 5 f7590b1aa8 feat(plugin-api): let add-ons ask for a visual style by name
Removing the binary wireframe event left add-ons with no way to set a
visual style except by spelling out a command string. Give them the
intent back, in the vocabulary everything else now speaks.

The name is carried as text rather than the render-mode type: this crate
is dependency-free by default and only pulls acadrust in behind the host
feature, which is not a weight to add to the manifest contract for one
enum. The host resolves it against the same list its ribbon, its picker
and its commands read, so an add-on can offer exactly the styles the
application does and no others. An unknown name says so and lists the
ones that exist, from that same list.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-07 14:29:01 +03:00
Hakan SevenandClaude Opus 5 b5a1146966 feat(view): one list of visual styles behind every way of choosing one
The ribbon offered four styles and dispatched each one's id as a command.
Two of those ids were not visual-style commands: "Shaded" carried SOLID,
which draws a 2D filled polygon, so choosing it started a draw command;
"Hidden" carried HIDDEN, which matched nothing at all, so it did nothing.
The intent had been for the button to fire its tool's event, but the
dropdown never reads one -- it dispatches the id.

Behind that sat two generations of the same feature. The render-mode
picker offered the seven styles a viewport can actually be drawn in;
everything else -- the ribbon, VSCURRENT, SHADEMODE, VISUALSTYLES and a
handful of bare verbs -- went through a binary wireframe-or-shaded flag
that could only approximate them, reporting "Hidden (shown shaded)" and
"X-Ray (shown as wireframe)" when asked for something it had no way to
draw. Four descriptions of one choice, each drifting on its own.

There is one list now. Each style names itself once -- mode, label, icon,
and the command that applies it -- and the ribbon, the picker, the
VISUALSTYLES verb and the interactive prompt all read from it, down to
the line that lists the choices, so what is offered cannot disagree with
what works. Keywords are the render modes' own names, since there is one
set of styles left to name.

The binary path is gone rather than kept alongside: its message, its
module event, the tool definitions that produced it, the ribbon's
special-cased highlight arms, the bare style verbs and the older keyword
spellings. X-Ray goes with it -- no render mode draws one, and it was
already coming out as a plain wireframe. Three files that had been left
holding a single icon constant each fold into the list.

Note: ModuleEvent::SetWireframe leaves the plugin API with it.

Closes #621

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-07 14:15:59 +03:00
Hakan SevenandClaude Opus 5 5c5c7606a1 feat(select): offer the selection modes at "Select objects"
Only Previous and Last were on offer, so Window, Crossing, Fence, the two
polygons and All could not be reached at all -- and with no keyboard to
type them into, the buttons were the only way in.

Window and Crossing fix the sense of the next box. Dragging derives that
from the direction the corner travels and would overwrite it the moment
the corner moved, so the derivation stands down while a keyword holds it.
Add and Remove decide what a pick does for the rest of the round: Remove
subtracts the way Shift already does, rather than through PICKADD, which
decides whether a pick *replaces* the set and so would have wiped
everything gathered so far. Both are dropped when the next command
starts, since neither outlives the selection that asked for them.

Fence and the polygons are picked point by point, which TRIM and EXTEND
already ask for. That gesture -- the points, the rubber-banded dashed
preview, whether the path closes -- moves into one place both bind to,
and the two preview builders duplicated in trim.rs go with it.

A fence is not the polygon test with crossing set: that closes the point
list and counts whatever lies inside, so a line drawn past a row of
objects would take everything behind it too. It gets a hit test that only
counts a real intersection, and skips the area-based geometry entirely.

The lasso gathered its hits from wires, hatches, block hatches, meshes and
block meshes inline; the new modes need the same list, and a second copy
would drift. Both now come through `Scene::path_hit_handles`.

Closes #596

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-07 13:32:11 +03:00
Hakan SevenandClaude Opus 5 0af6b470ed refactor(snap): measure typed distance along the guide that is drawn
Extension answered "which acquired ray is this snap on?" twice, from two
different tests: the overlay picked its guide by off-ray distance in
screen pixels, while typed-distance entry re-searched the tracking set in
world space. Two tests can disagree, and the ways they disagree are both
bad -- a guide on screen that typing ignores, or a distance running off
along a ray the user cannot see. The second search could also come up
empty on its own, since only four acquisitions are kept and the oldest is
dropped as new ones arrive.

Ask once. The search that draws the guide now also reports the endpoint it
drew from, the snap carries it, and typed distance reads the ray straight
off that -- the direction is just the way from the endpoint to the snap,
which sits beyond it by construction. The screen-space test stays the only
one, which is what the guide wanted anyway: it stays scale-independent
where a world test would not.

Also correct the tracking-point doc: it claims XZ with Y=0, but
`edge_dirs_at` builds every direction from X/Y with Z zeroed and the rays
follow it.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-07 11:29:15 +03:00
Hakan SevenandClaude Opus 5 b8f9727a27 fix(grip): stop double-offsetting the UCS grip snap marker
The extension anchors needed the same pane-to-canvas lift the snap
marker already got, but the block that added it was placed next to the
one that was already there rather than merged into it, so `screen` took
the tile origin twice. A single full-canvas tile has no origin to add, so
it only shows once the viewport is split. Fold the anchors into the
existing block.

Also require the ray to pass through the snapped point before offering it
as a distance reference. Only four acquisitions are kept and the oldest is
dropped as new ones arrive, so the ray that produced a snap can be gone by
the time a distance is typed; picking the nearest survivor unconditionally
sent the point off along an unrelated ray instead of falling back to plain
point input. `extension_snap` projects onto the ray in exact f64, so the
tolerance only has to clear rounding, scaled with the coordinate to hold at
UTM magnitudes.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-07 11:22:04 +03:00
Hakan Seven 687bc2079d Merge pull request #691 from gianlucafiore/fix/grip-direct-distance
Support tracking and direct distance input for grip editing
2026-08-07 11:16:49 +03:00
Hakan SevenandClaude Opus 5 e5974a8895 fix(setvar): refresh geometry the variable is baked into
LTSCALE scales the dash pattern baked into every wire, and PDMODE /
PDSIZE decide the point glyph built at tessellation time. Each has its
own command that invalidates for exactly that reason -- and PDMODE /
PDSIZE are also reached from the point style dialog and the viewport
setting, both of which invalidate too. Only SETVAR, the fourth way in,
set the header and stopped there, so the drawing kept rendering the old
value until some unrelated edit happened to rebuild it.

Invalidate the same way each variable's own command does, alongside the
TEXTFILL case already handled here.

The rest of the SETVAR list needs nothing: FILLMODE is read at render
time and hashed into viewport state, CELTSCALE and TEXTSIZE only seed
new entities, SPLFRAME is never read outside this file, and the others
are behaviour rather than geometry.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-07 11:10:42 +03:00
gianlucafiore 62457be90e Support tracking and direct distance input for grip editing 2026-08-06 23:35:58 -03:00
gianlucafiore 6752df6ea4 Fix linetype refresh and circle rendering 2026-08-06 23:35:16 +03:00
Karim Jerbi 8f009d0058 feat(insert): add docked block palette
Add a searchable block palette with previews, file import, collision-safe nested definitions, and dependency/table-record import. Keep palette thumbnails current across drawing switches and block-definition edits.
2026-08-06 11:02:52 +03:00
Hakan Seven 67a767e23b fix(view): pause hover during navigation 2026-08-06 00:21:44 +03:00
Hakan Seven b667eeeb05 refactor(render): unify scene rendering paths
Route model, paper, viewport and nested block content through one traversal while preserving leaf renderers. Carry decoded material state into mesh shading and harden solid geometry conversion.
2026-08-05 21:44:59 +03:00
Hakan Seven afc94dce13 feat(qselect): expand filter workflow
Refs #511
2026-08-05 13:40:53 +03:00
Hakan Seven e07f4f959b fix(view): preserve projection framing
Use full 3D bounds to derive a safe perspective eye distance without changing the camera target, orientation, or field of view.

Refs #502
2026-08-05 12:47:46 +03:00
Hakan Seven deaecdfb1e fix(ribbon): mask tools on start
Refs #494

Refs #507
2026-08-05 00:52:52 +03:00
Hakan Seven b92b8df997 fix(trim): handle lasso and box selection
Keep selection gestures in the active trim flow so crossed segments are modified instead of selected.\n\nRefs #487
2026-08-05 00:26:29 +03:00
Hakan Seven 474bfcc7db fix(input): restore arrow key navigation
Route editor and command-line arrows by active context and recall successfully dispatched commands.\n\nRefs #485
2026-08-05 00:08:35 +03:00
Hakan Seven 657d023fb7 feat(ucs): integrate coordinate systems
Route coordinate input, geometry creation, transforms, properties,\nviews, and viewport persistence through a shared working plane.\n\nRefs #484
2026-08-04 23:17:54 +03:00
Hakan Seven b1d55c6a7e feat(view): add visual style previews
- preview render modes on hover without committing
- distinguish 2D and 3D wireframe fill and depth behavior

Refs #483
2026-08-04 21:23:25 +03:00
Hakan Seven 44d0c65ae9 feat(ui): add viewport tooltips
Reuse ribbon tooltip styling for every viewport toolbar action and expose translated action names with related commands.

Refs #482
2026-08-04 19:10:47 +03:00
Hakan Seven 7c893d8f5f feat(ui): refine drawing tabs
Use theme-driven active and hover states, integrate close controls, and keep tab spacing stable across drawing states.

Refs #481
2026-08-04 19:04:59 +03:00
Hakan Seven 9cf0c9f945 fix(ui): refine command line and clipboard
- preserve command history colors after reopening
- remove collapsed command bar residue and split the unsaved prompt
- read system text when the drawing clipboard is empty

Refs #467
Refs #468
Refs #470
2026-08-04 18:53:08 +03:00
Hakan Seven d1f9055901 feat(ui): refine ribbon and view navigation 2026-08-04 18:21:52 +03:00
Hakan Seven 675538eb8d fix(layers): disable redundant set current
Refs #459
2026-08-04 17:37:19 +03:00
Hakan Seven c9550b48f9 feat(properties): refine quick properties
- Keep the floating panel near the selection and inside the viewport
- Persist the toggle and hide the panel without a selection
- Show only Material in 3D Visualization
2026-08-04 17:32:06 +03:00
Hakan Seven 5ad95d1d2e fix(context-menu): keep menu inside viewport
Refs #457
2026-08-04 17:10:27 +03:00
Hakan Seven b0806679c9 feat(command-line): resize history panel
Refs #456
2026-08-04 16:54:59 +03:00
Hakan Seven 539d83de47 feat(grips): improve direct editing
Refit arcs from fixed control points and highlight hovered grips.
Let Lengthen preview from the cursor or accept a typed distance.
2026-08-04 16:27:03 +03:00
Hakan Seven b744d4bf2d fix(viewport): align picks in oblique views
Project cursor rays directly onto the active drawing plane so previews
and committed points remain under the pointer.

Refs #429
Refs #452
2026-08-04 15:58:53 +03:00
Hakan Seven 429d2eb06b feat(ui): refine about dialog
Use intrinsic modal sizing and present platform details in a balanced layout.\n\nRefs #450
2026-08-04 15:53:34 +03:00
Hakan Seven 2f86bfde8e feat(input): add editable shortcut table
Persist all active bindings and route keyboard events through the configured table.\n\nRefs #449
2026-08-04 15:35:06 +03:00
Hakan Seven fb0f409bfd feat(input): expand shortcut support 2026-08-04 14:20:02 +03:00
Hakan Seven 4be8d9735c feat(ui): add dockable properties panel
Preserve panel width across left/right docking and persist resize, position, and auto-collapse preferences.

Refs #348
Refs #650
2026-08-04 13:46:02 +03:00
Hakan Seven 63c8245f98 fix: normalize recent Patreon support 2026-08-04 12:17:21 +03:00
Hakan Seven f6b9d6514e feat(plot): add Print All workflow 2026-08-04 11:29:11 +03:00
Hakan Seven 5662e07303 feat(area): support multi-object selection 2026-08-04 00:01:12 +03:00
Hakan Seven 48332ee807 feat(area): add object and aggregate modes
Mesh-backed measurements need cached surface metrics. Keep shared loop collection available in web builds.
2026-08-03 22:31:20 +03:00
Hakan Seven feb110404c fix(ui): correct supporter data and labels 2026-08-03 22:08:22 +03:00
Hakan Seven de2707a23e fix(styles): apply active creation defaults
Resolve style properties before editors and previews open.
Use absolute annotation multipliers for style-driven objects.

Refs #401

Refs #431
2026-08-03 16:43:58 +03:00