Commit Graph
48 Commits
Author SHA1 Message Date
Afonsop c6db94e86f fix: native-bpp decode accounting, GIF frame bombs, streaming remote bodies
A second adversarial round (with empirical reproduction against the
vendored image 0.25) found the budget contract still had holes:

- The probe billed 4 bytes/px, but 16-bit formats allocate their
  native layout during decode (8/px for RGBA16 PNG; TIFF stacks ~3x
  codec scratch on top). In-budget 16-bit images were falsely refused
  with self-contradictory notes, and the decode transient could exceed
  the budget the tiers claimed to enforce. The probe now reads the
  color type alongside the dimensions and bills the real peak (native
  layout + RGBA where they coexist, plus the TIFF codec factor), so
  refusals are honest and per-platform-correct, and the TIFF allowance
  follows the same accounting.
- GIF: the probe reads the LOGICAL SCREEN, but a crafted frame may
  declare itself arbitrarily larger (empirically: a 10x10 probed
  screen with an 8192x8192 frame — 320 MB and 29 s from a
  probe-claimed-400-byte image). The GIF frame buffer is now capped
  to the probed size plus slack; frame bombs refuse like any other
  oversize.
- Remote bodies on web streamed incrementally with the 64 MB cap
  enforced DURING the read (array_buffer() materialized a hostile
  chunked reply in full before any check could fire), aborting and
  cancelling the reader the moment the cap is crossed.
- Degenerate raster entities (zero/non-finite size or vectors) no
  longer produce NaN placement math that aborted whole PDF exports —
  they resolve to the broken-reference placeholder.
- Same-name resolution now has a deterministic winner (newest
  registration, via insertion order) instead of HashMap iteration
  order; URL query strings no longer break by-name matching; the
  image pick matches its refusal note by the quoted file name
  (substring collisions like a.tif/2011.tif attributed errors to the
  wrong file); a dropped remote outcome at the queue cap triggers one
  unconditional catch-up re-resolution instead of waiting for another
  event; the unknown-size refusal says so instead of printing 0x0;
  and the build script fails cleanly when there are no fonts to
  verify.
2026-09-18 06:23:30 +01:00
Afonsop ca0d7b962d feat: bounded huge-image decoding, webp/gif, remote rasters, Georgian/Armenian fonts
Giant rasters (500 MB aerial/GIS TIFFs) decoded whole: multiple GB of
RGBA on a wasm heap is tab death, and even desktop silently failed
past the image crate's 512 MiB default allocation cap. Decoding now
probes the header for dimensions first and runs in tiers — full decode
under a per-target budget (512 MB wasm / 4 GB native, enforced through
the decoder's allocation limits so oversize fails fast instead of
ballooning), Lanczos3 downsample to a display cap (4096² wasm /
8192² native) when the decode fits but the buffer is oversized, and a
clear localized refusal naming the dimensions and the limit when it
cannot. The probed source dimensions stay the entity's sizing truth,
so a downsampled buffer never shrinks the on-paper footprint; PDF
export self-corrects (effective DPI derives from placement × stored
pixels). Refusals surface once per image through the pick, open, and
drop/re-resolve paths.

Decode coverage gains webp and gif (pure Rust in image 0.25 — no C,
no lockfile churn), with the attach picker and drop filters matching.

Remote http(s) raster references now fetch on web: CORS-classified
failures name CORS, HTTP errors name the status, bodies over 64 MB and
8 s deadlines are bounded, and fetched bytes join the session source
registry (so ARCHIVE can package them and re-resolution reads cache).

Georgian and Armenian Noto subsets ship (generation blocks included),
so those scripts render instead of skipping; scripts still without
subsets remain silently skipped, unchanged.
2026-09-18 04:24:45 +01:00
Afonsop f70d3fda44 fix: final pre-PR review sweep
The final holistic + quality audit (three reviewers) returned GO with
four majors; all fixed.

- Start-page recents were the last unguarded writer of the open slot
  on web: clicking one under the open overlay clobbered the in-flight
  open and silently lost its bytes. Refused with the same in-progress
  notice the picker uses.
- The web quit-after-save continuation stalled: a Quit pending now
  survives the tab-save AND continues — offering to save the next
  dirty tab or finishing the quit, mirroring native.
- Data-extraction's multi-file pick is capped (16): every picked file
  becomes a full parsed document held for the session, and one giant
  pick could exhaust the wasm heap.
- The xref registry tests raced each other on the process-global
  registry (reproduced 3/10 at high thread counts — CI roulette); they
  now hold a module-level guard.

Minors: the collision warning is cataloged in all 21 locales; the
docs' parallelism detail header no longer contradicts its own table;
the drop-filter and name-hash helpers gained inline tests (their only
prior coverage was a verbatim copy in the red-team suites).
2026-09-18 03:33:12 +01:00
Afonsop 2e1006348b web: XREFs, data links & extraction, ARCHIVE transmittals, worker-side saves
The last four big web-vs-desktop gaps.

XREFs: XATTACH reads the picked drawing's bytes and registers them
name-keyed (the image/PDF-underlay pattern), pre-parses on the web
worker, and the commit merge produces the same xref block flow as
native. Opened drawings resolve xrefs from the registry (trailing-name
match) before cache build; unresolved references produce ONE warning
listing the names and the fix — drop the referenced files on the page
(drops and picker opens now register their bytes) and reopen or
XRELOAD. XRELOAD works off the registry on unsaved drawings too.

Data Link & Extraction: spreadsheet links read picked xlsx/csv bytes
(calamine from slices / CSV from string) through a session registry,
so preview, refresh, and table-insert work on web; extraction takes
external drawings via a multi-file picker parsed on the worker, and
outputs CSV/TXT/.dxex via download_bytes. The disabled 'Add folder'
button explains why.

ARCHIVE/ETRANSMIT: assembles the drawing plus registered images, PDF
underlays, and session plot tables into a deterministic ZIP (minimal
writer over flate2 — no new dependency) delivered as
{stem}_transmit.zip; skipped dependencies are reported by name.

Save/autosave off the UI thread: the parse worker gains a serialize
action (protocol v5) — the document is prepared once on the main
thread (clone, styles, dimension bake), then the DWG/DXF writer pass,
the dominant cost, runs on the worker; any worker failure falls back
to inline serialization with the identical shared implementation.
The save path also clears the dirty flag only when the tab didn't
change mid-write, and the runtime-fields sidecar now carries
graphic_data, fixing a pre-existing loss of raw DWG round-trip bytes
on web-opened documents.
2026-09-17 23:23:01 +01:00
Afonsop c643a10ed9 web: raster images, WBLOCK/SAVEALL/SCRIPT/CTB/LAYTRANS/INSERT, filled TTF text
A fresh web-vs-desktop audit found the remaining gaps; this closes the
top ten.

Raster images: IMAGE attach read the picked file via image::open on a
name-only path (dead on wasm), and raster references inside opened
drawings decoded the same way. Picked bytes are now registered in a
name-keyed store (the PDF-underlay pattern), decoding runs from memory,
and dropped .png/.jpg/.bmp/.tif files register by name so unresolved
references in opened drawings re-resolve and render (and export to PDF).

Command gaps fixed with the bytes-at-pick / download_bytes pattern:
- WBLOCK now downloads the extracted block instead of failing on fs.
- SAVEALL saves every dirty tab instead of printing a no-op message.
- SCRIPT picks and reads the .scr file through the browser picker.
- CENTERLTYPEFILE picks the .lin/.dnl file and populates from text.
- Custom .ctb tables picked on web now parse, register for the
  session, appear in the plot dialog list, and apply (load_named
  previously returned builtin-only on wasm).
- LAYTRANS 'Load standards file' parses the picked drawing on the
  existing web worker.
- INSERT 'from file' in the block palette parses picked bytes and
  feeds the shared import path.

Text fidelity: TTF-named styles rendered as thin stroke glyphs on web
because the font database was empty. Fetched Noto faces are now
registered into it with a family alias table (Arial, Times New Roman,
Cambria, ... -> Noto Sans/Serif), so those styles render filled, shape,
export to PDF as vector fills, and appear in the style picker.
ViewCube labels request semibold again on web.

docs/native-vs-web.md: the hatch and font rows were stale (both work on
web now), the GPU row claimed WebGPU is used where available (the
backend is pinned to OpenGL), and the parallelism row missed the parse
worker — all corrected.
2026-09-17 20:59:58 +01:00
Afonsop ebee7e5489 feat(help): open a generated command reference instead of Discussions (#1049)
HELP now opens docs/command-reference.md on GitHub — a 600-command
reference grouped by category with default shortcuts and selection
behavior, generated from the in-repo command registry. A staleness
test fails when the registry and the page drift apart; regenerate
with UPDATE_COMMAND_REFERENCE=1 cargo test command_reference. The
command-line notice is localized in all 21 languages.
2026-09-17 13:14:06 +01:00
Afonsop 3eab00bfbf web: fix red-team breaks in restore, drop-open, and plot paths
Three attack agents (state-machine tracing, executable input fuzzing,
browser-API review) found two breaks and several degradations; all are
fixed here, with the fuzzer's 22 tests kept as regression suites.

Breaks:
- Untitled tabs (and dot-names like ".dxf") autosave as DXF, but the
  restore name defaulted to .dwg — the worker dispatches its reader by
  the name's extension, so the only copy of unsaved work could never
  be restored and was re-offered forever. recovered_name now uses the
  worker's rsplit('.') convention and defaults extensionless names to
  dxf, matching new-document serialization.
- " (recovered)" stacked one suffix per crash-restore cycle; the
  marker is now stripped idempotently before appending.
- Opening a file (Ctrl+O / OPEN / automation) while a web open was in
  flight clobbered it: the in-flight open's bytes were lost and every
  queued restore prompt stalled for the session. on_open_file now
  refuses while an open is running (web only).

Hardening:
- Escape on the Recover Drawing modal defers instead of deleting the
  only snapshot; the Discard button still removes it.
- A cap-rejected restore enqueue warns in-app and re-offers the next
  snapshot; the drop queue cap rises to 32.
- Exiting mid-round no longer races snapshot removals (deferrals are
  drained, the sweep skips while a round is in flight); closing a tab
  with "Don't save" now also removes its web snapshot; the snapshot
  key derivation is shared via web_autosave_key.
- The plot-preview overlay no longer leaks a full PDF copy per preview
  (the Download closure lives in a replaceable registry).
- A corrupt autosave index.json fails the round/boot instead of
  silently resetting every row; plot pages with non-finite or non-
  positive sheet dimensions error instead of emitting a NaN MediaBox.
- The drop filter accepts dot-less "DWG"/"dxf" names the worker
  parses (same rsplit convention on both gates).
2026-09-16 13:56:08 +01:00
Afonsop 101d167ebc web: autosave snapshots with crash-recovery restore, persistent storage
The web build had no autosave: on_autosave was a no-op and the
subscription was native-only. Both gates are gone; every savetime tick
now serializes each dirty tab (same pre-serialize sync as the web save
path, no thumbnail, serialized serially off the critical paint path)
into a dedicated opencadstudio-autosave OPFS directory, keyed by
drawing name, without touching the dirty flag, downloads, or recents.

On boot the web app scans that directory and offers any snapshot
newer than the last saved copy in a localized Recover Drawing modal
(Restore opens it as "name (recovered).dwg" through the standard
open pipeline; Discard removes it; Escape discards). Saving or
closing a drawing clears its snapshot; the boot scan also garbage-
collects stale entries. run_web now requests navigator.storage.persist
so settings and OPFS data survive browser eviction.
2026-09-16 09:36:46 +01:00
Hakan Seven 7b4cef41f7 Complete parametric ribbon and constraint tools 2026-09-14 01:05:39 +03:00
PanosandClaude Opus 5 02b0f4c7d9 Tell the user when the scene is drawn without a GPU
A day of pan/zoom that had suddenly become unusable on a 1.2M-entity
drawing turned out to be no regression at all: an unattended NVIDIA
userspace update had been applied without a reboot, the Vulkan ICD could
no longer talk to the still-loaded kernel module, and wgpu silently fell
through to Mesa's llvmpipe. Nothing in the app said so. iced picks the
adapter and names it only under RUST_LOG, so the only evidence was 32
`llvmpipe-N` threads in /proc. The same silence covers WARP on Windows,
SwiftShader in browsers, and the case where wgpu finds no adapter at all
and iced quietly draws the chrome with tiny-skia while the shader widget
is dropped, leaving a blank viewport. This commit makes all of that
visible, on every platform, without new dependencies.

Detection (src/scene/pipeline/mod.rs)
-------------------------------------
`GpuStatus` is derived from two signals:

* `record_gpu_adapter`, run from `MultiPipeline::new` (once per device,
  next to the existing uncaptured-error handler), reads
  `Device::adapter_info()`, prints one `[gpu] adapter: <name> (<backend>,
  <device type>, driver …)` line — stderr natively, `console.warn` on the
  web, which has no stderr — and keeps a copy.
* `note_scene_draw`, run from `ViewportPane::draw`, counts how often the
  viewport asked for a primitive. Every renderer makes that call; only
  wgpu goes on to `prepare` and create a device. Three draws with no
  device ever recorded therefore mean the primitive is being dropped.

The verdict is one of `Unknown`, `Hardware`, `Software(adapter)` or
`NoRenderer`. `Software` is `DeviceType::Cpu` — what Vulkan reports for
llvmpipe, DX12 for WARP and GL for a renderer string that gives it away —
plus a name match on the fallbacks browsers expose (SwiftShader, llvmpipe,
lavapipe, "Microsoft Basic Render"), because the WebGPU backend reports
every adapter as `Other` (gfx-rs/wgpu#8819). Metal has no software device,
so on macOS only `NoRenderer` can occur (a VM without a GPU); it is caught
by the draw count like everywhere else.

`gpu_status_if_changed` costs one atomic load per app message: a
generation counter moves only when a device is recorded or the draw count
crosses its threshold, so the app re-derives the verdict a handful of
times per session.

Notification (src/app/startup.rs, view/modal.rs, ui/statusbar)
-------------------------------------------------------------
`refresh_gpu_status` runs after every message. On a degraded verdict it
speaks through three channels, because many users never read the command
line:

* A popup, `ModalKind::GpuWarning`, queued once per verdict through the
  startup-modal queue so it waits its turn behind the file open. It names
  the adapter, states the consequence (slow navigation, unstable or
  missing rendering) and gives the remedy that usually applies on the
  running platform: a driver update not yet followed by a reboot on
  Linux, a missing driver or a Remote Desktop / VM session on Windows, a
  VM without a Metal device on macOS, hardware acceleration switched off
  in the browser. "Don't show again for this device" stores
  `GpuStatus::identity()` in `settings.gpu_warning_silenced`; keying it by
  verdict means silencing "software rendering on llvmpipe" does not
  silence a later, different failure.
* A status-bar pill in the theme's warning colours, `⚠ Software rendering`
  or `⚠ No GPU renderer`. It is not a `StatusPill`, so it cannot be hidden,
  and it stays for the session; clicking it reopens the popup, silenced or
  not.
* A command-line entry through the new `CommandLine::push_warning`, which
  is styled like an error but deliberately does not touch `last_error` /
  `error_revision`: automation reads those to decide whether the command it
  just ran failed, and a graphics warning is not a failed command.

`subscription()` requests frames only while the verdict is `Unknown` on a
document tab (the Start page draws no viewport), so the probe costs one
frame on a working GPU, three on none, then stops.

Verification
------------
* Live on the machine that had the incident, with the Vulkan loader
  restricted to Mesa (`VK_DRIVER_FILES=…/lvp_icd.json WGPU_BACKEND=vulkan`):
  popup → OK → pill → click reopens; "Don't show again" → restart → pill
  only, no popup.
* `gpu_tests::the_adapter_is_reported_once_per_device` (ignored, needs an
  adapter) asserts the classification on a real device; it was run on both
  llvmpipe (`Cpu`) and an NVIDIA GT 1030 (`DiscreteGpu`).
* Unit tests cover the name heuristic, the verdict function, the identity
  rule, the warning text with an adapter name that itself contains
  parentheses and commas, the settings round trip and the command-line
  semantics. New strings are in all 21 locales as the `gpu` Fluent group.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-12 16:19:41 +03:00
PanosandClaude Opus 5 5f161bb037 Show the exact build in About, Copy Info, REPORT and --version
Builds from main reported only the release version (2026.36) even when
HEAD was hundreds of commits past the tag. build.rs now also emits
OCS_TAG_DISTANCE, OCS_COMMIT_DATE, OCS_BUILD_METADATA and
OCS_FULL_VERSION: a clean checkout of the release tag stays "2026.36",
anything else becomes "2026.36+194.gef189d77", with ".dirty" appended
for uncommitted changes and only the hash when no tag can be counted
from (shallow clone, tarball). OCS_APP_VERSION is unchanged so the
update check, donation gate, User-Agents and window title keep working.

The About dialog gains Build / Commit date / Profile cards, Copy Info,
the REPORT issue template and recovery reports carry the full version,
and `-V`/`--version` print it instead of Cargo's 2026.36.0. Release
workflows check out full history so release binaries see the tag.

Also fixes the build being marked dirty when there is no repository:
diff-index exits 128 there, not 1.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-12 16:19:40 +03:00
Hakan Seven 754cc89fab Merge PR #1216: expose MTP in snap menus 2026-09-12 15:12:04 +03:00
Hakan Seven cf33455e93 Move parametric tools to their own ribbon tab 2026-09-11 20:58:37 +03:00
PanosandClaude Opus 5 55b2dc1796 Translate the Options dialog into all 21 languages
Sixty-five new strings, each one added to the source catalogue and given a
value in every locale — not English placeholders. Terminology follows what
each locale already uses for the surrounding labels rather than being coined
fresh: grips, selection, preview, viewport, annotation scale and the UCS all
have established words in these files, and the new strings use them.

System-variable names stay in Latin uppercase inside the parentheses, as they
already do in every locale's SETVAR listing — they are identifiers, not prose.

One trap worth recording: Fluent attribute names must begin with a letter, so
the obvious slug for "3D Modeling" is not a legal key. It parses as far as
the message body and then fails the whole file, which surfaces as
`Invalid Fluent syntax in EnUs` with a byte offset rather than as anything
naming the attribute.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-10 08:55:34 +03:00
PanosandClaude Opus 5 08d339d38f Translate the new Selection labels, and pin what SELECTIONPREVIEW's bits do
The ten strings the Options card gained were English in nineteen of the
twenty-one locales. They are now translated in all of them, using the words
each locale already uses for the surrounding labels — de-DE "Griffe",
fr-FR "Poignées", zh-CN "夹点", zh-TW "掣點" and so on for grips, and the
same for selection, preview and object. System-variable names stay in Latin
uppercase, as they already do in every locale's SETVAR listing.

Composing them from existing keys, the way el-GR composes the neighbouring
labels, only worked for one of the ten: there is no existing key for "limit",
"unlimited", "shift", "rectangle" or "lasso", and "Grips: Objects" as the
label of a numeric limit would read worse than English.

The rollover preview was reported as possibly not working. It works, and now
says so: `the_idle_rollover_follows_bit_one` drives a settled hover over a
line and checks what the scene highlighted, for all four values of the
bitmask. Bit 1 alone highlights, bit 2 alone does not, both do, neither does
not. That is the assertion the inverted config comment would have made
someone doubt — the pick simply needs the cursor still for the 500 ms of
`HOVER_DWELL_MS` before it runs.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-09 14:15:44 +03:00
PanosandClaude Opus 5 cabb20db45 Put the rest of the selection settings on the Options card
Options → Selection already carried the pick box, the selection-area effect
and the grip visuals. Four settings behind it were finished, persisted and
reachable only by typing a system variable, and one more was a constant.

**Four that only needed a widget.** `SELECTIONEFFECT`, `SELECTIONPREVIEW`,
`PICKADD` and `PICKDRAG` are all wired end to end already. `SELECTIONEFFECT`
was the odd one: the card's own "Restore Defaults" button resets it, so it
could be turned off by a button and only turned back on from the command
line.

`SELECTIONPREVIEW` is a bitmask and gets one checkbox per bit, which is why
`set_preview_bit` exists rather than two inline expressions — the bits are
the thing this change is most able to get backwards.

The `PICKADD` checkbox is worded as AutoCAD words it, "use Shift to add to
selection", which is the *inverse* of the stored flag. That inversion is
commented at both ends; it reads as a bug otherwise.

**`GRIPOBJLIMIT`.** The limit past which a selection gets no grips arrived
with the selection work as a hardcoded 100, matching AutoCAD's default. It is
now a setting, a slider and a system variable, following `PICKBOX`'s path
exactly. AutoCAD's 0 means "no limit", which the old `>` comparison could not
express, so the read now guards on it — with a test, because zero is the one
value that would otherwise suppress every grip instead of allowing all of
them.

The slider covers 0..=1000, where every useful value lives; the sysvar takes
AutoCAD's full 0..=32767, and a larger value set from the command line parks
the handle at the end while the read-out keeps showing the real number.

**`SelectionPrefs`** replaces the `pick_box` parameter rather than adding
three more. `view_window` already took seventeen positional arguments, and
`pick_add` / `pick_drag_rect` are two adjacent `bool`s that would swap
silently at the call site.

**Two fixes found on the way.** `OPTIONS` and `OP` were registered for
autocomplete but never dispatched, so typing either answered "Unknown
command" and the dialog could only be opened from the start page. And the
`SELECTIONPREVIEW` doc comment had its bits the wrong way round — the code
reads `& 1` for the idle rollover and `& 2` for the in-command one, as
AutoCAD does — which would have put the two new checkboxes' labels backwards.

**Not done, deliberately.** `PICKFIRST` and `PICKAUTO` exist but are always
on, with no variable. `PICKSTYLE` is stored in the header and read by nobody
— group expansion decides from the group's own `selectable` flag — so an
"object grouping" checkbox would control nothing. Associative-hatch selection
does not exist as a selection behaviour. Each needs a behaviour change rather
than an exposure.

Of the ten new strings, en-US and el-GR are translated; the other nineteen
locales carry the English text. Composing them from existing keys, the way
el-GR composes the neighbouring labels, only works for one of the ten —
there is no existing key for "limit", "unlimited", "shift", "rectangle" or
"lasso", and "Grips: Objects" as the label of a numeric limit would be worse
than an untranslated string.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-09 13:19:06 +03:00
Hakan Seven fd3ddc1608 Finish Ramox edge and slice integration 2026-09-08 19:02:08 +03:00
Hakan Seven d66e89ca19 Complete application localization across all supported languages 2026-09-07 01:05:02 +03:00
Hakan Seven 2c83ae2736 Show donation request once per app version 2026-09-06 11:44:38 +03:00
PanosandClaude Opus 5 0d11421dea fix: free superseded wire buffers before allocating their replacement
The renderer shares one set of wire GPU buffers per wire_content_id
across every pane and viewport slot. Every geometry edit mints a new
content id, so every edit builds a new set -- and the old one stayed in
wire_buffer_cache until the map exceeded sixteen entries.

Sixteen entries is a sane bound for small drawings and fatal for large
ones. The policy counted entries, not bytes. On the reproducer a single
set is most of the card: the VRAM trace shows 572 MiB baseline rising to
1871 MiB on a 2048 MiB GT 1030. The second set has nowhere to go, so the
first edit after opening exhausts VRAM. The perf log shows exactly that
-- two `shared-fullupload` lines, an edit, then the process dies.

Evict on every miss instead of at a count threshold, and do it before
building the replacement rather than after, so the old allocation is
released while the new one is still being sized. Dropping this slot's own
handles first is what lets the retain see the superseded entry as
garbage; a pane still drawing that content keeps its own clone and its
strong count stays above one, so shared geometry is never pulled out
from under a live viewport.

The trade is that returning to a previous content id -- undo, mostly --
now rebuilds instead of hitting a warm entry. On a card where the
alternative is dying, that is the right way round.

Adds a PERF line reporting evicted and retained entry counts, so the
policy can be watched rather than assumed.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-04 16:21:08 +03:00
Hakan Seven 612ec8d60f Merge main and localize surveyor units 2026-09-03 12:45:24 +03:00
Hakan Seven 8c0af9d584 fix(i18n): complete missing translations 2026-08-29 09:46:47 +03:00
Hakan Seven 968ee3fda4 fix(dimension): complete ordinate workflow 2026-08-25 21:36:04 +03:00
Hakan Seven 9ded5eb04f feat(dimension): complete diameter workflow 2026-08-25 20:57:28 +03:00
Hakan Seven 8140c39e0d feat(dimension): complete radius workflow 2026-08-25 19:26:24 +03:00
Hakan Seven 649dfae337 fix(dimension): complete angular workflow 2026-08-25 18:55:25 +03:00
Hakan Seven 47612fd662 fix(plot): correct style mode behavior (#874) 2026-08-25 18:08:52 +03:00
Hakan Seven 97ec3ed67e fix(dimension): complete aligned workflow
Preserve association modes, source geometry, and sub-unit formatting.
2026-08-25 13:18:09 +03:00
Hakan Seven 20c7a2df58 fix(mleader): improve behavior and persistence
Keep annotation contexts, grip placement, and style dictionary entries consistent across editing and file saves.

Refs #779
2026-08-25 08:34:25 +03:00
Hakan Seven 06cd0ec6a9 feat(model): add exact solid modelling tools 2026-08-23 22:53:31 +03:00
Karim Jerbi 236b7756f8 feat(draworder): HATCHTOBACK command and interactive DRAWORDER workflows
Implement the AutoCAD compatible HATCHTOBACK (HB) command and enhance the interactive DRAWORDER command pipeline with comprehensive verb selection and viewport entity picking.

Key implementations and architectural details:

1. HATCHTOBACK command execution
   * Isolates entities belonging to the active layout block record or global space.
   * Filters out entities residing on locked layers using an efficient hash set lookup.
   * Records targeted delta undo operations on SortEntitiesTable objects (~100 bytes) rather than snapshotting full document structures.
   * Performs granular draw depth cache invalidation via Scene::invalidate_draw_depth, preserving existing geometry tessellations and spatial indices.
   * Emits localized user notifications when no qualifying hatch entities are found.

2. Interactive DrawOrderCommand state machine
   * Supports preselected entities as well as interactive on demand selection gathering.
   * Implements subverb selection supporting single character shortcuts (A for Above, U for Under, F for Front, B for Back) and case insensitive keyword parsing.
   * Enables interactive reference object picking directly in the viewport via needs_entity_pick and on_entity_pick callbacks.
   * Supports hexadecimal handle specification directly from the command line interface.

3. Ecosystem and system integration
   * Bumps DEFAULT_ALIASES_VERSION from 2 to 3 to register the HB alias while preserving existing user overrides.
   * Integrates HATCHTOBACK and HB into dynamic autocomplete registries.
   * Adds localized message strings across all 20 Fluent translation catalogs.
   * Corrects hatch boundary serialization flags to ensure outermost boundary rings consistently carry the external attribute.
2026-08-23 18:56:01 +01:00
Hakan Seven e74d5d78fc fix(rect): merge current main
Preserve both Point and Rectangle property layouts where the two pull requests changed the same section.
2026-08-22 23:03:26 +03:00
Hakan Seven 27cae9467a fix(rect): harden rectangle behavior
Reject invalid trim geometry instead of silently changing dimensions.\nRegister XData application IDs so rectangle metadata survives saves.
2026-08-22 22:55:36 +03:00
Hakan Seven 7293bec1b9 fix(point): preserve relative marker sizing
Keep rendering, picking, block transforms, and plotted output on the
same viewport-relative PDSIZE geometry without overloading wire width
fields.
2026-08-22 22:22:01 +03:00
Hakan Sevenandramox81 464971969c feat: complete freehand sketch workflow
Co-authored-by: ramox81 <[email protected]>
2026-08-21 00:17:44 +03:00
Hakan Seven 44efb8fd53 feat(frame): complete frame controls
Preserve frame geometry for selection while separating viewport and plot visibility across supported reference entities.

Refs #642
2026-08-20 23:37:02 +03:00
Hakan Seven 2a852f4479 feat(gradient): align editing and rendering 2026-08-20 21:17:10 +03:00
Hakan Seven 667f20d604 feat(revcloud): complete editing workflow
Keep source planes intact and stored Mline offsets in sync.

Refs #626
2026-08-20 20:33:57 +03:00
Hakan Seven 403247cbc3 feat(i18n): add Bulgarian locale 2026-08-17 23:45:04 +03:00
Hakan Seven 26fd9a3c02 feat(i18n): add seven UI locales 2026-08-14 11:06:53 +03:00
Hakan Seven 2f60dd0f59 refactor(i18n): group Fluent attributes 2026-08-09 11:12:47 +03:00
Hakan Seven 52799786f4 feat(i18n): complete locale catalogs 2026-08-09 09:44:54 +03:00
Hakan Seven e6345f89f7 feat(i18n): expand language coverage
Complete missing keys in the existing translations and add selectable Spanish, Brazilian Portuguese, Arabic, and Japanese resources.\n\nThe large compatibility catalogs for the new locales remain in progress.
2026-08-09 01:15:51 +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 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 25515fa33c feat(styles): overhaul style managers
Unify editor layouts, live previews, comparisons, read-only behavior, and localized controls across all named style managers.

Refs #629
2026-08-03 02:01:47 +03:00
Hakan Seven 3e6693b93f feat(locale): localize full interface 2026-08-02 22:09:25 +03:00
Hakan Seven b90b3e3ecf feat(locale): add Fluent translations
Default to the platform language with persisted overrides across native and web builds.

Refs #630
2026-08-02 18:53:38 +03:00