Commit Graph

10 Commits

Author SHA1 Message Date
Eliezer Hernandez f44155e098 i18n: Spanish translations for error and status-bar messages 2026-10-08 14:34:20 +07:00
bflatastic 649656b092 Fix #369: restart the web build's graphics after a lost device or context
When the browser dropped the page's WebGPU device or WebGL context, the
editor kept running but nothing it drew reached the screen: Chrome and
WebKit showed a frozen, stale canvas and Firefox a blank page, while
edits and Save went on.

- ui-egui: new `graphics` module. `GraphicsLoss` carries a loss report
  from any thread (wgpu's device-lost callback, the canvas's
  `webglcontextlost` event) to the UI thread. `VectorcraftApp::
  graphics_lost` writes Data Recovery copies of the modified documents at
  once. The app now notices when the host hands it a new egui context and
  uploads everything again (fonts and theme, the canvas, place cursor
  thumbnails); every per-thread texture cache (layer, symbol, swatch,
  style, asset and glyph thumbnails, previews, font samples, Navigator,
  Flattener Preview) is a `TexCache` that empties for a new context.
  Without this, the moved app drew stale or wrong textures and egui
  panicked on its missing font families.
- web: the shell watches both loss signals; on a loss it puts a fresh
  canvas in place of the dead one and starts the same `WebRunner` again,
  handing over the same app (documents, undo, panels), with services made
  for the new context and a status-bar note. If no new graphics can be had
  (or after three losses) it removes the stale canvas, lets go of the
  recovery copies and says to reload.
- engine: `recovery::leave` lets go of a session's area but keeps its
  copies, so the reloaded page offers them at once instead of after the
  heartbeat goes stale.
- desktop: eframe can't give a window a new device, so a lost device
  (driver reset) is logged, the copies are written and the status bar says
  to save and restart; closing then skips the Save Changes question it
  can't show.
- Tests: loss reporting across threads, `TexCache` emptying, the app
  uploading the canvas, a cached preview, theme and fonts again in a new
  context (fails without the fix), copies written on loss, and
  `recovery::leave` offering copies at once with both lock and heartbeat
  stores. docs/development.md (web section) and ROADMAP updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 07:18:32 +07:00
bflatastic 048cbc51b1 Fix #367: keep a paused browser tab's recovery copies
A heartbeat alone can't tell a crashed tab from one whose timers stopped in
the background, so another tab could discard (or restore) the copies of a
still-live document once its heartbeat went stale.

- Recovery stores without locks can now announce an area for as long as the
  app runs (RecoveryStore::announce/announced). The web shell implements it
  with the browser's Web Locks (navigator.locks through js_sys::Reflect): a
  tab holds a lock named after its area, which the browser releases only when
  the tab is gone. Lock snapshots are refreshed every 10 s (once before the
  first frame); a snapshot over a minute old counts every area as held.
- An area is gone only when no announcement holds it and its heartbeat is
  stale. Where the store can't tell (no Web Locks), the owner repairs: each
  heartbeat (also its first frame after a pause) and each recovery save write
  again the copies missing from its area, and a heartbeat newer than the one
  another tab wrote when taking the area over marks it running again.
- MemoryStore::announcing models Web Locks in tests; engine and UI tests cover
  the issue's sequence. Native folder locks are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 06:25:39 +07:00
bflatastic accfec7146 Fix #359: web drops land in the document they were dropped on
The web reads dropped files asynchronously, and the place inbox only kept
the drop point, so a file dropped on document A was placed into whatever
document was active when its bytes arrived. A drop on a canvas now carries
place::DropAt (the document's uid, the point, embed), and drop_files makes
that document active again before placing; when it has closed meanwhile,
nothing is placed and the status bar says so. The native drop path is
synchronous and goes through the same code, so it is unchanged in effect.

Test: a drop pending while another document opens lands in the first one
at the drop point; one whose document closes is not placed anywhere.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 05:28:50 +07:00
bflatastic ffce1cf686 M4.78: File > Print opens the Print dialog and prints through the system
The Print dialog (Ctrl/Cmd+P) edits the document's print settings in the
sections General, Marks and Bleed, Output, Graphics, Color Management,
Advanced and Summary, with a preview of each printed page under the section
list (drag the art to move its placement). It drives print.setup,
print.preview and file.print; the preview pages now carry their transform,
printed area and trim box. Print sends the job's PDF to the chosen printer
through the new PrintService (lp on macOS/Linux, the shell print verb on
Windows, a hidden frame and the browser's print dialog on the web); without
one, or with PDF File, it saves the PDF. Done only keeps the settings.
UI commands file.print, print.printers and print.printerSetup.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 16:30:35 +07:00
bflatastic 975067b44a M4.37: Data Recovery keeps copies of modified documents and offers them after a crash
- Every Preferences > File Handling > Data Recovery interval, modified documents
  changed since their last copy are written to the recovery store in the
  background (the same snapshot and encoder as Background Save; the status bar
  shows it). Complex documents (over 20,000 objects) are skipped while Turn off
  Data Recovery for complex documents is on.
- A copy goes when its document is saved, reverted, closed or undone back to its
  saved state, and when the app quits with nothing unsaved.
- Each running app keeps its copies in an area of its own: a sub-folder whose
  .lock file it keeps locked for its lifetime (File::try_lock), or on the web a
  heartbeat refreshed every minute. Only areas nobody holds (lock free, or no
  heartbeat for three intervals, at least three minutes) are offered, and an
  area is held while it is restored or discarded, so a second running instance
  never offers or deletes a live instance's copies. Empty leftover areas are
  tidied away.
- The next launch after a crash asks with the Recover Documents dialog (Restore /
  Discard / Cancel): restored documents open as "<name> [Recovered]", unsaved, and
  Save asks where to save them, suggesting their original file; the copy moves
  into the new app's area.
- Store: the recoveryFolder preference, else Data Recovery beside the desktop
  app's preferences; browser storage on the web (Services::recovery_store);
  tests use an in-memory store (with in-memory locks, or heartbeats).
- Engine: file.recovery.save, file.recovery.list, file.recovery.restore {file?},
  file.recovery.discard {file?}; DocState.recovered and DocState.recovery.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 15:24:16 +07:00
bflatastic 76b7d960f2 M4.25: Drop files on the canvas to place them; several placed files load the place cursor
Files dropped on the canvas of an open document are placed at the drop point (linked; Shift
embeds); dropped on the tab bar or with no document open they open and join Open Recent Files.
The canvas is outlined while files are dragged over it. On the web, drag events give the drop
point and the files arrive through the inbox.

File > Place picks several files. file.place.queue loads the place cursor (the `place` tool):
a click places the current file at 100% with its top-left corner there, a drag places it at the
dragged size with its aspect kept, the arrow keys cycle the files and Esc discards one; the
previous tool returns after the last. The cursor shows the file's thumbnail and a count badge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 08:42:30 +07:00
bflatastic 20e0935116 M4.24: File > Place as an engine command: images at their physical size, SVG/PDF/native as one group
file.place {path | name+dataBase64, link, template, replace, at, rect, page, crop} places a
file as one undo step without touching the clipboard. Raster images come in at 100% of the
resolution their file declares (PNG pHYs, JFIF/EXIF, TIFF, BMP; 72 ppi otherwise), linked to
their path by default; SVG becomes one group with its embedded images; a PDF/.ai page or a
native document's artboard becomes one clipped group. The images, symbols, patterns and
swatches the art uses join the document (renamed on conflict). Replace keeps the replaced
object's stacking place and transform; Template puts the art on a new locked template layer
below the current one. file.place.info and image.info report size, ppi and colour mode.

File > Place picks a file and opens the Place dialog (Link, Template, Replace); the web picks
through the inbox. The Control bar shows Linked File/Embedded, the file name, colour mode and
ppi of the selected image.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 08:42:30 +07:00
bflatastic 4d503c0327 M4.12: one loader and encoder for every frontend — engine fileio (load/encode/export/save, append-only FORMATS table, OPEN_EXTS, document.formats query, typed per-format options, parse_range); document.open reads native/legacy, SVG/SVGZ, PDF/.ai, .ait (opens untitled like native templates) and PNG/JPEG/GIF/WebP/TIFF/BMP; desktop, web, control channel, headless MCP and the CLI open and encode through it; open dialogs list "All readable files" plus one filter per format
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 11:36:51 +07:00
Brandon Thomas 4878f1c5dd Rename DrawCraft → VectorCraft: crates (vectorcraft-*), apps, binaries, types, env vars, docs, examples (.vectorcraft); old .drawcraft files and preferences still load. Fix: text bounds exact after opening a file; selection no longer marks a document modified (dirty = content differs from the saved document). Screenshots and renders regenerated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 14:30:09 -04:00