Commit Graph
619 Commits
Author SHA1 Message Date
gianlucafiore 9954888d1d fix: carry click-time pointer position in viewport input message 2026-10-14 10:29:07 -03:00
Hakan SevenandClaude Opus 5.5 ec603f1c8f Command line: hovered history lines stay until the pointer leaves
Lines still showing when the pointer reaches the overlay history no
longer fade under it; leaving restarts their COMMANDLINEFADETIME.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 13:27:57 +03:00
Hakan SevenandClaude Opus 5.5 a156a63edd Properties: open a dropdown only on the surface that asked (#1766)
The docked panel and Quick Properties render the same rows from one
state, so a picked colour opened both dropdowns and each swallowed the
other's click.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 12:34:16 +03:00
Hakan SevenandClaude Opus 5.5 d5de798f8a Options: snap marker size (#1770)
The object-snap glyphs were fixed at 5 px; a Drafting slider now scales
them from 1 to 20 px.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 12:24:32 +03:00
Hakan Seven be6dbfdd78 Merge pull request #1780 from ramox81/codex/ribbon-keytips
Ribbon key tips (Alt): codes on tabs, buttons and menu items

# Conflicts:
#	locales/ar-SA/opencadstudio.ftl
#	locales/bg-BG/opencadstudio.ftl
#	locales/cs-CZ/opencadstudio.ftl
#	locales/de-DE/opencadstudio.ftl
#	locales/el-GR/opencadstudio.ftl
#	locales/en-US/opencadstudio.ftl
#	locales/es-ES/opencadstudio.ftl
#	locales/fi-FI/opencadstudio.ftl
#	locales/fr-FR/opencadstudio.ftl
#	locales/hi-IN/opencadstudio.ftl
#	locales/hu-HU/opencadstudio.ftl
#	locales/it-IT/opencadstudio.ftl
#	locales/ja-JP/opencadstudio.ftl
#	locales/ko-KR/opencadstudio.ftl
#	locales/lo-LA/opencadstudio.ftl
#	locales/nl-NL/opencadstudio.ftl
#	locales/pl-PL/opencadstudio.ftl
#	locales/pt-BR/opencadstudio.ftl
#	locales/ru-RU/opencadstudio.ftl
#	locales/tr-TR/opencadstudio.ftl
#	locales/zh-CN/opencadstudio.ftl
#	locales/zh-TW/opencadstudio.ftl
#	src/app/control/actions.rs
2026-10-14 12:14:23 +03:00
Hakan Seven 21f33e933e Merge pull request #1777 from ramox81/codex/refedit-dialog
REFEDIT: Reference Edit dialog and unique names for xref objects

# Conflicts:
#	locales/ar-SA/opencadstudio.ftl
#	locales/bg-BG/opencadstudio.ftl
#	locales/cs-CZ/opencadstudio.ftl
#	locales/de-DE/opencadstudio.ftl
#	locales/el-GR/opencadstudio.ftl
#	locales/en-US/opencadstudio.ftl
#	locales/es-ES/opencadstudio.ftl
#	locales/fi-FI/opencadstudio.ftl
#	locales/fr-FR/opencadstudio.ftl
#	locales/hi-IN/opencadstudio.ftl
#	locales/hu-HU/opencadstudio.ftl
#	locales/it-IT/opencadstudio.ftl
#	locales/ja-JP/opencadstudio.ftl
#	locales/ko-KR/opencadstudio.ftl
#	locales/lo-LA/opencadstudio.ftl
#	locales/nl-NL/opencadstudio.ftl
#	locales/pl-PL/opencadstudio.ftl
#	locales/pt-BR/opencadstudio.ftl
#	locales/ru-RU/opencadstudio.ftl
#	locales/tr-TR/opencadstudio.ftl
#	locales/zh-CN/opencadstudio.ftl
#	locales/zh-TW/opencadstudio.ftl
#	src/app/control/actions.rs
#	src/app/mod.rs
#	src/app/view/modal.rs
2026-10-14 12:10:40 +03:00
Hakan Seven ff5833c35e Merge pull request #1776 from ramox81/codex/imageadjust-dialog
IMAGEADJUST: Image Adjust dialog for raster images
2026-10-14 12:01:13 +03:00
ramox81andClaude Opus 5.5 86429bf439 Ribbon key tips: Alt shows codes on tabs, buttons and menu items
A lone Alt shows a code on every ribbon tab and quick-access button.
Typing a tab code opens the tab and shows the codes of its buttons,
dropdowns, slide-outs and collapsed panels; a button code runs it, a
dropdown or slide-out code opens it and shows its item codes, Esc goes
back one level. Codes follow the reference ribbon; buttons without a
reference counterpart get none and unnamed menu items are numbered.

Codes are drawn as accent pills over the widgets that reported their
bounds in the current frame, with a hint bar under the ribbon. While
the tips are open keys go to them only. The automation gets a keytips
action and read operation.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 11:18:58 +03:00
Karim Jerbi 78bdfa3c74 feat(block-editor): add Save Block As
Create a new named definition from current session edits, restore the source checkpoint, and continue editing the copy with fresh local history. Add the name dialog and Save dropdown, clone supported owned metadata onto independent handles, and refuse unsupported relationships atomically.
2026-10-14 09:11:55 +01:00
ramox81andClaude Opus 5.5 92a3a8ee4d REFEDIT: Edit Reference dialog and unique names for xref objects
REFEDIT opens a dialog after the pick: the nesting chain as chips with a
preview of the chosen definition, all objects or picking them in the
drawing, attribute definitions (offered only when the definition has
them), locking objects outside the working set and unique names for an
external reference's named objects. The settings are remembered.

With unique names an xref's dependent layers, linetypes, text styles and
blocks are checked out as $N$NAME records of their own; without, under
the plain name, sharing a host record of that name. Save writes them back
under the reference's names, and the records made for the session are
removed when it closes. -REFEDIT uses the remembered settings.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 10:39:28 +03:00
ramox81andClaude Opus 5.5 2f55c73457 IMAGEADJUST: Image Adjust dialog for raster images
IMAGEADJUST (alias IAD) takes the selected raster images or asks
"Select image(s):", then opens a dialog with a before/after preview of
the first image and brightness, contrast and fade rows (bar plus
stepper, whole numbers 0-100). Apply writes the values to every chosen
image as one undo step; restore defaults sets 50/50/0. Adds the
image_adjust automation action and the image-adjust locale group.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 10:11:36 +03:00
Hakan SevenandClaude Opus 5.5 780b54489e feat(input): touch screen gestures on the drawing
One finger already worked as the mouse. Two or more fingers now make a
gesture instead of fighting over the cursor: two drag to pan and pinch to
zoom about the fingers, three drag to orbit; a quick two-finger tap opens
the context menu, a three-finger tap zooms to the extents and a quick
three-finger swipe up shows the isometric view. While a gesture runs the
drawing area's own pointer handling keeps out, and the press the first
finger started is dropped.

Refs #906

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 22:25:13 +03:00
Hakan SevenandClaude Opus 5.5 11f68a1185 feat(clipboard): paste images from other programs on desktop too
A paste that finds no text now reads an image off the system clipboard
on desktop as well as on the web. The pixels are written as a PNG beside
the drawing (or in the config folder for a drawing never saved) and
placed as a raster image at the cursor, the same way the web paste
attaches a screenshot. PASTESPEC falls back to the same image paste when
the clipboard holds neither objects nor text.

Refs #645

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 22:08:14 +03:00
Hakan SevenandClaude Opus 5.5 adbcac8a02 feat(start): recent files as a list, icons or large previews
Three toggles beside the Recent Documents title switch the panel between
the existing list, a grid of thumbnail tiles and one large preview per
file. The choice is kept in the recent section of the app config.

Refs #1494

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 21:46:40 +03:00
Karim Jerbi f39fb31137 feat(block-editor): isolate session history and add save checkpoints 2026-10-13 15:35:24 +01:00
Hakan SevenandClaude Opus 5.5 f18386c100 feat(io): FBXEXPORT writes the drawing's 3D meshes as binary FBX
FBXEXPORT (also EXPORTFBX) saves every 3D mesh of the drawing (solids,
surfaces and meshes, at full display resolution) as a binary FBX 7.4
file for visualisation tools: one model per object with its geometry,
face normals and a material in its display colour. Axes stay Z-up and
the unit scale follows the drawing's insertion units.

Refs #1502

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 16:37:09 +03:00
Hakan SevenandClaude Opus 5.5 8a2bc443e0 feat(options): menu bar, ribbon and HPASSOC switches; see-through command line by default
Options > Display gains Window Elements with "Show the menu bar"
(MENUBAR) and "Show the ribbon" (RIBBON / RIBBONCLOSE); Options > User
Preferences gains a Hatch switch for HPASSOC. The command line now
starts at 80 % opacity so the drawing shows through it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 16:14:26 +03:00
Hakan SevenandClaude Opus 5.5 ee18f7ad3c feat(ui): classic pull-down menu bar and ribbon toggle
MENUBAR 1 shows File, Edit, View, Insert, Format, Tools, Draw,
Dimension, Modify, Parametric and Help menus above the ribbon; every
entry runs an existing command, and a test keeps each entry pointing at
a registered command. RIBBONCLOSE hides the ribbon and RIBBON brings it
back, so the menus can replace it. Both choices persist.

Also translates the "File" label, which every locale had left in
English.

Refs #1450

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 16:05:51 +03:00
Hakan SevenandClaude Opus 5.5 5dd05bca18 feat(command-line): adjustable command line opacity
COMMANDLINEOPACITY (20-100 percent, default 100) and a slider under
Options > Display make the floating command line's panels see-through,
so the drawing stays visible behind it. The input field stays opaque
for legibility. The value is an application setting that persists.

Refs #1494

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 15:46:28 +03:00
Hakan SevenandClaude Opus 5.5 5a6f69f3f8 feat(select): drag selected objects to move them, Ctrl+drag to copy
Pressing on a selected object (away from its grips) and dragging runs
MOVE on the selection from the grabbed point, and the release is the
destination, so the drag gets the command's preview, snaps and ortho.
With Ctrl the drag runs COPY and places one copy. A press that does not
move stays an ordinary click, and drags that start off the selection
still make a window or lasso.

Refs #750

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 15:40:55 +03:00
Hakan SevenandClaude Opus 5.5 2ddf2a31da feat(hatch): HPASSOC sets whether new hatches are associative
HPASSOC (0 or 1, default 1) is the starting state of the hatch
command's associative option. It is an application setting: typed
directly or through SETVAR, it persists across sessions and does not
mark the drawing modified.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 15:32:55 +03:00
Hakan SevenandClaude Opus 5.5 b1d4c9bd9b feat(clipboard): PASTESPEC pastes in a chosen format
PASTESPEC offers the formats on the clipboard: Objects and Block for
objects copied in the application (the PASTECLIP and PASTEBLOCK paths),
and Text for the system clipboard's text, placed as multi-line text in
the current text style with its lines as paragraphs.

Refs #645

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 15:06:58 +03:00
Hakan SevenandClaude Opus 5.5 0eec9c450f feat(input): lock the next point to a typed <angle
Typing <angle at a point prompt (e.g. <30) now fixes the direction of
the next point from the last one until a point is placed: the cursor
moves along that direction, and a bare number entered next is the
distance along it. The angle is read in the drawing's angle base and
direction and turned into the current UCS. Works from the command line
and from scripts or the automation token input alike.

Fixes #1464

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 14:21:50 +03:00
Hakan SevenandClaude Opus 5.5 f6503ad4d3 fix(ui): keep a typed layer or style name when the field loses focus
Renaming a layer or a style was only applied on Enter; clicking anywhere
else dropped the typed name. A click that moves focus off a field whose
text changed now commits it. New and Copy in the style managers open the
generated name for renaming, so a new style can be named right away.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 11:19:12 +03:00
Hakan Seven 03445a540d Merge pull request #1722 from ramox81/codex/view-background
# Conflicts:
#	locales/ar-SA/opencadstudio.ftl
#	locales/bg-BG/opencadstudio.ftl
#	locales/cs-CZ/opencadstudio.ftl
#	locales/de-DE/opencadstudio.ftl
#	locales/el-GR/opencadstudio.ftl
#	locales/en-US/opencadstudio.ftl
#	locales/es-ES/opencadstudio.ftl
#	locales/fi-FI/opencadstudio.ftl
#	locales/fr-FR/opencadstudio.ftl
#	locales/hi-IN/opencadstudio.ftl
#	locales/hu-HU/opencadstudio.ftl
#	locales/it-IT/opencadstudio.ftl
#	locales/ja-JP/opencadstudio.ftl
#	locales/ko-KR/opencadstudio.ftl
#	locales/lo-LA/opencadstudio.ftl
#	locales/nl-NL/opencadstudio.ftl
#	locales/pl-PL/opencadstudio.ftl
#	locales/pt-BR/opencadstudio.ftl
#	locales/ru-RU/opencadstudio.ftl
#	locales/tr-TR/opencadstudio.ftl
#	locales/zh-CN/opencadstudio.ftl
#	locales/zh-TW/opencadstudio.ftl
2026-10-13 07:44:01 +03:00
turbuczn 09cf99f85c feat: Add advanced selection filter 2026-10-12 22:27:19 +02:00
turbuczn 7c61583ac2 Refactor FILTER validation and operator normalization (v2.2) 2026-10-12 22:27:19 +02:00
Afonsop 835e8d469f feat: in-place save (File System Access), image paste, print wiring, web capture
The last high-severity web gap plus a small integration bundle.

In-place save: with the File System Access API (Chromium/Edge), Save
now works like desktop. Plain Save on a tab with a retained handle
writes straight through — no picker, no download, no "(1)" copies.
First save or format change opens showSaveFilePicker AT the confirm
gesture (the worker serialization that follows can outlast the
transient-activation window; retained handles need no activation to
write). Picker cancel cancels the save like desktop; picker or write
failure falls back to the legacy download with a note; Firefox/Safari
keep the download flow untouched (feature-detected). Handles are
keyed by tab id in a thread-local registry (Message must be Send —
JsValue cannot ride in messages or App state); closing a tab drops
its handle; Save As re-picks. Dirty clears only on successful
persistence, same race guard as before.

Image paste: Ctrl+V of a screenshot inserts it as an image reference
— the clipboard reader falls back to image/* items when neither the
entity clipboard nor text has anything (precedence preserved), the
bytes enter the image source registry as pasted-<timestamp>.<ext>,
and attach reuses the IMAGE command's model-creation path verbatim.

Browser print: beforeprint snapshots the WebGL canvas into a print-
only <img> (the capture_canvas technique), so the browser's own print
menu produces the drawing instead of a blank page.

Automation capture on web: the capture op no longer calls the
synchronous GPU-map screenshot (dead on wasm); main's frame-parked
capture waits for a presented frame and reads via capture_canvas — the
same workaround the save-thumbnail path uses — delivering the PNG as a
download.

(cherry picked from commit 6e89b36d)

Ported onto main (#1717 review): the web capture rides main's existing
frame-parked capture (one ControlCaptureFrame path, ignoring the timer
tick on wasm) instead of a second parked-capture state; closing a tab
drops its file handle inside main's on_tab_close.
2026-10-12 12:02:05 +03:00
Afonsop 6af8d8ba8c feat: restore-all session recovery, ?file= deep links, offline PWA
Session recovery: more than one pending snapshot adds a 'Restore all'
button to the Recover Drawing modal — every snapshot goes through the
exact same guarded path as a single restore (deleted only after its
own open succeeds). The open-tab list and active tab persist to
localStorage on web (open/save/close) and the remembered tab is
re-selected once a restore batch settles; the modal notes how many
drawings the session had. Nothing is auto-opened.

Deep links: ?file=<url|name> opens a drawing at boot. http(s) URLs
must end in .dwg/.dxf and are fetched with the same streaming,
64 MB-capped, 8 s-deadline reader the remote images use, then go
through the guarded open queue; cross-origin opens announce the
origin first. Bare names resolve from the browser's recent storage,
with a hint when absent. Everything else (file:, javascript:, drive
letters, non-drawing tails) is refused with a console note.

Offline PWA: the app was installable (manifest) but downloaded its
55 MB bundle on every visit. A cache-first service worker now serves
the whole bundle from Cache Storage after one online visit —
navigations network-first so updates land, hashed assets
stale-while-revalidate, non-GET/cross-origin passthrough, old caches
cleaned on activate, and the waiting-worker update flow documented.
The Pages workflow stamps the cache version from the bundle hash and
fails if sw.js is missing or unstamped.

(cherry picked from commit 5f9b345e)

Ported onto main (#1717 review): the cache-version stamp moved from the
skipped build-web.sh wrapper into the Pages workflow; the tab-close hook
lands in main's on_tab_close, which also cancels pinned control requests.
2026-10-12 12:02:05 +03:00
Afonsop db8db14006 feat: web automation wrapper (#1349), DATALINK table on web, System theme; honest decode accounting
Ported from 2c38de53 (#1717). Main already carries the hatch PDF draw
order fix (#1345), the numeric set_properties compare (#1348) and the
web open from bytes, so those parts stay out; what remains:

#1349 — window.ocsControl.ready/request promise wrapper with an
ocs-ready event (web/ocs-control.js, loaded by the web page), and the
startup donation prompt is suppressed once a control request is seen
(opt-in pre-boot via localStorage["opencadstudio.automation"] = "1").
docs/automation/web.md documents both. The control protocol stays at 1.

DATALINK CSV-to-table works on web (picker + bytes through the session
registry — was std::fs, silently dead); COLORSCHEME accepts System
(prefers-color-scheme on web, AppsUseLightTheme on Windows).

Decode accounting, made honest per the fix-of-fix review: into_rgba8
allocates the RGBA destination while the native buffer is alive for
every non-Rgba8 layout (L8 5, La8 6, Rgb8 7, L16 6, La16 8 B/px
peaks — the formula now says so), GIF bills the screen+frame
coexistence at 8 B/px, and TIFF drops the 3x multipliers both ways —
tiff 0.11 splits the given budget between frame and scratch (a total
ceiling), so the old double-billing refused honest 16-bit GeoTIFFs
~2.25x early while admitting hostile chunk transients at 3x.

(cherry picked from commit 2c38de53)
2026-10-12 12:02:05 +03:00
Afonsop 108ef045b8 web: worker-route WBLOCK/SAVEALL/ARCHIVE; close the follow-up review
WBLOCK, SAVEALL, and web ARCHIVE now serialize through the Web Worker
like SAVE/AUTOSAVE: prepare on the main thread, writer pass on the
worker, inline fallback on any worker failure. SAVEALL clears each
tab's dirty flag under the same unchanged-tab race guard the single
save uses; ARCHIVE assembles the ZIP in the completion handler so the
transmittal matches the snapshot taken at command time.

The review of the previous fix commit found the collision refusal
still REPLACED the registry bytes — the exact silent retargeting it
was meant to prevent. The refusal now keeps the old registration, the
extraction settings picker reports refused picks in the wizard's error
slot (previously silent, and the stale collision flag could surface as
a wrong-file warning on an unrelated browse), and the xref registry's
same-name ambiguity is documented as a browser fundamental (native
distinguishes by folder): newest registration wins consistently for
resolution, ARCHIVE, and XRELOAD — matching what the drawing displays.
Also: the Quit pending-close survives a successful web tab-save
(continue the quit instead of dropping it), the last ZIP64 sentinel
(central size) is rejected like the other four, and the new behaviors
gained regression tests (collision keeps old bytes, registry eviction,
including the deadlock fix for the guard-scoping that test exposed).

(cherry picked from commit b635d56c)

Ported onto main (#1717 review): ARCHIVE and SAVEALL serialize
document_for_save() like a single web Save; the ZIP is built with the
zip crate; WBLOCK keeps its native background-task title.
2026-10-12 12:02:04 +03:00
Afonsop 61c32c7f74 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 ZIP 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.

(cherry picked from commit 2e100634)

Ported onto main (#1717 review): the new ocs_save crate builds on the
pinned codec; the hand-written ZIP writer is replaced by the zip crate
already in the tree; ARCHIVE packs registered xref bytes too and
serializes document_for_save() like a web Save; the web save prepares
document_for_save() (prepare_document_for_save now takes the copy by
value, so no second clone); the wasm resolve_path consults the xref
registry, so every reference lookup — not just resolve — finds dropped
files; XRELOAD keeps main's undo, unload and stale-baseline handling;
native data extraction streams the source drawings one at a time.
2026-10-12 12:02:04 +03:00
Afonsop 9cd898a17a 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.

(cherry picked from commit c643a10e)

Ported onto main (#1717 review): conflicts resolved toward main — the
font-file lookup, PDF searchable text, purge service, block palette and
plot-style copy stay as they are on main; the web pieces (font registry
and aliases, session plot styles, SCRIPT/SAVEALL/CENTERLTYPEFILE picks,
block-from-file on the web) are layered on top.
2026-10-12 12:02:04 +03:00
Afonsop 69fa7cae4f 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).

(cherry picked from commit 3eab00bfbf)

Ported onto main (#1717 review): the five standalone tests/*_redteam.rs
files stay out — no test target builds them and they exercised copies of
the functions rather than the code itself.
2026-10-12 12:02:04 +03:00
Afonsop 7b467e16ea web: restore deletes its snapshot by open identity, not name
Second review pass found the restore marker matched WebFileOpened by
display name: any same-named open finishing while the recovered one
was still queued deleted the only copy. The snapshot key now travels
with the queued file (tagged queue entries), the marker records the
open id assigned at dispatch, and success, failure, and cancel all
match that id. Cancelling an unrelated open no longer clears a
pending restore marker.

Also: the boot scan removes dead index rows (missing or zero-byte
snapshot files) that no other path could discard, and the drop
listener install only latches when the listeners really registered.

(cherry picked from commit c8777a857b)
2026-10-12 12:02:04 +03:00
Afonsop 3023cefea4 web: harden drop-open and autosave after adversarial review
Review of the drop-open and autosave commits surfaced one blocker and
five majors; all are fixed here, and two claims in the autosave commit
message are corrected by this one: tab close does NOT clear a snapshot
(matches native .sv$ semantics — saving, restoring, discarding, exit,
and the capped boot scan do), and serialization runs on the main
thread between awaits, not off the paint path.

- The restore prompt can no longer appear with no entries (empty
  "Recover Drawing" modal on every boot once the autosave dir exists).
- Restore no longer consumes the snapshot before the open succeeds:
  the recovered drawing goes through the guarded drop queue, and the
  snapshot is deleted only after the tab opens; cancel or parse
  failure keeps the only copy.
- Saving during an autosave round defers its snapshot cleanup until
  the round finishes, so a stale round can no longer resurrect a
  snapshot newer than the save.
- The boot scan no longer deletes snapshots by mtime comparison
  (re-opening a drawing refreshes the recent copy's mtime, which
  silently destroyed wanted snapshots); GC is now a 16-entry cap with
  oldest-first eviction.
- OPFS write failures abort the writable stream (a leaked lock could
  stall the same-key writer and disable autosave for the session), and
  DOMException errors now carry their name/message.
- Empty (crash-window) snapshots are never offered; an unreadable one
  warns and stays discardable instead of looping forever.
- Drop-open polish: reads start inside the handler and complete in
  drop order (legacy WebKit safety, deterministic multi-file order),
  dragenter is canceled too, the install guard only latches on
  success, non-drawing drops don't steal propagation, a lock-poison
  panic can't cross into JS, and re-dropping an open drawing no
  longer risks clobbering an in-flight open (it re-queues).
- The model-space sheet floor is per-axis (a thin 8x400 mm profile
  keeps its length) and centers content on the enlarged page.

(cherry picked from commit 3edbc5b7c8)
2026-10-12 12:02:04 +03:00
Afonsop 12fd04790f 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.

(cherry picked from commit 101d167ebc)
2026-10-12 12:02:04 +03:00
Afonsop 6cb8c00fa0 web: enable STL/STEP export and OBJ import, drag-and-drop open
STLOUT/STEPOUT wrote via std::fs and IMPORTOBJ read via std::fs, all
of which fail on wasm32. The export pickers now mirror the PDF path
(pick_*_owned returns a name-only PathBuf; the browser download names
the file) and the write tail swaps fs::write for sys::download_bytes.
OBJ import reads the picked file's bytes directly, like the web open
picker.

Opening by drag-and-drop was dead on the web build: winit never emits
FileDropped there. A wasm-gated io::web_drop module installs DOM
dragover/drop listeners, filters .dwg/.dxf, reads the File bytes
async, and queues them; the existing 50 ms PollWebControl tick drains
the queue into the same open path the picker uses (progress overlay,
recovery, OPFS recent cache).

Also floors the model-space plot sheet: extents smaller than 10 mm
(or the 0x0 paper a fresh unsaved drawing carries) now fall back to
the standard sheet instead of producing a ~3 pt page.

(cherry picked from commit ad60be11ed)
2026-10-12 12:02:04 +03:00
Afonsop 38f0e2b25f app: wire web export, preview, and print flows through the plot UI
- plot dialog: the web build defaults to 'Save to PDF file...' (its only
  output choice); native dropdowns and defaults are untouched.
- on_plot_dlg_commit forces to_file on wasm so a stale page setup can no
  longer route the web build into the (unreachable) printer path; both
  preview branches now go through preview_page_pdf, which is identical to
  the old inline temp-PDF + OS-viewer flow on desktop.
- Print ribbon / PRINTALL on wasm render the PDF and open it in a tab
  instead of erroring; PrintAllPdf picks a virtual path and downloads.
- pdf_plot_options maps the dialog's Quality dropdown into PdfPlotOptions.

(cherry picked from commit 892d693d2d)
2026-10-12 12:02:04 +03:00
ramox81andClaude Opus 5.5 c8167c54ee -VIEW sEttings Background dialog and view backgrounds
Color, Gradient and Image at the background prompt open a Background
dialog (None / Solid / Gradient / Image, preview, solid colour, two- or
three-colour gradient with rotation, image file with position and
scale). Apply links the view to a new background object in
ACAD_BACKGROUND; restoring a view gives the model viewport a copy of
it (or none), and saving a view copies the viewport's background.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 10:50:00 +03:00
Hakan Seven 1333efcad2 Merge pull request #1549 from MarcelP102/codex/graphic-attributes-cleanup
[UX] Introducing graphic attributes panel

# Conflicts:
#	locales/ar-SA/opencadstudio.ftl
#	locales/bg-BG/opencadstudio.ftl
#	locales/cs-CZ/opencadstudio.ftl
#	locales/de-DE/opencadstudio.ftl
#	locales/el-GR/opencadstudio.ftl
#	locales/en-US/opencadstudio.ftl
#	locales/es-ES/opencadstudio.ftl
#	locales/fi-FI/opencadstudio.ftl
#	locales/fr-FR/opencadstudio.ftl
#	locales/hi-IN/opencadstudio.ftl
#	locales/hu-HU/opencadstudio.ftl
#	locales/it-IT/opencadstudio.ftl
#	locales/ja-JP/opencadstudio.ftl
#	locales/ko-KR/opencadstudio.ftl
#	locales/lo-LA/opencadstudio.ftl
#	locales/nl-NL/opencadstudio.ftl
#	locales/pl-PL/opencadstudio.ftl
#	locales/pt-BR/opencadstudio.ftl
#	locales/ru-RU/opencadstudio.ftl
#	locales/tr-TR/opencadstudio.ftl
#	locales/zh-CN/opencadstudio.ftl
#	locales/zh-TW/opencadstudio.ftl
#	src/app/mod.rs
#	src/app/update/dialog.rs
#	src/app/view/mod.rs
#	src/locale_catalog.rs
#	src/ui/dock.rs
2026-10-12 00:49:27 +03:00
Hakan Seven 7ca457a6cc Merge pull request #1702 from MarcelP102/Pallet-placement
[UX] Pallets you can dock, group and float (including LAYERS panel)
2026-10-12 00:27:53 +03:00
Hakan SevenandClaude Opus 5.5 7e0866e647 fix(input): fire a rebound SPACE only from the command line
#1713 dispatched a captured SPACE as a shortcut whenever it was rebound,
so with SPACE on FINALIZE a space typed into any other text field (layer
names, Properties, find) also ran FINALIZE, and the space it typed stayed
in the command line. Ask whether the command input has focus before
running the binding, and drop the space the field already inserted.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 23:38:06 +03:00
Hakan Seven 7b8397fe6b Merge pull request #1696 from YsDirector/pr/c11-next-frame
Capture after the next rendered frame

# Conflicts:
#	src/app/control/mod.rs
2026-10-11 23:30:20 +03:00
Claude 44f8f76ab3 Merge main into codex/graphic-attributes-cleanup
Resolve conflicts from main's rustfmt pass by taking main's versions of
command.rs, style.rs, view/mod.rs, scene/mod.rs, ui/window/mod.rs and
ui/properties.rs and re-applying this branch's Graphic Attributes changes.
Add the Graphic Attributes strings to the new lo-LA catalog.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01E1QnME6WJbD5iGFae1eeCb
2026-10-11 19:09:37 +00:00
Claude 2fca3fd18d Merge main into Pallet-placement
Resolved conflicts:
- app/update/mod.rs: keep the dock's dock_open_at_default for the XREF
  manager; take main's stable xref row identity (row_index(&id)).
- app/view/mod.rs: keep the edge layout from app::dock; drop main's copy
  of the old edge-stack code (main only reformatted it there).
- ui/window/layers.rs: keep main's new visible_indices next to the
  branch's view_panel.
Files the branch only reformatted take main's version. Adds the Lao
(lo-LA) translations for the new dock texts.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_013RyGiMXcsf9HTKfqDxaQGt
2026-10-11 18:44:00 +00:00
Claude bf1c4ddc74 style: apply cargo fmt across workspace (as on main)
Formats the branch the way main's 9cf9ff2 did, so merging main only
conflicts where the code itself differs.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_013RyGiMXcsf9HTKfqDxaQGt
2026-10-11 18:29:37 +00:00
Karim Jerbi 9cf9ff2c5c style: apply cargo fmt across workspace 2026-10-11 03:14:29 +01:00
Karim Jerbi 6fa11887c7 fix(xref): carry stable row identity in row messages
XrefManagerSelect/RightClick/RowOp/PathPick/FindReplace carried positional display indices that rescans could retarget. New XrefRowId{key,saved_path} (the identity refresh uses) in all 5 variants, resolved via row_index at dispatch with graceful no-op; panel selection stays index-based. display.index remains for view-time styling/lookup only. Tests: xref_rowop_unknown_id_is_noop (RED first); existing dialog/simulator/click tests migrated to ids. Full suite: 2044 passed / 0 failed / 25 ignored.
2026-10-11 02:17:18 +01:00
Karim Jerbi d1da26feb6 fix(properties): scope asym-scale opt-out per tab
props_asym_scale was app-global keyed by raw handle values, but fresh documents restart numbering at seed 1, leaking one tab's unchecked Uniform box into another tab reusing the value. The set moves to DocumentTab; all uses tab-scoped. Also removes a dead destructure left in overlay.rs. Test: asym_scale_opt_out_does_not_leak_across_tabs with handle-reuse premise assert (RED first). Full suite: 2025 passed / 0 failed / 25 ignored, zero warnings.
2026-10-10 16:14:22 +01:00