feat(io): autosave to .sv$ recovery files (SAVETIME command) (#205)

A periodic subscription (every SAVETIME minutes — default 10, 0 disables)
writes a <name>.sv$ recovery copy of each dirty, already-saved drawing at its
own DWG version. Non-destructive: it never touches the original file or the
dirty flag, and skips unsaved (path-less) tabs. The SAVETIME command
sets/queries the interval and persists it. Recovery files (.sv$ and .bak) open
through the normal Open dialog — the loader detects DWG vs DXF from the leading
magic bytes, and a "Backup / Autosave" filter lists them.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hakan Seven 2026-06-29 13:17:40 +03:00
commit 02d2764e45
7 changed files with 93 additions and 6 deletions

View file

@ -38,10 +38,10 @@ const PHASE_FINALIZING: u8 = 3;
pub async fn pick_open_path() -> Option<(PathBuf, u64)> {
let handle = rfd::AsyncFileDialog::new()
.set_title("Open CAD file")
.add_filter("CAD Files", &["dwg", "dxf", "bak", "DWG", "DXF", "BAK"])
.add_filter("CAD Files", &["dwg", "dxf", "bak", "sv$", "DWG", "DXF", "BAK"])
.add_filter("DWG Files", &["dwg", "DWG"])
.add_filter("DXF Files", &["dxf", "DXF"])
.add_filter("Backup Files", &["bak", "BAK"])
.add_filter("Backup / Autosave", &["bak", "sv$", "BAK"])
.add_filter("All Files", &["*"])
.pick_file()
.await?;
@ -169,9 +169,9 @@ pub fn load_file(path: &Path) -> Result<CadDocument, String> {
.map(|e| e.to_string_lossy().to_lowercase())
.unwrap_or_default();
// A `.bak` backup holds a verbatim DWG or DXF copy — detect the real format
// from the file's leading bytes rather than the `.bak` extension.
let effective = if ext == "bak" {
// A `.bak` backup or `.sv$` autosave holds a verbatim DWG/DXF copy — detect
// the real format from the file's leading bytes, not the extension.
let effective = if ext == "bak" || ext == "sv$" {
sniff_dwg_or_dxf(path)
} else {
ext.clone()