- Insert > Reference: the PDF Attach button becomes an Attach Underlay menu
(PDF / DWF / DGN); its face runs the last one chosen.
- DWFATTACH, DGNATTACH and ATTACH on a .dwf/.dwfx/.dgn open the Attach
dialog for that kind: sheets or models with thumbnails (one chosen),
placement, path type, file details with the sheet / model size, and for
DGN the conversion units (sub units offer 1 / sub units per master as the
scale).
- DGN V8 true colours come from the ExtendedColors record packed in the
non-model attributes stream; the colour word's high part numbers them.
- A DGN model's extent includes each text's box (its length along the
baseline and 1.5 x its height), as the reference sizes a model.
- DWFx text uses the family named in its embedded (deobfuscated) font.
- DWF contrast pivots at 0.73 once colours are turned over for a dark
background.
- Hairline strokes are drawn solid one pixel wide, and underlay rasters are
made at or below their size on screen so those lines stay unbroken.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- PDFOSNAP, DWFOSNAP and DGNOSNAP are separate; UOSNAP sets them all and
reads 2 while they differ. Enable Snap switches the selected kind's.
- DWFCLIP / DGNCLIP clip their own kind with the kind's prompts and message;
CLIP routes DWF and DGN underlays there; Enter at a corner asks again.
- The right-edge underlay tools show for DWF and DGN too (import is PDF
only).
- Underlay layers: DGN levels (V8 level table, "Default" listed as "0"; V7
used levels named from the level name table), hidden through the layer
override data with the DGN placeholder string first; a plotted DWF has
none.
- V8 colour read from its colour word (by level, index, or a true colour
drawn as its nearest index); 3D V8 elements drawn in plan (lines, line
strings, ellipses and arcs by their orientation, text); a model's extent
includes its text.
- DWF 6 TrueType text (font, height, rotation from the font opcode) and DWFx
glyph runs are drawn; the default W2D colour map is the measured one.
- Contrast pulls lightness towards a per-kind pivot as measured (PDF 0.5,
DWF 0.243, DGN 0.65).
- Zoom extents includes raster images and underlays whose frames are hidden.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Properties… in the Plot dialog froze the application on Windows.
`on_printer_properties` called `DocumentPropertiesW` with `DM_IN_PROMPT`
from inside the closure handed to `iced::window::run`, and iced runs that
closure synchronously on the winit event-loop thread. The driver's
property sheet wants to run its own modal message loop on that same
thread, so the two loops collide: the sheet is never created at all, the
window stops painting, and the thread spins at 100% of one core with no
way back.
Measured on Windows 11 x86-64: the pinned thread is the window's own UI
thread, and compstui.dll plus the driver's PrintConfig.dll are loaded
into the process, so execution does reach the driver's property-sheet
code and never leaves it. It reproduces with a physical printer as
readily as with Microsoft Print to PDF, so the mechanism was broken for
every printer, not only the virtual one named in the report.
The system's own printing-preferences UI is now started as a separate
process, which pumps its own messages and leaves ours free, so the
freeze is impossible by construction rather than merely unlikely. The
command that does it already existed for the case where no printer is
selected; it now serves a named printer too, and the in-process modal
path is gone. Preferences still reach the job: the sheet writes the
per-user DEVMODE that the plot path reads back through
`DocumentPropertiesW`, which was confirmed by changing the orientation
in the sheet and watching the value the plot path reads change with it.
The new test pins down that the printer name must reach `printui` as a
single argument: split across several it matches nothing and the process
exits 0 without drawing anything, which would be a failure with nothing
to show the user.
Fixes#1474
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Frames: follow the drawing state, DWF/DGN frames, image frame profile, frame-only picking
The codec [patch] to ramox81/cadcodec@ea5edaa is dropped: that commit landed as opencadcodec#63 and main already pins ad16215, which contains it.
DWFATTACH / -DWFATTACH and DGNATTACH / -DGNATTACH attach a DWF / DWFx sheet
or a DGN (V7 or V8) model with the reference's prompts; ATTACH routes the
file types to them. The sheet or model is read into vector paths, drawn as a
transparent underlay raster (contrast, fade, monochrome and background
adjustment apply) and offers its geometry to object snaps. Definitions are
kept in ACAD_DWFDEFINITIONS / ACAD_DGNDEFINITIONS, the External References
palette lists and reloads them, and image textures get line-keeping mip
levels so thin underlay lines stay visible when zoomed out.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An image's transparency flag decides whether transparent pixels show what
is behind the image or draw in their stored colour: the image pass and PDF
plots now follow it (it was ignored, so every PNG alpha showed through).
TRANSPARENCY selects images and asks for the mode, defaulting to the first
image's current one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The Frames list shows the drawing's frame state (hide / display and plot /
display but not plot / frames vary), follows commands, undo and document
switches, and lists the mixed state as a disabled item.
- DWFFRAME and DGNFRAME are frame variables of their own, kept in the header,
and FRAME sets them with the others.
- Out-of-range frame values say "Requires an integer between 0 and 2." and
ask again; a set value reports "Regenerating model.".
- IMAGEFRAME is stored in the image variables as bits (2 is written as 3) and
a drawing without them uses the profile value, which setting IMAGEFRAME
updates and the settings file keeps.
- Raster images, PDF underlays and wipeouts are picked on their frame only;
underlay snap geometry is never picked.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Attach PDF Underlay, Underlay Layers, PDF Import Settings and Import PDF
are laid out with rounded cards and accent titles, segmented controls for
path type, import layers and rotation, toggle chips for the on-screen
choices and import options, a page preview with previous/next for Import
PDF, always-visible file details, and a footer with help on the left and
the named action (Attach, Apply, Save, Import) on the right.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Shown and brought forward while only xrefs are selected: Edit Reference
In-Place, Open Reference, Create Clipping Boundary, Remove Clipping and
External References.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Pin cadcodec upstream at d290dbe (the merged underlay change) and
cadkernel at 4134ae3, which follows it; drop the fork patch, move the
plugin-API and web-worker crates and the Python plugin lock to the same
codec so one copy is built.
- "Import as block" with raster images left IMAGE entities without an
image definition: block definition now creates it like add_entity does,
through one shared helper.
- The three preview outlines (attach frame, clip boundary, import area)
stay f64 instead of casting world points to f32.
- Page size / total / PDF scale entries use the catalog's positional
placeholder; the bare {name} form failed to format in 20 locales.
- The Underlay Layers bulb is the SVG layer icon, not a text glyph.
- PDF dialogs use the shared dialog buttons and radios.
- Options opened from a PDF dialog clears its saved state on return.
- Paper-space underlay background reuses the model block handle already
resolved; dead `let _ = i` lines and a misplaced doc comment removed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- `XREF <args>` runs whole: probing the bare verb first now opens the
External References palette, which refreshed every entry and toggled
the palette before `XREF Reload A` / `XREF ?` ran.
- Update the two tests that encoded the old behaviour: `-XREF ?` prints
the reference table, and attaching a missing file is rejected.
- Drop the duplicate "Specify On-screen" catalog entry that shadowed the
translated common key, with its unused ftl keys.
- Move `remove_pipe_symbols`' doc comment back above its function.
- Note in `document_for_save` that resolved xref content stays out.
- Gate the desktop-only preview cap and tab index for wasm.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Localize block description placeholder via BlockDefinitionState field
- Add lookup mappings to locale_catalog for block definition dialog,
validation messages, on-screen prompts, and insertion units
- Add Fluent strings for all 41 keys across all 21 supported locales
Attach External Reference dialog after XATTACH / ATTACH; ATTACH routes
drawings, PDFs and images; placement prompts and messages follow the
reference (presets, Corner/XYZ, nonzero check, existing definition reuse,
missing file, self-reference); name keeps the file's case; overlay and
path type at attach; one undo step for definition and INSERT; -XREF option
prompt and `?` table; XREF opens the palette; ER alias; xref Properties
rows; dependent symbols flagged and remapped and resolved content left out
of the saved file; dialog_ok automation action; localized labels.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Tables copied from other applications into the plot styles folder were
listed but refused on selection, showing as "missing" with the reason
lost on a console: the loader failed hard on a checksum or a text length
that did not add up and on any non-UTF-8 byte — and those files carry
their descriptions in Windows-1252.
- decompress_ctb reads the file as it is: a checksum or length mismatch is
reported and read past (a genuinely cut-off file is still an error), a
zlib stream with a bad trailer is retried as raw deflate, and text that
is not UTF-8 is read as Windows-1252 (pens and colours are numbers and
unaffected; other code pages only show wrong characters in descriptions).
- PlotStyleTable carries load_warnings; every load site reports them on the
command line, so a table that needed tolerance says so.
- load_named explains itself: a .stb name says named tables are not
supported yet, a .ctb missing from the folder says which folder to copy
it into (instead of "Unknown built-in plot style").
- The Plot dialog shows the reason under the Table row (style_error) —
from a selection, from a page setup that names a table, and from the
Load… picker, whose error used to be swallowed.
- A table picked with Load… from outside the plot styles folder is copied
into it (unless a file of that name is already there), so it is listed
from then on and page setups naming it resolve after a restart.
Tests: flipped checksum, text length off by one, a short payload field,
Windows-1252 descriptions, a truncated stream, .stb and missing names.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The plot dialog asks CUPS printers for their sheets and margins; on Windows
the query was a stub, so every printer showed the paper catalogue with
unverified margins. The driver is now asked the way the plot dialog expects:
- windows_media (compiled and tested everywhere): the three parallel
DeviceCapabilities tables (ids, sizes in tenths of a millimetre, 64-WCHAR
names) become one PrinterMedia per distinct size — a rotated or
borderless twin folds onto its sheet, catalogue sheets keep their name,
unknown sizes carry the driver's — with the device's margins on every
entry and the driver's default sheet found by id or by user size.
- print_to_printer (Windows): DeviceCapabilitiesW for the tables, the
default DEVMODE for the default sheet, one portrait printer DC for the
unprintable margins (physical offsets and extents, per device — a
documented approximation). The DEVMODE fetch is shared with the print
path (with_printer_devmode).
- A direct print now asks the driver for the plot's own sheet (by id, or
as a user-defined size), so an A3 plot prints on A3 rather than shrunk
into the driver's default; a driver that refuses the sheet is asked for
the orientation alone.
- The capability cache remembers printers that could not be asked, so a
slow failing query runs once per session, not on every dialog open.
- PRINTERS <name> reports a printer's sheets, margins and default sheet.
Tests: names (NUL trim, full slot, lossy), media folding and dropping,
default by id / user size / none, DC margins, paper id lookup.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
On Windows a plot with no printer chosen went to the PDF application's
bare "print" verb — whatever that application considered its default —
and a viewer without a "printto" verb (Edge, store readers) silently
dropped the printer the dialog had chosen. The dialog also never named the
system default, and a failed printer enumeration left only "Default" and
"PDF" with the reason written to a console nobody sees.
- The default printer is looked up by name (GetDefaultPrinterW) both for
the dialog's "System default printer (…)" entry and before every job.
- A pure route decision (plan_print_route) resolves "Default" to that
name and uses the PDF application only when it registered a "printto"
verb for .pdf (AssocQueryStringW); otherwise the job goes straight to
the spooler through the existing GDI path. The report says which:
"Sent to printer: NAME (direct print | via the PDF application)".
- list_printers reports the system's own error; the plot dialog shows it
under the printer row and the command line warns once. A desktop without
CUPS is an empty list, not an error.
- PRINTERS command: default printer, listed printers (or the error) and,
on Windows, the print route — a report to paste into an issue.
- Properties… on the Default entry opens that printer's preferences sheet.
- GDI path: the driver's dmSize is kept (its private data lives behind
it), the print raster is no longer memoised for the session, and a
spooled temporary PDF is removed.
Tests: route planning (named / blank / no default / printto or not), the
PRINTERS command; the two Windows-only GDI tests pass the resolved name.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- PSETUPIN / -PSETUPIN: with no arguments a file picker offers the chosen
drawing's named page setups in the Plot dialog; `PSETUPIN <file>` opens
the same chooser on that file; `PSETUPIN <file> *` or
`PSETUPIN <file> name,name` import straight away. Same-named setups are
replaced, the import is one undo step, and the report counts both.
- The Plot dialog's page-setup list gains "Import…": the chooser shows the
source drawing's setups with a tick each, All, Import and Cancel, and
selects the first imported setup afterwards. The source is read off the
UI thread with the same reader the editor uses for its own setups
(`scene::document_page_setups`), and handles that only meant something
in the source drawing (plot view, visual style) are dropped on import.
- Options → General → Plotting: "Show the page setup for new layouts"
opens the Plot / Page Setup dialog whenever a layout is created
(persisted with the plot preferences).
Tests: the document reader, the command (all / named / unknown / missing
file / undo) against a drawing saved by the app, the chooser flow, and the
new-layout option. Nine new strings in all 21 locales.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Plot dialog:
- "Units" (millimetres / inches) is the page setup's own unit: the offsets
and the custom scale are shown in it, the stored ratio converts with it
so the plotted result and the paper space's unit stay the same
- "Custom: [paper] mm = [drawing] units" spells the picked scale in that
unit and accepts a typed ratio, which selects the list's name for it or
becomes a ratio entry of its own
- "Hide paperspace objects" is the page setup's plot-hidden flag; it is no
longer derived from the "Hidden Line" shade mode
- "Save changes to layout" (paper layouts, on by default) writes the dialog
into the layout when a plot is sent; previews never touch it
- offsets are labelled in the page-setup unit and kept in millimetres in
the file
System variables (typeable, through SETVAR, listed by SETVAR ?):
- PLOTOFFSET, PAPERUPDATE, PLOTROTMODE, PLOTTRANSPARENCYOVERRIDE and
BACKGROUNDPLOT live with the persisted plot preferences: the plot origin
counts from the printable corner unless PLOTOFFSET says the paper edge;
a printer that does not list the sheet either warns or switches to its
default sheet; transparency can be forced on or off; BACKGROUNDPLOT bit 1
is the dialog's background switch and bit 2 drives batch plots
- CTAB and TILEMODE read and switch the current layout
Tests cover every new control and variable; six new strings in all 21
locales; the Plot dialog grows to 940×690 for the two extra rows.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Comments and the Options help text describe the behaviour of commercial
solutions without naming any product. The command menu puts the command's
keywords directly under Enter / Cancel, with Recent Input and Snap
Overrides below them, so the choices that change from step to step are the
closest to the pointer.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The command menu now follows the commercial solutions' layout row for row: Enter, Cancel,
Recent Input ▸, the command's keywords, Snap Overrides ▸ (every object snap
with its glyph, Mid Between 2 Points, None, Osnap Settings…), then Pan and
Zoom. Pan / Zoom run transparently while a command is active ('PAN,
'ZOOM …): navigation modes leave the command untouched and an interactive
ZOOM Window parks it and resumes it afterwards. Rows carry an icon gutter
so labels align; the Enter row shows a default value ("<0.5000>") as its
hint instead of a second finish row.
ARC gets the commercial solutions' prompt flow with all keywords: start [Center], second
point [Center/End], end; after Center: end [Angle/chord Length]; after
End: center [Angle/Direction/Radius]; Enter continues tangent. The
previous default (Center/Start/End with method codes) stays as ARC_CSE.
Object snaps gain a one-shot "None" override. New strings in all locales.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Every new UI string (menu rows, Options → Right-click Customization, the
PLINE sub-prompts and keyword labels, MOVE/COPY/OFFSET prompts) gets its
Fluent attribute in all 21 locales plus the source → attribute rows in
locale_catalog.rs; the SETVAR help line lists SHORTCUTMENU and
SHORTCUTMENUDURATION. Tests that compared prompt text now check command
state so they pass under any UI language.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
MOVE: Displacement, Enter at the second point uses the base as the vector.
COPY: Displacement, mOde (Single/Multiple), Undo of the last copy.
FILLET: Undo of the last fillet (the command refreshes its entity cache
through the new CadCommand::on_document_undone hook after the host undoes
the document step). CHAMFER: Multiple and Undo. OFFSET: Erase (the offset
replaces its source), Layer (Current/Source), Undo, Exit.
POLY3D, TRACE, MIRROR and MEASUREGEOM expose the keywords they already
accepted as options, so the context menu and option buttons list them.
The pinned prompt now keeps informational bracket groups such as
"[3 objects]" and only drops real option listings when the buttons take over.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Properties… next to the printer used to open the CUPS web interface in a
browser. It now edits the driver options of the *selected* printer the
way LibreOffice's Properties… does, without leaving the plot dialog:
- CUPS platforms (Linux, any desktop; macOS): an in-line editor lists the
printer's PPD options as `lpoptions -l` reports them — media type,
colour mode, quality, duplex, trays, scaling — with readable choice
labels, Reset / Cancel / Apply. Apply keeps only the choices that differ
from the driver defaults, per printer, in the plot config, and every job
for that printer passes them as `lp -o key=value`. Where CUPS cannot
answer, the desktop's printer settings panel opens instead of a browser.
- Windows: the driver's document-properties sheet (`DocumentPropertiesW`
with prompt, owned by the main window) and, on OK, the result stored as
the user's printing preferences for that printer (`SetPrinter` level 9),
which the job honours. `printui /e` (printing preferences) replaces the
device-properties sheet as the fallback.
The desktop print portal was tried first and dropped: GTK 4's print
dialog loses the preselected printer when the default queue is listed
before it, so the dialog always opened on the wrong printer.
Dialog layout: the modal grows to 940×620; Printer / plotter and Paper
size share a line with their pickers (long queue and media names now fit);
Copies + Properties… and Custom… sit beneath; the custom-sheet editor
labels each margin (Left / Right, Top / Bottom) and shows the unit.
Also recognises `none_device`, AutoCAD's stored spelling for "no plotter",
and writes it back unchanged.
Tests: `lpoptions` parsing and keyword humanising, the editor flow (load,
stale answer ignored, set, apply, remember, reset, error, cancel), device
name round-trips including `none_device`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
AutoCAD lets a plotter configuration carry user-defined sheets; a drawing
then names them like any other media (`Roll_24_(609.60_x_1500.00_MM)`).
The plot dialog now defines such sheets inline, under the Size row:
name, width/height, unit, and the printable margins in that unit. Added
sheets are selected at once, offered for every device after the device's
own list, remembered in the plot config across sessions, and written to
the layout with their own margins (a printer that reports the same size
still wins with its hardware margins). A selected user-defined sheet can
be removed; the sheet itself stays selectable in the drawing that uses it.
`CustomPaper` / `Margins` live in the paper catalogue with serde support;
`PlotDevice::margins_mm` consults the user's definitions. The Plot
dialog's form helpers (button and field styles, dividers, `label :
control` rows) move to `ui::style::form` so the next dialogs share them
instead of copying.
Tests: draft validation and seeding, catalogue naming of user sheets,
device margins with user definitions, and dialog round-trips (add,
select, persist, write, replace, remove, refuse an invalid size).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A page setup names its plotter, and AutoCAD looks that name up in its own
configurations: `DWG To PDF.pc3` for PDF output, the spooler name for a
system printer, `None` when nothing is assigned. Drawings saved here used
to carry the dialog's label ("Save to PDF file…"), so AutoCAD reported a
missing device on every layout plotted to PDF from Open CAD Studio.
- New `io::plot_device`: `PlotDevice { None, Pdf, Printer }` with AutoCAD's
spellings, a reader for stored names that keeps any plotter
configuration AutoCAD wrote (an HP `.pc3`, `DWF6 ePlot.pc3` — plotted to
PDF here but preserved for AutoCAD) and upgrades the legacy label.
- Printable areas: the PDF driver's margins per sheet variant (full bleed
edge to edge; standard / expand insets provisional until read off
AutoCAD's plotter configuration), and for CUPS printers the media and
margins the queue reports over IPP (`ipptool` `media-col-database`),
parsed once per printer and cached. Windows enumeration follows with
native GDI printing.
- Page setups now store the device in AutoCAD's spelling and the margins
of the chosen device/sheet, keeping the drawing's own values while
neither changed (AutoCAD's `PAPERUPDATE` behaviour). The paper-space
printable-area rectangle follows.
- Plot dialog: the default entry names the printer it resolves to; a
printer that answered lists its own sheets with a note; the media query
runs in the background and answers for a since-deselected printer are
dropped.
Tests: device names, stored-name resolution, PDF margin table, IPP media
parsing (sizes, margins, borderless folding, default sheet), PWG names,
plus dialog ↔ layout round-trips for PDF, legacy label, preserved plotter
configuration, sheet change → margins, and printer selection.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Remove the dead catalog entry for the deleted XREF dead-end text and add the 389 source strings missing from the catalog (data extraction, drafting settings, panels, commands). Every locale gets the English text as placeholder under common/insert following existing practice. scripts/test_locales.py passes (5077 lookups).
Row right-click menu is exactly Open, Attach..., Unload, Reload, Detach, Change Path Type group with Make Absolute, Make Relative, Remove Path, Select New Path, Find and Replace. Change Path Type renders as indented group. Open uses OCS for DWG and OS default for images and PDFs. Preview decodes on selection change, not only at refresh. Details shows placed display width and height for image instances.
External References dock default 360 to 460 for the column-rich table. Attach split button reads Attach DWG with Attach Image and Attach PDF options, all menu rows full width. List view shows one distinct glyph and theme color per reference status.
Right-click actions bound to the row under the cursor, split-button toolbar groups with shared borders and hover states, and a notice when an operation needs a selection first.
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 <noreply@anthropic.com>
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 <noreply@anthropic.com>