From ec410552cfbe2b153b7b3d77aa358f72872ee9a2 Mon Sep 17 00:00:00 2001 From: Fabian Grajales Date: Sat, 17 Oct 2026 13:07:36 -0500 Subject: [PATCH] Export: a progress dialog with Cancel for background exports While an export runs in the background (Background Export), a dialog shows what is being exported and how far it has got: artboard by artboard for a raster export of several artboards, busy otherwise. Cancel (also Esc) stops it and writes no file; Continue in Background leaves it to the status bar. It opens after 0.4 s and never over another dialog. Built on the shared vectorcraft_doc::cancel (#2546): fileio::progress::Progress wraps its CancelToken and a progress sink, so a cancelled export stops drawing and keeps no half-drawn pattern tile or effect raster. - ui: background jobs carry their Progress; export_progress draws the dialog; UI commands file.cancelExport and file.exportInBackground. - i18n: the strings in every language with complete menus, Danish, Dutch and Indonesian included; "{n} more exports waiting" is a plural (trn). - docs: ui-parity, gaps (G12) and ROADMAP rows. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Dt49z1ubK9x1bPq51APgz3 --- ROADMAP.md | 4 +- crates/engine/src/cmd/fileio/mod.rs | 1 + crates/engine/src/cmd/fileio/progress.rs | 82 ++++++++++ crates/ui-egui/src/background.rs | 53 +++++- crates/ui-egui/src/chrome.rs | 2 +- crates/ui-egui/src/export_progress.rs | 196 +++++++++++++++++++++++ crates/ui-egui/src/i18n/bg.tsv | 6 + crates/ui-egui/src/i18n/cs.tsv | 6 + crates/ui-egui/src/i18n/da.tsv | 6 + crates/ui-egui/src/i18n/de.tsv | 6 + crates/ui-egui/src/i18n/el.tsv | 6 + crates/ui-egui/src/i18n/es.tsv | 6 + crates/ui-egui/src/i18n/fi.tsv | 6 + crates/ui-egui/src/i18n/fr.tsv | 6 + crates/ui-egui/src/i18n/hu.tsv | 6 + crates/ui-egui/src/i18n/id.tsv | 6 + crates/ui-egui/src/i18n/it.tsv | 6 + crates/ui-egui/src/i18n/ja.tsv | 6 + crates/ui-egui/src/i18n/ko.tsv | 6 + crates/ui-egui/src/i18n/nl.tsv | 7 +- crates/ui-egui/src/i18n/pl.tsv | 6 + crates/ui-egui/src/i18n/pt-br.tsv | 6 + crates/ui-egui/src/i18n/ru.tsv | 6 + crates/ui-egui/src/i18n/sv.tsv | 6 + crates/ui-egui/src/i18n/uk.tsv | 6 + crates/ui-egui/src/i18n/vi.tsv | 6 + crates/ui-egui/src/i18n/zh-hans.tsv | 7 +- crates/ui-egui/src/i18n/zh-hant.tsv | 6 + crates/ui-egui/src/io.rs | 50 +++++- crates/ui-egui/src/lib.rs | 2 + crates/ui-egui/src/menus.rs | 15 ++ crates/ui-egui/src/tests_background.rs | 28 ++++ docs/gaps.md | 4 +- docs/ui-parity.md | 4 +- 34 files changed, 560 insertions(+), 15 deletions(-) create mode 100644 crates/engine/src/cmd/fileio/progress.rs create mode 100644 crates/ui-egui/src/export_progress.rs diff --git a/ROADMAP.md b/ROADMAP.md index a58c0c66d..9bd1d8bd2 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -9,7 +9,7 @@ **Stage: alpha** · next: beta, ~13 points (ready for real work ~62% → ~75%, and reliable `.ai` exchange) and ~170–270 h away -> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-18 · **Change:** minor (about 40 community PRs of 2026-10-18: Word .docx placed as area type, CCITT Group 3 TIFF, Roman Hanging Punctuation, blend spines edited with the Anchor Point tool, shadows and glows in stack order, coloured 3D lights, a Dutch catalog completed; 3D lighting: one to ten colored lights, smooth colored Inflate, legacy white-light appearance preserved; surface batching and restored seam minimum; scores and estimates unchanged; about 145 community PRs of 2026-10-17: image-decode overflow guards, opaque floating panels, MCP export cancel and progress, editable Illustrator EPS import, scoped `doc_inspect`, Dutch, Danish and Indonesian, Simplified Chinese messages, screen-reader names; headline numbers unchanged; about 160 community PRs of 2026-10-15/16: PDF import crash fixes, Dimension tool, legacy `.ai` save, Intertwine, web pen input, Image Trace fixes, `.ai`/EPS/PDF import fidelity, Bulgarian, Greek, Swedish and Finnish catalogs; headline numbers unchanged) · **Target:** Adobe Illustrator 2026 (30.x) +> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-18 · **Change:** minor (Background Export progress dialog with Cancel, #2564; about 40 community PRs of 2026-10-18: Word .docx placed as area type, CCITT Group 3 TIFF, Roman Hanging Punctuation, blend spines edited with the Anchor Point tool, shadows and glows in stack order, coloured 3D lights, a Dutch catalog completed; 3D lighting: one to ten colored lights, smooth colored Inflate, legacy white-light appearance preserved; surface batching and restored seam minimum; scores and estimates unchanged; about 145 community PRs of 2026-10-17: image-decode overflow guards, opaque floating panels, MCP export cancel and progress, editable Illustrator EPS import, scoped `doc_inspect`, Dutch, Danish and Indonesian, Simplified Chinese messages, screen-reader names; headline numbers unchanged; about 160 community PRs of 2026-10-15/16: PDF import crash fixes, Dimension tool, legacy `.ai` save, Intertwine, web pen input, Image Trace fixes, `.ai`/EPS/PDF import fidelity, Bulgarian, Greek, Swedish and Finnish catalogs; headline numbers unchanged) · **Target:** Adobe Illustrator 2026 (30.x) VectorCraft is a clean-room, open-source, pure-Rust reimplementation of the Adobe Illustrator workflow. It runs on macOS, Windows, Linux, FreeBSD and the web (WASM), and agents can drive all of it over MCP, the CLI and a JSON @@ -135,6 +135,7 @@ Ranked; detail in [docs/roadmap.md](docs/roadmap.md) and [docs/gaps.md](docs/gap | Date | What landed | |---|---| | 2026-10-18 | About 40 community PRs landed in one batch through the full gate, with our fixes on top. New: Word `.docx` files place as area type with their formatting (#2766), CCITT Group 3 TIFF files open (#2770), Paragraph panel menu › Roman Hanging Punctuation (#2775), the Anchor Point tool edits blend spine points and handles (#2643, fixes #2544), shadows and glows each draw from the art with the effects before them, without the old exponential cost (#2395), one to ten coloured lights in 3D effects (#2706), Artboard Options X and Y with a reference point (#2739), more Contextual Task Bar actions (#2471), Optical Margin Alignment for a whole thread (#2773), History panel Step Backward/Forward and Set History Limit (#2764), Edit › Undo and Redo name the step as the History panel does (#2774), bounding-box handles snap to the grid, pixels and guides (#2715), Ctrl/Cmd+A in the Artboards panel (#2761), Tabs panel menu items (#2742), Import Data Sets defines variables from header cells (#2736), a Linux option to use X11 when a pen is attached (#2717). Fidelity and robustness: variable fonts named by their default weight and fallback glyphs keep bold and italic (#2733, #2732), legacy `.ai` layer names in Windows Latin-1 (#2722), WMF window mapping (#2721), Magic Wand inside isolation (#2720), type values out of range in files are clamped (#2728, #2757), raster effects stay responsive in drags (#2745), exports and Save for Web report undecodable images (#2762, #2737, #2684), Windows-safe exported file names (#2738), a marquee skips a Shaper Group's originals (#2772). Languages: the Dutch catalog completed (#2730). Headline numbers unchanged (estimated). | +| 2026-10-17 | Background Export shows a progress dialog: the file being exported, the artboard count and a bar, Cancel (Esc) and Continue in Background. It runs on the shared cancel token of #2546, so a cancelled export writes no file and keeps no half-drawn pattern tile or effect raster; translated in every language with complete menus (#2564). Headline numbers unchanged. | | 2026-10-17 | About 145 community PRs landed in six batches, each through the full gate, plus our fixes on top. Crashes and hostile input: an image's RGBA size is checked from its header before decoding, without overflowing, in the renderer, sampling, recoloring and Crop Image (#2442, #2468, #2407), PDF Add Around and huge art are clamped (#2440), graph axis labels past i64, `+` in hex colours and XML ids are handled (#2610, #2470, #2447), Document Raster Effects resolutions open in range (#2579), Batch names stay in their folder (#2504). Regressions: floating panel groups are opaque again while the docked card keeps its rounded corners (#2499), the Wayland pointer returns over panels (#2502). New: MCP cancels long exports, answers while one runs and reports progress (#2546), `doc_inspect` takes ids, kind and paging (#2543), raster export of a page rectangle (#2511), `text.create` returns bounds (#2478), PDF presets and ICC profiles persist (#2547), screen-reader names for widgets (#2611), Expand on Intertwine (#2622, fixes #2540), Duplicate/Copy/Cut of several artboards (#2567), print presets by name (#2607), TSV data sets (#2608), planar TIFF (#2598), Alt-click a swatch onto a gradient stop (#2623), Shift-click ranges in Links, Graphic Styles and Libraries (#2612, #2613, #2618), Space+Shift rotates the view (#2601), Presentation Mode keys (#2583), Small Caps and super/subscript buttons (#2508). Fidelity: editable vector art from Illustrator EPS (#2571), `.ai` Drop Shadow Darkness (#2513), overprint preview with Darken (#2514), Generic CMYK black round-trips (#2503), PNG sRGB chunk (#2519), kerning at a caret (#2518), tab widths in justified area type (#2487), Transform copies keep their own gradients (#2486), smooth Zig Zag (#2475), Live Corners round up to their neighbours (#2484), kerning across style runs (#2554), SVG letter spacing on justified lines (#2574), Dimension tool snapping (#2482), gradient chips (#2495), Transform panel flips and percentages (#2400, #2404). Later the same day: PostScript `.ps` and `.epsf` files open (#2461), EPS images with CCITTFax compression draw (#2445), Illustrator 3 EPS without layers (#2436), `.ai` type opacity and blend modes (#2420), envelope meshes and compound holes in `.ai` (#2410, #2444), CMYK image soft masks in PDF export (#2448), Show Transparency Grid saved with the document (#2488), Save Selection in the Layers panel menu (#2483), Character and Paragraph Reset Panel (#2479, #2438), Cmd/Ctrl+Shift double-click releases a guide (#2467), Shift+Enter keeps a field active (#2431), Shift keeps reshaped handles perpendicular (#2425), emptied type keeps its style (#2464), Smart Blur radius (#2443), freeform gradients recolour (#2397), mesh slivers (#2456), Recolor wheel handles in CMYK (#2454), and six Image Trace fixes (#2387, #2584, #2586–#2589). Fifth batch: Optical Margin Alignment (#2563), Window › Blend panel (#2573), graph column designs (#2406), Live Paint Gap Options (#2424), the SVG Interactivity panel (#2437), RTF placed as area type (#2524), the Touch Type tool (#2469), pattern fills expand into clipped tiles (#2616), Save and Export ask before replacing a file an added extension names (#2619), the Artboard tool's Control bar and Contextual Task Bar (#2559, #2556), Alt-click New Artboard for its options (#2496), Shift-click in Brushes (#2562), the Move dialog remembers the last move (#2542), PDF export uses the document's assigned profiles (#2536), Printer Setup opens the selected printer's preferences on Windows (#2577). Sixth batch: Effect › Artistic › Cutout, Neon Glow and Poster Edges (#2453), View › Rulers › Change to Global / Artboard Rulers (#2416), Symbol Options 9-slice guides (#2512), Create Gradient Mesh on an embedded image (#2538), Rotate View cursors and the Mac trackpad rotation gesture (#2594, #2644), choice parameters as dropdowns in the inline effect editor (#2582), Group Selection picks and moves one path of a compound path (part of #2441), Shift-click ranges in the Artboards panel (#2626), Lock Variables (#2631), a layer dropped under the last row goes to the bottom (#2632), and robustness work: huge selections draw bounded overlays (#2635, #2645, part of #2460), values out of range in presets and files are clamped (#2656), whole-number checks for counts (#2638, #2659), exports warn about overset type (#2628), symbol instances measured by their art (#2614), colour-managed Eyedropper in CMYK documents (#2664). Later: Effect › Rasterize (#2409), variable-font axis sliders in the Character panel (#2710), Image Trace Create Strokes on an exact distance transform with Guo–Hall thinning and a skeleton graph (#2703–#2705), Auto Color keeps rare colours (#2702), large images trace with a warning (#2700), a 1e9 pt input bound on Reshape Segment, Convert Anchor Point and the Pencil (#2709), task-bar Connect, Corner and Smooth for selected anchors (#2697), PDF 0 w strokes import as hairlines (#2696), threading text from the out port (#2713, part of #2572), a panel per swatch library with Persistent (#2714, #2603), installed plug-ins kept across restarts (#2695, #2541). Languages: Dutch, Danish and Indonesian complete (#2393), Simplified Chinese status and error messages (#2578), translated file filters and presets (#2590). Headline numbers unchanged (estimated). | | 2026-10-16 | Symbol Options › Export Type (Graphic or Movie Clip, default Movie Clip) with Registration; `symbol.options` / `symbol.list` (G5). | 2026-10-15 | Cursors: 36 more tool states get cursors of their own. Node editing follows the public manual (Direct Selection's anchor squares, Reshape Segment, Group Selection's plus, the arrow hollow on a snap, the Alt-copy arrowheads), the Anchor Point caret, the Curvature pen and Free Transform's side-shear arrows are drawn; Lasso, the freehand, symbolism and other tools borrow a drawn cursor until theirs is drawn. `crates/tools/src/cursors.toml` lists every cursor for whoever draws them, and `cargo xtask cursors` warns about one missing from it. Scores unchanged. | @@ -183,6 +184,7 @@ The full pre-standard record of what landed is kept in [docs/roadmap.md](docs/ro | 2026-10-17 | minor | Colored Inflate: shared membrane normals and smooth RGB vector ramps replace flat polygon bands; channel groups are batched per solid-fill surface. White-light Inflate retains its original polygons, paint composition and seam joins; the 0.05 seam minimum is restored. Scores and hour estimates unchanged. | | 2026-10-16 | minor | 3D lighting: one to ten directional lights, a selectable light list, Add Light and Delete Light, independent settings and artboard gestures; ambient applied once and smooth RGB vector export; scores and hour estimates unchanged | | 2026-10-16 | minor | 3D lighting: opaque RGB Color for the shared directional light, with neutral ambient, preview/reset controls and smooth vector Revolve shading; scores and hour estimates unchanged | +| 2026-10-17 | minor | Progress log row for the Background Export progress dialog (#2564); headline numbers unchanged. | | 2026-10-14 | minor | G11: open-path anchor Delete removes adjoining segments without bridging the gap; explicit Remove Anchors keeps its reconnect/refit behavior. Scores and hours unchanged. | | 2026-10-14 | minor | Document fonts register every explicitly supplied craft-fonts family after the existing Japanese, Arabic and Thai prefix; optional empty input remains supported. Headline scores and estimates are unchanged. | | 2026-10-14 | minor | Progress log rows for the later merges of 2026-10-14 (#1630–#1875 as listed there); headline numbers unchanged. | diff --git a/crates/engine/src/cmd/fileio/mod.rs b/crates/engine/src/cmd/fileio/mod.rs index 241986cba..5cc8e319f 100644 --- a/crates/engine/src/cmd/fileio/mod.rs +++ b/crates/engine/src/cmd/fileio/mod.rs @@ -40,6 +40,7 @@ pub mod pdf; mod pdfimport; pub mod pngtext; pub mod ppi; +pub mod progress; pub mod psdread; mod save; mod screens; diff --git a/crates/engine/src/cmd/fileio/progress.rs b/crates/engine/src/cmd/fileio/progress.rs new file mode 100644 index 000000000..a69cf96a8 --- /dev/null +++ b/crates/engine/src/cmd/fileio/progress.rs @@ -0,0 +1,82 @@ +//! Progress and Cancel of an export running off the UI thread (Background Export), on the shared +//! [`vectorcraft_doc::cancel`] mechanism. +//! +//! The frontend makes a [`Progress`], runs the export inside [`scope`] on its worker and reads the +//! counts from the UI thread to draw them; its Cancel button calls [`Progress::cancel`]. Inside the +//! scope the encoders that work in steps (raster formats: one artboard at a time) report each step +//! through [`vectorcraft_doc::cancel::progress`] and stop at the next check once cancelled; the +//! renderer stops drawing and keeps no half-drawn pattern tile or effect raster. Encoders that +//! make one file in one go report nothing: the frontend shows them as busy and checks +//! [`Progress::cancelled`] itself before writing, so a cancelled export never writes a file. + +use std::rc::Rc; +use std::sync::Arc; +use std::sync::atomic::{AtomicU64, Ordering}; + +use vectorcraft_doc::cancel::{self, CancelToken, ProgressSink}; + +/// How far an export has got, shared between the worker and the UI thread. +#[derive(Debug, Default)] +pub struct Progress { + token: CancelToken, + done: AtomicU64, + total: AtomicU64, +} + +impl Progress { + pub fn new() -> Arc { + Arc::new(Self::default()) + } + + /// `(steps done, steps in all)`; `total` is 0 while the export can't say (one file made in one go). + pub fn counts(&self) -> (u64, u64) { + let total = self.total.load(Ordering::Relaxed); + (self.done.load(Ordering::Relaxed).min(total), total) + } + + /// The share of the export done, 0 to 1; `None` while the export can't say. + pub fn fraction(&self) -> Option { + let (done, total) = self.counts(); + (total > 0).then(|| done as f64 / total as f64) + } + + /// Ask the export to stop: it writes nothing more, and a render under way stops drawing. + pub fn cancel(&self) { + self.token.cancel(); + } + + pub fn cancelled(&self) -> bool { + self.token.is_cancelled() + } +} + +/// Run `f` with `progress`'s token in effect on this thread and the steps reported to it. +pub fn scope(progress: &Arc, f: impl FnOnce() -> R) -> R { + let to = Arc::clone(progress); + let sink: ProgressSink = Rc::new(move |done, total, _| { + to.total.store(total, Ordering::Relaxed); + to.done.store(done, Ordering::Relaxed); + }); + cancel::scope(&progress.token, Some(sink), f) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn a_raster_export_counts_its_artboards_and_stops_once_cancelled() { + let mut doc = vectorcraft_doc::Document::new(100.0, 100.0); + let second = doc.artboards[0].clone(); + doc.artboards.push(second); + let p = Progress::new(); + assert_eq!(p.fraction(), None, "nothing reported yet"); + let png = scope(&p, || super::super::encode_all(&doc, "png", &serde_json::json!({"useArtboards": true}))); + assert!(png.is_ok(), "{:?}", png.err()); + assert_eq!(p.counts(), (1, 2), "the step under way when the last artboard started"); + p.cancel(); + let r = scope(&p, || super::super::encode_all(&doc, "png", &serde_json::json!({"useArtboards": true}))); + assert!(matches!(r, Err(crate::EngineError::Cancelled)), "{:?}", r.err()); + assert!(!cancel::is_cancelled(), "the scope ended"); + } +} diff --git a/crates/ui-egui/src/background.rs b/crates/ui-egui/src/background.rs index 157769910..bf3ee1285 100644 --- a/crates/ui-egui/src/background.rs +++ b/crates/ui-egui/src/background.rs @@ -6,9 +6,11 @@ //! It needs a thread-safe writer from the host ([`crate::Services::write_shared`], desktop); //! without one (the web, which downloads) the work runs at once, as with the preference off. +use std::sync::Arc; use std::sync::mpsc::{Receiver, Sender, channel}; use serde_json::{Value, json}; +use vectorcraft_engine::cmd::fileio::progress::Progress; use vectorcraft_engine::file_access::ScopeHandle; use crate::VectorcraftApp; @@ -50,9 +52,24 @@ pub struct Job { pub label: String, /// The document the job reads (`DocState::uid`), if any. pub doc: Option, + /// How far an export has got, and its Cancel ([`crate::export_progress`] shows it). + pub progress: Option>, + /// Continue in Background: the progress dialog no longer shows this job. + pub hidden: bool, then: Then, } +impl Job { + pub fn id(&self) -> u64 { + self.id + } + + /// Was the job cancelled (it writes nothing more; its result is still on its way)? + pub fn cancelled(&self) -> bool { + self.progress.as_ref().is_some_and(|p| p.cancelled()) + } +} + /// The worker and the jobs it has not finished. #[derive(Default)] pub struct Background { @@ -93,6 +110,26 @@ impl Background { pub fn busy_with(&self, uid: u64) -> bool { self.jobs.iter().any(|j| j.doc == Some(uid)) } + + /// Cancel every export in progress → how many there were. + pub fn cancel_exports(&mut self) -> usize { + let mut n = 0; + for p in self.jobs.iter().filter(|j| !j.cancelled()).filter_map(|j| j.progress.as_ref()) { + p.cancel(); + n += 1; + } + n + } + + /// Hide the exports in progress from the progress dialog (they go on) → how many there were. + pub fn hide_exports(&mut self) -> usize { + let mut n = 0; + for j in self.jobs.iter_mut().filter(|j| j.progress.is_some() && !j.hidden && !j.cancelled()) { + j.hidden = true; + n += 1; + } + n + } } /// Run `work` on a worker thread when `enabled` and the host can write from one, else now; `then` @@ -106,6 +143,20 @@ pub fn run( doc: Option, work: impl FnOnce(Writer) -> Result + Send + 'static, then: impl FnOnce(&mut VectorcraftApp, Result) -> Result + 'static, +) -> Result { + run_with_progress(app, enabled, label, doc, None, work, then) +} + +/// [`run`] for work that reports to `progress` (an export): the progress dialog shows it while it +/// runs in the background, with Cancel. +pub fn run_with_progress( + app: &mut VectorcraftApp, + enabled: bool, + label: String, + doc: Option, + progress: Option>, + work: impl FnOnce(Writer) -> Result + Send + 'static, + then: impl FnOnce(&mut VectorcraftApp, Result) -> Result + 'static, ) -> Result { let mut work: Work = Box::new(work); let shared = app.services.write_shared.clone().filter(|_| enabled && app.services.download.is_none()); @@ -116,7 +167,7 @@ pub fn run( Ok(()) => { let bg = &mut app.background; bg.next_id += 1; - bg.jobs.push(Job { id, label: label.clone(), doc, then: Box::new(then) }); + bg.jobs.push(Job { id, label: label.clone(), doc, progress, hidden: false, then: Box::new(then) }); if label.is_empty() { return Ok(json!({ "background": true })); } diff --git a/crates/ui-egui/src/chrome.rs b/crates/ui-egui/src/chrome.rs index b49b99825..ebab4f6e9 100644 --- a/crates/ui-egui/src/chrome.rs +++ b/crates/ui-egui/src/chrome.rs @@ -739,7 +739,7 @@ pub fn status_bar(app: &mut VectorcraftApp, ui: &mut Ui) { .background .jobs .iter() - .filter(|j| !j.label.is_empty()) + .filter(|j| !j.label.is_empty() && !j.cancelled()) .map(|j| j.label.as_str()) .chain(app.opening.pending.iter().map(|p| p.label.as_str())) .collect(); diff --git a/crates/ui-egui/src/export_progress.rs b/crates/ui-egui/src/export_progress.rs new file mode 100644 index 000000000..b14916347 --- /dev/null +++ b/crates/ui-egui/src/export_progress.rs @@ -0,0 +1,196 @@ +//! The export progress dialog: while an export runs in the background (Background Export) a modal +//! window shows what is being exported, how far it has got (artboard by artboard for raster +//! exports of several artboards; busy for one artboard and for formats made in one go, which +//! report no steps) and two buttons: Cancel (`file.cancelExport`, also +//! Esc) stops it and writes nothing, Continue in Background (`file.exportInBackground`) closes the +//! dialog and leaves the export to the status bar. +//! +//! It opens after [`DELAY`] so a quick export doesn't flash it, and waits while another dialog is +//! open (the overwrite question, an options dialog): never two modals at once. + +use serde_json::json; + +use crate::dialogs::modal; +use crate::theme::Tokens; +use crate::{VectorcraftApp, widgets}; + +/// Seconds an export runs before the dialog opens. +pub const DELAY: f64 = 0.4; + +/// Where the job a frame first saw is remembered (its id, the time it was first seen). +fn seen_id() -> egui::Id { + egui::Id::new("export-progress-seen") +} + +/// What the dialog shows: (label, steps done, steps in all, share done, exports after this one). +fn shown(app: &VectorcraftApp, now: f64, ctx: &egui::Context) -> Option<(String, u64, u64, f32, usize)> { + let jobs: Vec<_> = app.background.jobs.iter().filter(|j| !j.hidden && !j.cancelled()).filter(|j| j.progress.is_some()).collect(); + let first = jobs.first()?; + let since = ctx.data_mut(|m| { + let seen = m.get_temp_mut_or_insert_with(seen_id(), || (first.id(), now)); + if seen.0 != first.id() { + *seen = (first.id(), now); + } + seen.1 + }); + if now - since < DELAY { + return None; + } + let (done, total) = first.progress.as_ref().map(|p| p.counts()).unwrap_or_default(); + let frac = first.progress.as_ref().and_then(|p| p.fraction()).unwrap_or(0.0) as f32; + Some((first.label.clone(), done, total, frac, jobs.len() - 1)) +} + +pub fn show(app: &mut VectorcraftApp, ctx: &egui::Context) { + let now = ctx.input(|i| i.time); + let Some((label, done, total, frac, more)) = shown(app, now, ctx) else { return }; + if app.ui.dialog.is_some() { + return; + } + let t = Tokens::get(ctx); + let heading = format!("{}…", crate::i18n::msg(&label)); + let mut action = None; + modal::show(ctx, &heading, egui::Id::new("export-progress"), -40.0, 20, |ui| { + ui.set_width(360.0); + modal::heading(ui, &heading); + ui.add_space(12.0); + // One step says nothing a spinner doesn't: the bar would sit at 0 % until the file is written. + if total > 1 { + let step = crate::i18n::fmt(tl!("Artboard {n} of {total}"), &[("n", &(done + 1).min(total).to_string()), ("total", &total.to_string())]); + // The count on its own line: drawn over the bar it would straddle the fill's edge. + ui.horizontal(|ui| { + ui.label(egui::RichText::new(step).color(t.text_dim)); + ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { + ui.label(egui::RichText::new(format!("{:.0} %", frac * 100.0)).color(t.text_dim)); + }); + }); + ui.add_space(4.0); + ui.add(egui::ProgressBar::new(frac).desired_width(360.0).desired_height(6.0).fill(t.accent)); + } else { + ui.horizontal(|ui| { + ui.add(egui::Spinner::new().size(14.0).color(t.accent)); + ui.label(egui::RichText::new(tl!("Encoding and writing the file…")).color(t.text_dim)); + }); + } + if more > 0 { + ui.add_space(6.0); + let queued = crate::i18n::tn(more as u64, "{n} more export waiting", "{n} more exports waiting"); + ui.label(egui::RichText::new(queued).size(11.5).color(t.text_dim)); + } + ui.add_space(16.0); + let row = egui::vec2(ui.min_rect().width(), ui.spacing().interact_size.y); + ui.allocate_ui_with_layout(row, egui::Layout::right_to_left(egui::Align::Center), |ui| { + if widgets::secondary_button(ui, "Cancel").clicked() { + action = Some("file.cancelExport"); + } + ui.add_space(8.0); + if widgets::secondary_button(ui, "Continue in Background").clicked() { + action = Some("file.exportInBackground"); + } + }); + }); + if ctx.input(|i| i.key_pressed(egui::Key::Escape)) { + action = Some("file.cancelExport"); + } + if let Some(id) = action + && let Err(e) = app.run(id, json!({})) + { + app.status(e); + } +} + +#[cfg(test)] +mod tests { + use std::sync::Arc; + + use serde_json::json; + use vectorcraft_engine::Session; + use vectorcraft_engine::cmd::fileio::progress::Progress; + + use crate::{Services, VectorcraftApp, background}; + + /// An app whose background writer waits until `go` is set. + fn app(go: Arc) -> VectorcraftApp { + let services = Services { + write_shared: Some(Arc::new(move |_: &str, _: &[u8]| { + while !go.load(std::sync::atomic::Ordering::SeqCst) { + std::thread::sleep(std::time::Duration::from_millis(1)); + } + Ok(()) + })), + ..Default::default() + }; + VectorcraftApp::new(Session::new(), services) + } + + /// Draws one frame of the dialog at `time` seconds → the texts it drew. + fn frame(app: &mut VectorcraftApp, ctx: &egui::Context, time: f64) -> String { + let mut out = ctx.run_ui(egui::RawInput { time: Some(time), ..Default::default() }, |ui| super::show(app, ui.ctx())); + out.textures_delta.clear(); + let mut texts = String::new(); + for shape in out.shapes { + if let egui::epaint::Shape::Text(t) = shape.shape { + texts.push_str(t.galley.text()); + texts.push('\n'); + } + } + texts + } + + #[test] + fn opens_after_the_delay_and_cancel_closes_it() { + let go = Arc::new(std::sync::atomic::AtomicBool::new(false)); + let mut app = app(go.clone()); + let p = Progress::new(); + let r = background::run_with_progress( + &mut app, + true, + "Exporting a.png".into(), + None, + Some(p.clone()), + |w| w("/a.png", b"x").map(|()| json!({})), + |_, r| r, + ); + assert_eq!(r.unwrap()["background"], true); + let ctx = egui::Context::default(); + crate::theme::install_fonts(&ctx); + assert!(!frame(&mut app, &ctx, 0.0).contains("Exporting a.png"), "not before the delay"); + frame(&mut app, &ctx, 1.0); // a new window measures itself unseen first + let shown = frame(&mut app, &ctx, 1.1); + assert!(shown.contains("Exporting a.png") && shown.contains("Cancel") && shown.contains("Continue in Background"), "{shown}"); + app.run("file.cancelExport", json!({})).unwrap(); + assert!(p.cancelled()); + assert_eq!(app.ui.status, crate::io::EXPORT_CANCELLED); + assert!(!frame(&mut app, &ctx, 2.0).contains("Exporting a.png"), "a cancelled export isn't shown"); + go.store(true, std::sync::atomic::Ordering::SeqCst); + background::wait_all(&mut app); + } + + #[test] + fn continue_in_background_hides_it_and_the_export_goes_on() { + let go = Arc::new(std::sync::atomic::AtomicBool::new(false)); + let mut app = app(go.clone()); + let p = Progress::new(); + background::run_with_progress( + &mut app, + true, + "Exporting b.png".into(), + None, + Some(p.clone()), + |w| w("/b.png", b"x").map(|()| json!({})), + |_, r| r, + ) + .unwrap(); + let ctx = egui::Context::default(); + crate::theme::install_fonts(&ctx); + frame(&mut app, &ctx, 0.0); + frame(&mut app, &ctx, 1.0); + assert!(frame(&mut app, &ctx, 1.1).contains("Exporting b.png")); + assert_eq!(app.run("file.exportInBackground", json!({})).unwrap()["hidden"], 1); + assert!(!frame(&mut app, &ctx, 2.0).contains("Exporting b.png")); + assert!(!p.cancelled()); + go.store(true, std::sync::atomic::Ordering::SeqCst); + background::wait_all(&mut app); + assert!(app.background.jobs.is_empty()); + } +} diff --git a/crates/ui-egui/src/i18n/bg.tsv b/crates/ui-egui/src/i18n/bg.tsv index 06da118f2..9a2f46693 100644 --- a/crates/ui-egui/src/i18n/bg.tsv +++ b/crates/ui-egui/src/i18n/bg.tsv @@ -5153,6 +5153,12 @@ theme Appearance Външен вид Enable Guides for 9-Slice Scaling Включване на водачи за 9-slice мащабиране Persistent Постоянна Library Panel Options Опции на панела с библиотека + Artboard {n} of {total} Работна област {n} от {total} + Encoding and writing the file… Кодиране и записване на файла… +@plural {n} more export waiting|{n} more exports waiting Още {n} експортиране в изчакване|Още {n} експортирания в изчакване + Continue in Background Продължаване във фонов режим + Cancel Export Отказ от експортирането +@msg Export cancelled Експортирането е отказано # Paragraph panel menu › Justification… Desired Желано Word Spacing: Разстояние между думите: diff --git a/crates/ui-egui/src/i18n/cs.tsv b/crates/ui-egui/src/i18n/cs.tsv index 7e56142eb..302971f13 100644 --- a/crates/ui-egui/src/i18n/cs.tsv +++ b/crates/ui-egui/src/i18n/cs.tsv @@ -5188,6 +5188,12 @@ mark-style Japanese Japonské @msg too many library panels were opened in this session v této relaci bylo otevřeno příliš mnoho panelů knihoven @msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. Obraz má {_1} × {_2} pixelů, na jedné straně více než {LARGE_TRACE_SIDE}: vektorizuje se, ale pomaleji. Rychleji se vektorizuje s předvolbou nižší věrnosti nebo menší kopie obrazu. @msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). Obraz byl vektorizován v rozlišení {_1} × {_2} pixelů, zmenšený z {_3} × {_4} (nejvýše {_5} megapixelů). + Artboard {n} of {total} Pracovní plocha {n} z {total} + Encoding and writing the file… Kódování a zápis souboru… +@plural {n} more export waiting|{n} more exports waiting Čeká ještě {n} export|Čekají ještě {n} exporty|Čeká ještě {n} exportů + Continue in Background Pokračovat na pozadí + Cancel Export Zrušit export +@msg Export cancelled Export byl zrušen # Paragraph panel menu › Justification… Desired Požadované Word Spacing: Mezery mezi slovy: diff --git a/crates/ui-egui/src/i18n/da.tsv b/crates/ui-egui/src/i18n/da.tsv index 8927a16a0..8f91ea563 100644 --- a/crates/ui-egui/src/i18n/da.tsv +++ b/crates/ui-egui/src/i18n/da.tsv @@ -5172,6 +5172,12 @@ swatch Bright Violet Kraftig violet Typing Indtastning Persistent Vedvarende Library Panel Options Indstillinger for bibliotekspanel + Artboard {n} of {total} Tegneflade {n} af {total} + Encoding and writing the file… Koder og skriver filen… +@plural {n} more export waiting|{n} more exports waiting {n} eksport mere venter|{n} eksporter mere venter + Continue in Background Fortsæt i baggrunden + Cancel Export Annullér eksport +@msg Export cancelled Eksport annulleret # Paragraph panel menu › Justification… Desired Ønsket Word Spacing: Ordafstand: diff --git a/crates/ui-egui/src/i18n/de.tsv b/crates/ui-egui/src/i18n/de.tsv index 8a8eda6e0..a14348818 100644 --- a/crates/ui-egui/src/i18n/de.tsv +++ b/crates/ui-egui/src/i18n/de.tsv @@ -4763,6 +4763,12 @@ effect Regular Regelmäßig @msg too many library panels were opened in this session in dieser Sitzung wurden zu viele Bibliotheksbedienfelder geöffnet @msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. Das Bild hat {_1} × {_2} Pixel, mehr als {LARGE_TRACE_SIDE} auf einer Seite: Es wird nachgezeichnet, aber langsamer. Eine Vorgabe mit geringerer Detailtreue oder eine kleinere Kopie des Bildes geht schneller. @msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). Das Bild wurde mit {_1} × {_2} Pixeln nachgezeichnet, verkleinert von {_3} × {_4} (höchstens {_5} Megapixel). + Artboard {n} of {total} Zeichenfläche {n} von {total} + Encoding and writing the file… Datei wird codiert und geschrieben… +@plural {n} more export waiting|{n} more exports waiting {n} weiterer Export wartet|{n} weitere Exporte warten + Continue in Background Im Hintergrund fortsetzen + Cancel Export Export abbrechen +@msg Export cancelled Export abgebrochen # Paragraph panel menu › Justification… Desired Optimal Word Spacing: Wortabstand: diff --git a/crates/ui-egui/src/i18n/el.tsv b/crates/ui-egui/src/i18n/el.tsv index 9a83f14f9..1640142cc 100644 --- a/crates/ui-egui/src/i18n/el.tsv +++ b/crates/ui-egui/src/i18n/el.tsv @@ -5140,6 +5140,12 @@ effect Points Σημεία @msg too many library panels were opened in this session άνοιξαν πάρα πολλοί πίνακες βιβλιοθηκών σε αυτή τη συνεδρία @msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. Η εικόνα είναι {_1} × {_2} εικονοστοιχεία, πάνω από {LARGE_TRACE_SIDE} σε μία πλευρά: ανιχνεύεται, αλλά πιο αργά. Μια προρύθμιση χαμηλότερης πιστότητας ή ένα μικρότερο αντίγραφο της εικόνας ανιχνεύεται γρηγορότερα. @msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). Η εικόνα ανιχνεύτηκε σε {_1} × {_2} εικονοστοιχεία, σμικρυμένη από {_3} × {_4} (το πολύ {_5} megapixel). + Artboard {n} of {total} Πίνακας σχεδίασης {n} από {total} + Encoding and writing the file… Κωδικοποίηση και εγγραφή του αρχείου… +@plural {n} more export waiting|{n} more exports waiting {n} ακόμη εξαγωγή σε αναμονή|{n} ακόμη εξαγωγές σε αναμονή + Continue in Background Συνέχεια στο παρασκήνιο + Cancel Export Ακύρωση εξαγωγής +@msg Export cancelled Η εξαγωγή ακυρώθηκε # Paragraph panel menu › Justification… Desired Επιθυμητό Word Spacing: Απόσταση λέξεων: diff --git a/crates/ui-egui/src/i18n/es.tsv b/crates/ui-egui/src/i18n/es.tsv index 998364ac7..e86ca03b7 100644 --- a/crates/ui-egui/src/i18n/es.tsv +++ b/crates/ui-egui/src/i18n/es.tsv @@ -5130,6 +5130,12 @@ mark-style Japanese Japonesas @msg too many library panels were opened in this session se abrieron demasiados paneles de biblioteca en esta sesión @msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. La imagen tiene {_1} × {_2} píxeles, más de {LARGE_TRACE_SIDE} en un lado: se calcará, pero más despacio. Un ajuste preestablecido de menor fidelidad o una copia más pequeña de la imagen se calcan más rápido. @msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). La imagen se calcó a {_1} × {_2} píxeles, reducida desde {_3} × {_4} ({_5} megapíxeles como máximo). + Artboard {n} of {total} Mesa de trabajo {n} de {total} + Encoding and writing the file… Codificando y escribiendo el archivo… +@plural {n} more export waiting|{n} more exports waiting {n} exportación más en espera|{n} exportaciones más en espera + Continue in Background Continuar en segundo plano + Cancel Export Cancelar exportación +@msg Export cancelled Exportación cancelada # Paragraph panel menu › Justification… Desired Deseado Word Spacing: Espaciado entre palabras: diff --git a/crates/ui-egui/src/i18n/fi.tsv b/crates/ui-egui/src/i18n/fi.tsv index 291267ae4..28049fbe4 100644 --- a/crates/ui-egui/src/i18n/fi.tsv +++ b/crates/ui-egui/src/i18n/fi.tsv @@ -5152,6 +5152,12 @@ mark-style Japanese Japanilainen @msg too many library panels were opened in this session tässä istunnossa on avattu liian monta kirjastopaneelia @msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. Kuva on {_1} × {_2} kuvapistettä, yhdeltä sivultaan yli {LARGE_TRACE_SIDE}: se jäljitetään, mutta hitaammin. Pienemmän tarkkuuden esiasetus tai kuvan pienempi kopio jäljittyy nopeammin. @msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). Kuva jäljitettiin kokoon {_1} × {_2} kuvapistettä pienennettynä koosta {_3} × {_4} (enintään {_5} megapikseliä). + Artboard {n} of {total} Piirtoalusta {n}/{total} + Encoding and writing the file… Tiedostoa koodataan ja kirjoitetaan… +@plural {n} more export waiting|{n} more exports waiting {n} vienti odottaa|{n} vientiä odottaa + Continue in Background Jatka taustalla + Cancel Export Peruuta vienti +@msg Export cancelled Vienti peruutettiin # Paragraph panel menu › Justification… Desired Haluttu Word Spacing: Sanaväli: diff --git a/crates/ui-egui/src/i18n/fr.tsv b/crates/ui-egui/src/i18n/fr.tsv index 71eab0de7..4d5218e09 100644 --- a/crates/ui-egui/src/i18n/fr.tsv +++ b/crates/ui-egui/src/i18n/fr.tsv @@ -5187,6 +5187,12 @@ theme Appearance Apparence @msg too many library panels were opened in this session trop de panneaux de bibliothèque ont été ouverts dans cette session @msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. L’image fait {_1} × {_2} pixels, plus de {LARGE_TRACE_SIDE} sur un côté : elle est vectorisée, mais plus lentement. Un paramètre prédéfini moins fidèle ou une copie plus petite de l’image se vectorise plus vite. @msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). L’image a été vectorisée à {_1} × {_2} pixels, réduite de {_3} × {_4} ({_5} mégapixels au plus). + Artboard {n} of {total} Plan de travail {n} sur {total} + Encoding and writing the file… Encodage et écriture du fichier… +@plural {n} more export waiting|{n} more exports waiting {n} autre exportation en attente|{n} autres exportations en attente + Continue in Background Continuer en arrière-plan + Cancel Export Annuler l’exportation +@msg Export cancelled Exportation annulée # Paragraph panel menu › Justification… Desired Optimal Word Spacing: Espacement des mots : diff --git a/crates/ui-egui/src/i18n/hu.tsv b/crates/ui-egui/src/i18n/hu.tsv index d3cd55962..d84b7ef26 100644 --- a/crates/ui-egui/src/i18n/hu.tsv +++ b/crates/ui-egui/src/i18n/hu.tsv @@ -5174,6 +5174,12 @@ imageTrace Outline Körvonal @msg too many library panels were opened in this session ebben a munkamenetben túl sok könyvtárpanel nyílt meg @msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. A kép {_1} × {_2} képpont, egyik oldalán több mint {LARGE_TRACE_SIDE}: a vektorizálás megtörténik, de lassabban. Kisebb hűségű előbeállítással vagy a kép kisebb másolatával gyorsabb. @msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). A kép vektorizálása {_1} × {_2} képponton történt, {_3} × {_4} méretről kicsinyítve (legfeljebb {_5} megapixel). + Artboard {n} of {total} Rajztábla: {n} / {total} + Encoding and writing the file… A fájl kódolása és írása… +@plural {n} more export waiting|{n} more exports waiting Még {n} exportálás várakozik + Continue in Background Folytatás a háttérben + Cancel Export Exportálás megszakítása +@msg Export cancelled Az exportálás megszakítva # Paragraph panel menu › Justification… Desired Kívánt Word Spacing: Szóköz: diff --git a/crates/ui-egui/src/i18n/id.tsv b/crates/ui-egui/src/i18n/id.tsv index c5a1c581d..3336f0745 100644 --- a/crates/ui-egui/src/i18n/id.tsv +++ b/crates/ui-egui/src/i18n/id.tsv @@ -5172,6 +5172,12 @@ swatch Bright Violet Violet cerah Typing Mengetik Persistent Persisten Library Panel Options Opsi panel pustaka + Artboard {n} of {total} Artboard {n} dari {total} + Encoding and writing the file… Mengodekan dan menulis berkas… +@plural {n} more export waiting|{n} more exports waiting {n} ekspor lagi menunggu + Continue in Background Lanjutkan di latar belakang + Cancel Export Batalkan ekspor +@msg Export cancelled Ekspor dibatalkan # Paragraph panel menu › Justification… Desired Diinginkan Word Spacing: Spasi Kata: diff --git a/crates/ui-egui/src/i18n/it.tsv b/crates/ui-egui/src/i18n/it.tsv index 49290e8fb..28eb097af 100644 --- a/crates/ui-egui/src/i18n/it.tsv +++ b/crates/ui-egui/src/i18n/it.tsv @@ -4801,6 +4801,12 @@ lighting Light Luce @msg too many library panels were opened in this session in questa sessione sono stati aperti troppi pannelli libreria @msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. L’immagine è di {_1} × {_2} pixel, più di {LARGE_TRACE_SIDE} su un lato: viene vettorializzata, ma più lentamente. Un predefinito a fedeltà più bassa o una copia più piccola dell’immagine è più veloce. @msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). L’immagine è stata vettorializzata a {_1} × {_2} pixel, ridotta da {_3} × {_4} (al massimo {_5} megapixel). + Artboard {n} of {total} Tavola da disegno {n} di {total} + Encoding and writing the file… Codifica e scrittura del file… +@plural {n} more export waiting|{n} more exports waiting {n} altra esportazione in attesa|{n} altre esportazioni in attesa + Continue in Background Continua in background + Cancel Export Annulla esportazione +@msg Export cancelled Esportazione annullata # Paragraph panel menu › Justification… Desired Desiderato Word Spacing: Spaziatura parole: diff --git a/crates/ui-egui/src/i18n/ja.tsv b/crates/ui-egui/src/i18n/ja.tsv index b95859e12..68dc8d797 100644 --- a/crates/ui-egui/src/i18n/ja.tsv +++ b/crates/ui-egui/src/i18n/ja.tsv @@ -4841,6 +4841,12 @@ lighting Light ライト @msg too many library panels were opened in this session このセッションで開いたライブラリパネルが多すぎます @msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. 画像は {_1} × {_2} ピクセルで、一辺が {LARGE_TRACE_SIDE} を超えています。トレースはできますが、時間がかかります。忠実度の低いプリセットや画像の小さいコピーを使うと速くなります。 @msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). 画像は {_3} × {_4} から縮小した {_1} × {_2} ピクセルでトレースされました (最大 {_5} メガピクセル)。 + Artboard {n} of {total} アートボード {n} / {total} + Encoding and writing the file… ファイルをエンコードして書き込み中… +@plural {n} more export waiting|{n} more exports waiting 待機中の書き出し: あと {n} 件 + Continue in Background バックグラウンドで続行 + Cancel Export 書き出しをキャンセル +@msg Export cancelled 書き出しをキャンセルしました # Paragraph panel menu › Justification… Desired 最適 Word Spacing: 単語間隔: diff --git a/crates/ui-egui/src/i18n/ko.tsv b/crates/ui-egui/src/i18n/ko.tsv index 611acbd23..fd75fe6e7 100644 --- a/crates/ui-egui/src/i18n/ko.tsv +++ b/crates/ui-egui/src/i18n/ko.tsv @@ -4764,6 +4764,12 @@ lighting Light 광원 @msg too many library panels were opened in this session 이 세션에서 라이브러리 패널을 너무 많이 열었습니다 @msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. 이미지가 {_1} × {_2}픽셀로 한 변이 {LARGE_TRACE_SIDE}을 넘습니다. 추적은 되지만 더 느립니다. 충실도가 낮은 사전 설정이나 이미지의 작은 사본을 사용하면 더 빠릅니다. @msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). 이미지를 {_3} × {_4}에서 축소한 {_1} × {_2}픽셀로 추적했습니다(최대 {_5}메가픽셀). + Artboard {n} of {total} 아트보드 {n} / {total} + Encoding and writing the file… 파일을 인코딩하고 쓰는 중… +@plural {n} more export waiting|{n} more exports waiting 대기 중인 내보내기 {n}개 더 + Continue in Background 백그라운드에서 계속 + Cancel Export 내보내기 취소 +@msg Export cancelled 내보내기를 취소했습니다 # Paragraph panel menu › Justification… Desired 희망 Word Spacing: 단어 간격: diff --git a/crates/ui-egui/src/i18n/nl.tsv b/crates/ui-egui/src/i18n/nl.tsv index 904a159b8..3a196972b 100644 --- a/crates/ui-egui/src/i18n/nl.tsv +++ b/crates/ui-egui/src/i18n/nl.tsv @@ -5215,7 +5215,12 @@ swatch Bright Violet Felviolet @msg too many library panels were opened in this session er zijn in deze sessie te veel bibliotheekpanelen geopend @msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. De afbeelding is {_1} × {_2} pixels, meer dan {LARGE_TRACE_SIDE} aan één zijde: ze wordt getraceerd, maar langzamer. Een voorinstelling met minder detail of een kleinere kopie van de afbeelding traceert sneller. @msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). De afbeelding is getraceerd op {_1} × {_2} pixels, verkleind van {_3} × {_4} (maximaal {_5} megapixels). - + Artboard {n} of {total} Tekengebied {n} van {total} + Encoding and writing the file… Bestand coderen en schrijven… +@plural {n} more export waiting|{n} more exports waiting Nog {n} export in de wachtrij|Nog {n} exports in de wachtrij + Continue in Background Doorgaan op achtergrond + Cancel Export Export annuleren +@msg Export cancelled Export geannuleerd # Paragraph panel menu › Justification… Desired Gewenst Word Spacing: Woordafstand: diff --git a/crates/ui-egui/src/i18n/pl.tsv b/crates/ui-egui/src/i18n/pl.tsv index ed359c6be..1c5c6b5df 100644 --- a/crates/ui-egui/src/i18n/pl.tsv +++ b/crates/ui-egui/src/i18n/pl.tsv @@ -5040,6 +5040,12 @@ mark-style Japanese Japońskie @msg too many library panels were opened in this session w tej sesji otwarto zbyt wiele paneli bibliotek @msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. Obraz ma {_1} × {_2} pikseli, ponad {LARGE_TRACE_SIDE} na jednym boku: zostanie obrysowany, ale wolniej. Ustawienie wstępne o niższej wierności lub mniejsza kopia obrazu obrysuje się szybciej. @msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). Obraz obrysowano w rozdzielczości {_1} × {_2} pikseli, po zmniejszeniu z {_3} × {_4} (najwyżej {_5} megapikseli). + Artboard {n} of {total} Obszar kompozycji {n} z {total} + Encoding and writing the file… Kodowanie i zapisywanie pliku… +@plural {n} more export waiting|{n} more exports waiting Czeka jeszcze {n} eksport|Czekają jeszcze {n} eksporty|Czeka jeszcze {n} eksportów + Continue in Background Kontynuuj w tle + Cancel Export Anuluj eksport +@msg Export cancelled Eksport anulowany # Paragraph panel menu › Justification… Desired Optymalne Word Spacing: Odstępy między wyrazami: diff --git a/crates/ui-egui/src/i18n/pt-br.tsv b/crates/ui-egui/src/i18n/pt-br.tsv index 9c953e785..bbcb19d41 100644 --- a/crates/ui-egui/src/i18n/pt-br.tsv +++ b/crates/ui-egui/src/i18n/pt-br.tsv @@ -5105,6 +5105,12 @@ mark-style Japanese Japonesas @msg too many library panels were opened in this session foram abertos painéis de biblioteca demais nesta sessão @msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. A imagem tem {_1} × {_2} pixels, mais de {LARGE_TRACE_SIDE} em um lado: ela é traçada, mas mais devagar. Uma predefinição de menor fidelidade ou uma cópia menor da imagem traça mais rápido. @msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). A imagem foi traçada em {_1} × {_2} pixels, reduzida de {_3} × {_4} (no máximo {_5} megapixels). + Artboard {n} of {total} Mesa de Trabalho {n} de {total} + Encoding and writing the file… Codificando e gravando o arquivo… +@plural {n} more export waiting|{n} more exports waiting Mais {n} exportação aguardando|Mais {n} exportações aguardando + Continue in Background Continuar em Segundo Plano + Cancel Export Cancelar Exportação +@msg Export cancelled Exportação cancelada # Paragraph panel menu › Justification… Desired Desejado Word Spacing: Espaçamento entre Palavras: diff --git a/crates/ui-egui/src/i18n/ru.tsv b/crates/ui-egui/src/i18n/ru.tsv index a6e4199d2..9ba4ae4f4 100644 --- a/crates/ui-egui/src/i18n/ru.tsv +++ b/crates/ui-egui/src/i18n/ru.tsv @@ -4797,6 +4797,12 @@ lighting Light Свет @msg too many library panels were opened in this session в этом сеансе открыто слишком много панелей библиотек @msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. Изображение {_1} × {_2} пикселей, больше {LARGE_TRACE_SIDE} по одной стороне: трассировка выполняется, но медленнее. Стиль с меньшей точностью или уменьшенная копия изображения трассируются быстрее. @msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). Изображение трассировано в размере {_1} × {_2} пикселей, уменьшенное с {_3} × {_4} (не более {_5} мегапикселей). + Artboard {n} of {total} Монтажная область {n} из {total} + Encoding and writing the file… Кодирование и запись файла… +@plural {n} more export waiting|{n} more exports waiting Ещё {n} экспорт в очереди|Ещё {n} экспорта в очереди|Ещё {n} экспортов в очереди + Continue in Background Продолжить в фоне + Cancel Export Отменить экспорт +@msg Export cancelled Экспорт отменён # Paragraph panel menu › Justification… Desired Желательно Word Spacing: Интервал между словами: diff --git a/crates/ui-egui/src/i18n/sv.tsv b/crates/ui-egui/src/i18n/sv.tsv index 5f0522664..9f38a55bb 100644 --- a/crates/ui-egui/src/i18n/sv.tsv +++ b/crates/ui-egui/src/i18n/sv.tsv @@ -5144,6 +5144,12 @@ theme Appearance Utseende @msg too many library panels were opened in this session för många bibliotekspaneler har öppnats i den här sessionen @msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. Bilden är {_1} × {_2} pixlar, mer än {LARGE_TRACE_SIDE} på en sida: den vektoriseras, men långsammare. En förinställning med lägre återgivning eller en mindre kopia av bilden går snabbare. @msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). Bilden vektoriserades vid {_1} × {_2} pixlar, nedskalad från {_3} × {_4} (högst {_5} megapixlar). + Artboard {n} of {total} Rityta {n} av {total} + Encoding and writing the file… Kodar och skriver filen… +@plural {n} more export waiting|{n} more exports waiting {n} export till väntar|{n} exporter till väntar + Continue in Background Fortsätt i bakgrunden + Cancel Export Avbryt export +@msg Export cancelled Exporten avbröts # Paragraph panel menu › Justification… Desired Önskat Word Spacing: Ordavstånd: diff --git a/crates/ui-egui/src/i18n/uk.tsv b/crates/ui-egui/src/i18n/uk.tsv index 3d516a3ba..52aa84fcd 100644 --- a/crates/ui-egui/src/i18n/uk.tsv +++ b/crates/ui-egui/src/i18n/uk.tsv @@ -4781,6 +4781,12 @@ lighting Light Світло @msg too many library panels were opened in this session у цьому сеансі відкрито забагато панелей бібліотек @msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. Зображення {_1} × {_2} пікселів, понад {LARGE_TRACE_SIDE} з одного боку: трасування виконується, але повільніше. Стиль із нижчою точністю або менша копія зображення трасуються швидше. @msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). Зображення трасовано в розмірі {_1} × {_2} пікселів, зменшене з {_3} × {_4} (щонайбільше {_5} мегапікселів). + Artboard {n} of {total} Монтажна область {n} з {total} + Encoding and writing the file… Кодування та запис файлу… +@plural {n} more export waiting|{n} more exports waiting Ще {n} експорт в очікуванні|Ще {n} експорти в очікуванні|Ще {n} експортів в очікуванні + Continue in Background Продовжити у фоні + Cancel Export Скасувати експорт +@msg Export cancelled Експорт скасовано # Paragraph panel menu › Justification… Desired Бажане Word Spacing: Інтервал між словами: diff --git a/crates/ui-egui/src/i18n/vi.tsv b/crates/ui-egui/src/i18n/vi.tsv index f95d85c29..c159290e7 100644 --- a/crates/ui-egui/src/i18n/vi.tsv +++ b/crates/ui-egui/src/i18n/vi.tsv @@ -4781,6 +4781,12 @@ theme Appearance Giao diện @msg too many library panels were opened in this session đã mở quá nhiều bảng thư viện trong phiên này @msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. Ảnh có kích thước {_1} × {_2} điểm ảnh, hơn {LARGE_TRACE_SIDE} ở một cạnh: ảnh vẫn được dò, nhưng chậm hơn. Một thiết lập sẵn có độ trung thực thấp hơn hoặc một bản sao nhỏ hơn của ảnh sẽ dò nhanh hơn. @msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). Ảnh đã được dò ở {_1} × {_2} điểm ảnh, thu nhỏ từ {_3} × {_4} (tối đa {_5} megapixel). + Artboard {n} of {total} Bảng vẽ {n} / {total} + Encoding and writing the file… Đang mã hóa và ghi tệp… +@plural {n} more export waiting|{n} more exports waiting Còn {n} lượt xuất đang chờ + Continue in Background Tiếp tục trong nền + Cancel Export Hủy xuất +@msg Export cancelled Đã hủy xuất # Paragraph panel menu › Justification… Desired Mong muốn Word Spacing: Khoảng cách từ: diff --git a/crates/ui-egui/src/i18n/zh-hans.tsv b/crates/ui-egui/src/i18n/zh-hans.tsv index fad18c6f0..96b6557ff 100644 --- a/crates/ui-egui/src/i18n/zh-hans.tsv +++ b/crates/ui-egui/src/i18n/zh-hans.tsv @@ -5023,7 +5023,12 @@ lighting Light 光源 @msg only http, https and mailto URLs open in the browser, not “{url}” 浏览器中只会打开 http、https 和 mailto 的 URL,不会打开 “{url}” @msg {id} opens another app: only the person at the keyboard can run it {id} 会打开其他应用程序:只有使用键盘的人才能运行它 @msg “{name}” is not a file “{name}” 不是文件 - + Artboard {n} of {total} 画板 {n} / {total} + Encoding and writing the file… 正在编码并写入文件… +@plural {n} more export waiting|{n} more exports waiting 另有 {n} 个导出等待中 + Continue in Background 在后台继续 + Cancel Export 取消导出 +@msg Export cancelled 已取消导出 # Paragraph panel menu › Justification… Desired 所需 Word Spacing: 字间距: diff --git a/crates/ui-egui/src/i18n/zh-hant.tsv b/crates/ui-egui/src/i18n/zh-hant.tsv index 5c9133ece..d825da0a2 100644 --- a/crates/ui-egui/src/i18n/zh-hant.tsv +++ b/crates/ui-egui/src/i18n/zh-hant.tsv @@ -4077,6 +4077,12 @@ lighting Light 光源 Enable Guides for 9-Slice Scaling 啟用 9-slice 縮放參考線 Persistent 持久 Library Panel Options 資料庫面板選項 + Artboard {n} of {total} 工作區域 {n} / {total} + Encoding and writing the file… 正在編碼並寫入檔案… +@plural {n} more export waiting|{n} more exports waiting 另有 {n} 個匯出等待中 + Continue in Background 在背景繼續 + Cancel Export 取消匯出 +@msg Export cancelled 已取消匯出 # Paragraph panel menu › Justification… Desired 所需 Word Spacing: 字間距: diff --git a/crates/ui-egui/src/io.rs b/crates/ui-egui/src/io.rs index b41ffd0c4..2c700a919 100644 --- a/crates/ui-egui/src/io.rs +++ b/crates/ui-egui/src/io.rs @@ -3,7 +3,9 @@ use serde_json::{Value, json}; use vectorcraft_doc::SavedView; +use vectorcraft_doc::cancel; use vectorcraft_engine::EngineError; +use vectorcraft_engine::cmd::fileio::progress::{self, Progress}; use vectorcraft_engine::cmd::fileio::{self, Format, SAVE_FORMATS, SaveMode, SavePlan, TEMPLATE_FORMATS}; use crate::background::{self, Writer}; @@ -675,6 +677,25 @@ pub fn recent_files(app: &VectorcraftApp) -> &[String] { &r[..r.len().min(app.session.prefs.recent_files_count as usize)] } +/// The status once an export was cancelled. +pub const EXPORT_CANCELLED: &str = "Export cancelled"; + +/// File → Cancel Export (the progress dialog's Cancel): every export running in the background +/// stops and writes nothing more → `{cancelled: n}`. +pub fn cancel_exports(app: &mut VectorcraftApp) -> Value { + let n = app.background.cancel_exports(); + if n > 0 { + app.status(EXPORT_CANCELLED); + } + json!({ "cancelled": n }) +} + +/// The progress dialog's Continue in Background: the exports go on, shown in the status bar only +/// → `{hidden: n}`. +pub fn hide_exports(app: &mut VectorcraftApp) -> Value { + json!({ "hidden": app.background.hide_exports() }) +} + /// Export the active document in `format` (default: the path's extension, else PNG) with the /// `document.export` options in `params` (artboard, range, useArtboards, selectedOnly, ppi, SVG /// options…): every file it writes (one per artboard, linked images) goes next to `path`. The @@ -704,18 +725,31 @@ pub fn export(app: &mut VectorcraftApp, format: Option<&str>, path: Option 1 { - out["files"] = json!(files); - } - Ok(out) + progress::scope(&reporting, || { + cancel::check().map_err(|e| e.to_string())?; + let enc = fileio::encode_all(&doc, f.id, ¶ms).map_err(|e| e.to_string())?; + cancel::check().map_err(|e| e.to_string())?; + let files = write_encoded(write, &doc, &path, &enc)?; + let mut out = json!({ "path": files.first().unwrap_or(&path), "warnings": enc.warnings }); + if files.len() > 1 { + out["files"] = json!(files); + } + Ok(out) + }) }; let enabled = app.session.prefs.background_export; let failed = target.clone(); - let mut r = background::run(app, enabled, label, None, work, move |app, r| { + let mut r = background::run_with_progress(app, enabled, label, None, Some(progress), work, move |app, r| { + // A file written before the Cancel arrived is reported as written. + if r.is_err() && asked.cancelled() { + app.status(EXPORT_CANCELLED); + return Ok(json!({ "cancelled": true })); + } let r = r.inspect_err(|e| report_write_error(app, true, &failed, e))?; let what = match r["files"].as_array() { Some(files) => format!("{} files", files.len()), diff --git a/crates/ui-egui/src/lib.rs b/crates/ui-egui/src/lib.rs index 36b8a6445..834174217 100644 --- a/crates/ui-egui/src/lib.rs +++ b/crates/ui-egui/src/lib.rs @@ -25,6 +25,7 @@ pub mod cursors; pub mod dialogs; pub mod dock; mod edit_toolbar; +pub mod export_progress; pub mod find_font; pub mod floating; pub mod font_menu; @@ -1440,6 +1441,7 @@ impl VectorcraftApp { dock::floating_panel(self, &ctx); floating::show(self, &ctx); dialogs::show(self, &ctx); + export_progress::show(self, &ctx); palette::show(self, &ctx); if self.custom_titlebar { titlebar::resize_zones(ui); diff --git a/crates/ui-egui/src/menus.rs b/crates/ui-egui/src/menus.rs index d9b5e6925..3db77498c 100644 --- a/crates/ui-egui/src/menus.rs +++ b/crates/ui-egui/src/menus.rs @@ -747,6 +747,18 @@ pub const UI_COMMANDS: &[(&str, &str, &str, &str)] = &[ ("file.openRecent29", "Open Recent File 29", "", "{}"), ("file.openRecent30", "Open Recent File 30", "", "{}"), ("file.reveal", "Show in Folder", "", "{} show the document's file in the system file manager (desktop) → {path}"), + ( + "file.cancelExport", + "Cancel Export", + "", + "{} stop every export running in the background (Background Export): an export stops between artboards and writes no file once cancelled → {cancelled: n}", + ), + ( + "file.exportInBackground", + "Continue in Background", + "", + "{} close the export progress dialog; the exports go on, shown in the status bar → {hidden: n}", + ), ( "ui.dxfOptionsDialog", "DXF Options Dialog", @@ -1101,6 +1113,8 @@ pub fn run_ui_command(app: &mut VectorcraftApp, id: &str, p: &Value) -> Option io::new_from_template(app, s("path")), "file.revert" if p.get("confirmed").and_then(Value::as_bool) != Some(true) => io::ask_revert(app), "file.reveal" => io::reveal(app), + "file.cancelExport" => Ok(io::cancel_exports(app)), + "file.exportInBackground" => Ok(io::hide_exports(app)), id if id.starts_with("file.openRecent") => match recent_slot(app, id).cloned() { Some(path) => io::open_reporting(app, &path), None => Err("no such recent file".into()), @@ -2225,6 +2239,7 @@ pub fn enabled(app: &VectorcraftApp, id: &str) -> bool { "type.bold" | "type.italic" => crate::panels::character::text_style(app).is_some(), id if crate::panels::paragraph::ALIGN_COMMANDS.iter().any(|c| c.0 == id) => crate::panels::character::text_style(app).is_some(), "file.reveal" => app.services.reveal.is_some() && app.session.active().is_some_and(|d| d.path.is_some()), + "file.cancelExport" | "file.exportInBackground" => app.background.jobs.iter().any(|j| j.progress.is_some() && !j.cancelled()), "file.place" | "file.export.svg" | "file.export.png" diff --git a/crates/ui-egui/src/tests_background.rs b/crates/ui-egui/src/tests_background.rs index 757b6830c..b793e695f 100644 --- a/crates/ui-egui/src/tests_background.rs +++ b/crates/ui-egui/src/tests_background.rs @@ -121,6 +121,34 @@ fn exports_run_in_the_background_and_the_preference_turns_both_off() { assert!(app.background.jobs.is_empty()); } +#[test] +fn a_cancelled_export_writes_nothing() { + let (gate, writing) = (Gate::default(), Gate::default()); + let files = Files::default(); + let (f, g, w) = (files.clone(), gate.clone(), writing.clone()); + let services = Services { + write_shared: Some(Arc::new(move |p: &str, b: &[u8]| { + w.open(); + g.wait(); + f.lock().unwrap().push((p.to_string(), b.to_vec())); + Ok(()) + })), + ..Default::default() + }; + let mut app = VectorcraftApp::new(Session::new(), services); + app.run("file.new", json!({"width": 100, "height": 100})).unwrap(); + // The first export holds the worker at the gate, past its last check: it still writes. + io::export(&mut app, Some("svg"), Some("/out/first.svg".into()), &json!({})).unwrap(); + writing.wait(); + io::export(&mut app, Some("png"), Some("/out/second.png".into()), &json!({})).unwrap(); + assert_eq!(app.run("file.cancelExport", json!({})).unwrap()["cancelled"], 2); + assert_eq!(app.run("file.cancelExport", json!({})).unwrap()["cancelled"], 0, "nothing left to cancel"); + gate.open(); + background::wait_all(&mut app); + assert_eq!(written(&files), ["/out/first.svg"]); + assert_eq!(app.ui.status, io::EXPORT_CANCELLED); +} + #[test] fn quitting_waits_for_background_saves() { let gate = Gate::default(); diff --git a/docs/gaps.md b/docs/gaps.md index 32be65762..20d9eaebb 100644 --- a/docs/gaps.md +++ b/docs/gaps.md @@ -1,6 +1,6 @@ # Gaps -> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-17 · **Change:** minor (3D lighting: one to ten colored lights, smooth colored Inflate, legacy white-light appearance preserved; surface batching and restored seam minimum; scores and estimates unchanged; Blend spine Anchor Point conversions, selection/stacking priority and Shift regression coverage (#2544); scores and hours unchanged; transparency grid visibility saved in native documents, #1968; G6: MCP cancellation, progress notifications and busy answers during long exports, #2165, #2171, #2172; EPS editing data: legacy mesh corners, vector opacity masks and compound member paint retained; raster page comparison filters minification without relaxing limits; scores and hours unchanged; correction: quick Image Trace presets leave the panel as it was, the Image Trace button opens it; batch exporting, #1935; G2: Effect › Sketch › Halftone Pattern; Photoshop filters 30/57, 27 remaining (22–38 h); G5: Symbol Options Export Type (Graphic / Movie Clip); G1: Smart Guides line extension, #2120; G16: Brazilian Portuguese catalog complete under the tests, #1914; Localization 71% → 74%, hours 55–90 → 50–85; G6: image variables landed, estimate 8–14 → 6–12 h; Warp Options controls verified by focused checks and original-image review; estimates unchanged; existing-guide Escape cancellation validated 2026-10-14; scores and hours unchanged; open-path anchor Delete keeps surviving runs separate; estimates unchanged; Artboard tab-switch follow-up historically validated on `a0671777`; scores and hours unchanged; Artboard clean active-tab close/Delete follow-up validated 2026-10-15; G13: Object › Intertwine landed, #1763; no entry closed, estimates unchanged; MCP `add_text` exposes the existing `vertical` option for point, area and path type; horizontal remains the default; scores and hours unchanged; Vertical fullwidth question and exclamation marks keep their nominal glyph upright when the font has no vertical alternate; scores and hours unchanged.; G1: Curvature curve geometry, #1236 in part; Width-point finite-arithmetic follow-up validated; scores and hours unchanged; Image Trace adjustment discovery; G9: legacy `.ai` of version 7 saved with type as text and hidden objects kept, legacy type read, #868; estimate unchanged; Revolve: automatic smooth shading and default-on Cap with continuous rims; later 2026-10-14: G8 untrusted files, #1852, #1862–#1873; G11 anchor X/Y/W/H, #1824, Snap to Grid over Snap to Pixel, #1816, redo during a drag, #1719, Dim Images, #1683, failed-save dialog, #1685; gaps noted for reporters: G1 Warp Options for Effect › Warp, #1760, Color Picker Tab order, #1692, key anchor, #1636; G7 custom toolbars, #1759, workspace panel widths, #1753; G11 Shape Builder gaps between closed shapes, #1691, interior anchor delete; G13 Intertwine, #1763, gradient mesh from an image, #1676, Image Trace radial gradients, #1849; G12 duplicated line removed; no entry closed, estimates unchanged; verified PngSuite downloads, #1729; synthetic control-input budgets, #1709; 2026-10-14 fixes: G11 anchor Align and Distribute, #1636, Delete and selection, #1674, #1666, #1743, #1686, Roughen and Scribble, #1643, #1648; G8 file and input hardening, #1776–#1783, #1786, #1807; G10 Wayland cursors and the Windows title bar, unconfirmed, #1645, #1758; no entry closed, estimates unchanged; later 2026-10-13 fixes, #1570, #1578, #1590, #1613, #1615, #1616, #1620 in part, #1602 in part; no entry closed, estimates unchanged; TIFF is available in Export for Screens and Asset Export with shared format settings; scores and hours unchanged; G1/M11: Artboard deletion and history selection normalization; history regressions and full workspace CI pass; scores and hours unchanged; Character styles retain explicit Auto leading, propagate resets and preserve local leading overrides; scores and hours unchanged; Width Point Edit: Shift-double-click enables adjoining adjustment; plain double-click keeps it off; scores and hours unchanged; G12: Identify Objects By acts, #394; ~16 → ~15 prefs still inert; G12: Appearance of Black › Printing / Exporting acts, #394; ~16 → ~15 prefs still inert; control transport deadlines, #1726; G1: Average and Join in one step with a corner or smooth choice added as missing (it was listed as done in ui-parity.md); Join after Average, #1533; guide objects, #1534; Keyboard Shortcuts, #1539; G9 and G11 fixes, #1528, #1530, #1544, #1553, #1512, #1537; estimates unchanged; Direct Selection anchor precedence, #1508; dialog slider rails, #1483; template layers in Outline view, #1486; Tabs panel stop drags, #1458; type that does not fit its frame is reported, #1471; 3D on outlined type, #1481, Twist range, #1524, twisted walls, #1525; print-size images, #1517; French catalog, #1511; estimates unchanged; G1: Split Into Grid options, #1462, Transform Objects and Reflect's angle, #1448 and #1460, Pathfinder on a group, #1450, value fields, #1457 and #1465 in part; G9: grayscale TIFFs with alpha, #1466 in part; G6: unknown parameter keys refused, #1442; G10: window surfaces past 8192 px, #1459 in part; G15: many-object edits, #1429 in part; estimates unchanged; G16: Vietnamese catalog added, complete under the tests; three key languages remain, 60–100 → 55–90 h; G3: Extrude & Bevel, Inflate and Rotate added, 45–75 → 25–45 h; G9: `.ai` compound path paint, #1410, overprint kept on import, #1388, text-only PDF parts, #1387; G1: Transform dialogs preview, #1412, transforms of direct-selected anchors, #1408, corner drags, #1229; G11: strokes wider than their shape, #1396; estimates unchanged; G9: `.ai` editing-data follow-ups (gradient stops, type placement, short area-type frames), #1390, #1391, #1392; G15: symbol instance art kept between frames, #1386; G1: dialog keyboard focus, #1367 and #1395; estimates unchanged; G9: placed (linked) files read from `.ai` editing data, #1264; G9: gradient shapes import as paths, #1304, and white constant alpha masks, #1286 in part; G15: linear deselect on hide or lock, #1353; estimates unchanged; G7: custom ruler origin done, #1212; G1: Artboard object clicks create a fitted artboard (#1227); remaining interaction pass and estimate unchanged; Pathfinder single-result source names and SVG IDs retained, #1293; DXF control-point SPLINE closure read, #1299 follow-up; G16: Korean catalog added, 70–110 → 60–100 h; Czech render-site localization follow-up; scores and estimates unchanged; Document Setup: Edit Artboards closes the modal after applying valid settings; pointer-click regressions; scores and hours unchanged; Artboards: Escape exits editing; an active drag cancels first; shortcut-routing regression; scores and hours unchanged; G11: valid selection after Simplify and Add Anchor Points (#1272); estimates unchanged; G2: Film Grain added; Photoshop effects 29/57; 28 filters remain; G10: RPM runtime dependency metadata (#1275); estimates unchanged; G15: texture-effect distance searches optimized (#1257); idle-machine/platform drag QA still pending; estimate unchanged; G1: Curvature point display improved (#1236, in part); estimates unchanged; G4: full-name font matching (#1258); Windows font-service QA pending; estimate unchanged; G11: no-op anchor Align keeps history and saved state (#1180 follow-up); estimates unchanged; G1: dedicated Eyedropper cursor and precise-cursor checks (#1269); estimates unchanged; G1: Eyedropper reads visible locked and outside-isolation sources (#1251); estimate unchanged; G11: Direct Selection segment Delete (#1278); estimates unchanged; G1: painted strokes regain the existing default after None/zero-width inheritance (#1253); estimate unchanged; G15: large-selection edits, cached live effects and faster Pathfinder, #1173; G4: proportional vertical metrics done, #966; G11: Shape Builder merge seams on grids of lines fixed, #893; scaled text controls corrected, #1034; group font-size preservation deferred; G9: `.ai` gradient matrices and meshes from the editing data; estimate unchanged; G9: `.ai` art styles from the editing data; estimate unchanged; G9: `.ai` editing-data follow-ups (gradient stops, type placement, short area-type frames), #1390, #1391, #1392; G15: symbol instance art kept between frames, #1386; G1: dialog keyboard focus, #1367 and #1395; estimates unchanged; G9: placed (linked) files read from `.ai` editing data, #1264; G9: gradient shapes import as paths, #1304, and white constant alpha masks, #1286 in part; G15: linear deselect on hide or lock, #1353; estimates unchanged; G7: custom ruler origin done, #1212; G1: Artboard object clicks create a fitted artboard (#1227); remaining interaction pass and estimate unchanged; Pathfinder single-result source names and SVG IDs retained, #1293; DXF control-point SPLINE closure read, #1299 follow-up; G16: Korean catalog added, 70–110 → 60–100 h; Czech render-site localization follow-up; scores and estimates unchanged; Document Setup: Edit Artboards closes the modal after applying valid settings; pointer-click regressions; scores and hours unchanged; Artboards: Escape exits editing; an active drag cancels first; shortcut-routing regression; scores and hours unchanged; G11: valid selection after Simplify and Add Anchor Points (#1272); estimates unchanged; G2: Film Grain added; Photoshop effects 29/57; 28 filters remain; G10: RPM runtime dependency metadata (#1275); estimates unchanged; G15: texture-effect distance searches optimized (#1257); idle-machine/platform drag QA still pending; estimate unchanged; G1: Curvature point display improved (#1236, in part); estimates unchanged; G4: full-name font matching (#1258); Windows font-service QA pending; estimate unchanged; G11: no-op anchor Align keeps history and saved state (#1180 follow-up); estimates unchanged; G1: dedicated Eyedropper cursor and precise-cursor checks (#1269); estimates unchanged; G1: Eyedropper reads visible locked and outside-isolation sources (#1251); estimate unchanged; G11: Direct Selection segment Delete (#1278); estimates unchanged; G1: painted strokes regain the existing default after None/zero-width inheritance (#1253); estimate unchanged; G15: large-selection edits, cached live effects and faster Pathfinder, #1173; G4: proportional vertical metrics done, #966; G11: Shape Builder merge seams on grids of lines fixed, #893; scaled text controls corrected, #1034; group font-size preservation deferred; G9: `.ai` gradient matrices and meshes from the editing data; estimate unchanged; G9: `.ai` editing-data follow-ups (gradient stops, type placement, short area-type frames), #1390, #1391, #1392; G15: symbol instance art kept between frames, #1386; G1: dialog keyboard focus, #1367 and #1395; estimates unchanged; G9: placed (linked) files read from `.ai` editing data, #1264; G9: gradient shapes import as paths, #1304, and white constant alpha masks, #1286 in part; G15: linear deselect on hide or lock, #1353; estimates unchanged; G7: custom ruler origin done, #1212; G1: Artboard object clicks create a fitted artboard (#1227); remaining interaction pass and estimate unchanged; Pathfinder single-result source names and SVG IDs retained, #1293; DXF control-point SPLINE closure read, #1299 follow-up; G16: Korean catalog added, 70–110 → 60–100 h; Czech render-site localization follow-up; scores and estimates unchanged; Document Setup: Edit Artboards closes the modal after applying valid settings; pointer-click regressions; scores and hours unchanged; Artboards: Escape exits editing; an active drag cancels first; shortcut-routing regression; scores and hours unchanged; G11: valid selection after Simplify and Add Anchor Points (#1272); estimates unchanged; G2: Film Grain added; Photoshop effects 29/57; 28 filters remain; G10: RPM runtime dependency metadata (#1275); estimates unchanged; G15: texture-effect distance searches optimized (#1257); idle-machine/platform drag QA still pending; estimate unchanged; G1: Curvature point display improved (#1236, in part); estimates unchanged; G4: full-name font matching (#1258); Windows font-service QA pending; estimate unchanged; G11: no-op anchor Align keeps history and saved state (#1180 follow-up); estimates unchanged; G1: dedicated Eyedropper cursor and precise-cursor checks (#1269); estimates unchanged; G1: Eyedropper reads visible locked and outside-isolation sources (#1251); estimate unchanged; G11: Direct Selection segment Delete (#1278); estimates unchanged; G1: painted strokes regain the existing default after None/zero-width inheritance (#1253); estimate unchanged; G15: large-selection edits, cached live effects and faster Pathfinder, #1173; G4: proportional vertical metrics done, #966; G11: Shape Builder merge seams on grids of lines fixed, #893; scaled text controls corrected, #1034; group font-size preservation deferred; SVG knockout groups of opaque art export without masks; G9: `.ai` editing-data follow-ups (gradient stops, type placement, short area-type frames), #1390, #1391, #1392; G15: symbol instance art kept between frames, #1386; G1: dialog keyboard focus, #1367 and #1395; estimates unchanged; G9: placed (linked) files read from `.ai` editing data, #1264; G9: gradient shapes import as paths, #1304, and white constant alpha masks, #1286 in part; G15: linear deselect on hide or lock, #1353; estimates unchanged; G7: custom ruler origin done, #1212; G1: Artboard object clicks create a fitted artboard (#1227); remaining interaction pass and estimate unchanged; Pathfinder single-result source names and SVG IDs retained, #1293; DXF control-point SPLINE closure read, #1299 follow-up; G16: Korean catalog added, 70–110 → 60–100 h; Czech render-site localization follow-up; scores and estimates unchanged; Document Setup: Edit Artboards closes the modal after applying valid settings; pointer-click regressions; scores and hours unchanged; Artboards: Escape exits editing; an active drag cancels first; shortcut-routing regression; scores and hours unchanged; G11: valid selection after Simplify and Add Anchor Points (#1272); estimates unchanged; G2: Film Grain added; Photoshop effects 29/57; 28 filters remain; G10: RPM runtime dependency metadata (#1275); estimates unchanged; G15: texture-effect distance searches optimized (#1257); idle-machine/platform drag QA still pending; estimate unchanged; G1: Curvature point display improved (#1236, in part); estimates unchanged; G4: full-name font matching (#1258); Windows font-service QA pending; estimate unchanged; G11: no-op anchor Align keeps history and saved state (#1180 follow-up); estimates unchanged; G1: dedicated Eyedropper cursor and precise-cursor checks (#1269); estimates unchanged; G1: Eyedropper reads visible locked and outside-isolation sources (#1251); estimate unchanged; G11: Direct Selection segment Delete (#1278); estimates unchanged; G1: painted strokes regain the existing default after None/zero-width inheritance (#1253); estimate unchanged; G15: large-selection edits, cached live effects and faster Pathfinder, #1173; G4: proportional vertical metrics done, #966; G11: Shape Builder merge seams on grids of lines fixed, #893; scaled text controls corrected, #1034; group font-size preservation deferred; G9: `.ai` keeps its RGB numbers, PDF import keeps strokes apart from fills faded or masked by their group; estimates unchanged) · **Target:** Adobe Illustrator 2026 (30.x) +> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-17 · **Change:** minor (G12: Background Export progress dialog with Cancel, #2564; no entry closed, estimates unchanged; 3D lighting: one to ten colored lights, smooth colored Inflate, legacy white-light appearance preserved; surface batching and restored seam minimum; scores and estimates unchanged; Blend spine Anchor Point conversions, selection/stacking priority and Shift regression coverage (#2544); scores and hours unchanged; transparency grid visibility saved in native documents, #1968; G6: MCP cancellation, progress notifications and busy answers during long exports, #2165, #2171, #2172; EPS editing data: legacy mesh corners, vector opacity masks and compound member paint retained; raster page comparison filters minification without relaxing limits; scores and hours unchanged; correction: quick Image Trace presets leave the panel as it was, the Image Trace button opens it; batch exporting, #1935; G2: Effect › Sketch › Halftone Pattern; Photoshop filters 30/57, 27 remaining (22–38 h); G5: Symbol Options Export Type (Graphic / Movie Clip); G1: Smart Guides line extension, #2120; G16: Brazilian Portuguese catalog complete under the tests, #1914; Localization 71% → 74%, hours 55–90 → 50–85; G6: image variables landed, estimate 8–14 → 6–12 h; Warp Options controls verified by focused checks and original-image review; estimates unchanged; existing-guide Escape cancellation validated 2026-10-14; scores and hours unchanged; open-path anchor Delete keeps surviving runs separate; estimates unchanged; Artboard tab-switch follow-up historically validated on `a0671777`; scores and hours unchanged; Artboard clean active-tab close/Delete follow-up validated 2026-10-15; G13: Object › Intertwine landed, #1763; no entry closed, estimates unchanged; MCP `add_text` exposes the existing `vertical` option for point, area and path type; horizontal remains the default; scores and hours unchanged; Vertical fullwidth question and exclamation marks keep their nominal glyph upright when the font has no vertical alternate; scores and hours unchanged.; G1: Curvature curve geometry, #1236 in part; Width-point finite-arithmetic follow-up validated; scores and hours unchanged; Image Trace adjustment discovery; G9: legacy `.ai` of version 7 saved with type as text and hidden objects kept, legacy type read, #868; estimate unchanged; Revolve: automatic smooth shading and default-on Cap with continuous rims; later 2026-10-14: G8 untrusted files, #1852, #1862–#1873; G11 anchor X/Y/W/H, #1824, Snap to Grid over Snap to Pixel, #1816, redo during a drag, #1719, Dim Images, #1683, failed-save dialog, #1685; gaps noted for reporters: G1 Warp Options for Effect › Warp, #1760, Color Picker Tab order, #1692, key anchor, #1636; G7 custom toolbars, #1759, workspace panel widths, #1753; G11 Shape Builder gaps between closed shapes, #1691, interior anchor delete; G13 Intertwine, #1763, gradient mesh from an image, #1676, Image Trace radial gradients, #1849; G12 duplicated line removed; no entry closed, estimates unchanged; verified PngSuite downloads, #1729; synthetic control-input budgets, #1709; 2026-10-14 fixes: G11 anchor Align and Distribute, #1636, Delete and selection, #1674, #1666, #1743, #1686, Roughen and Scribble, #1643, #1648; G8 file and input hardening, #1776–#1783, #1786, #1807; G10 Wayland cursors and the Windows title bar, unconfirmed, #1645, #1758; no entry closed, estimates unchanged; later 2026-10-13 fixes, #1570, #1578, #1590, #1613, #1615, #1616, #1620 in part, #1602 in part; no entry closed, estimates unchanged; TIFF is available in Export for Screens and Asset Export with shared format settings; scores and hours unchanged; G1/M11: Artboard deletion and history selection normalization; history regressions and full workspace CI pass; scores and hours unchanged; Character styles retain explicit Auto leading, propagate resets and preserve local leading overrides; scores and hours unchanged; Width Point Edit: Shift-double-click enables adjoining adjustment; plain double-click keeps it off; scores and hours unchanged; G12: Identify Objects By acts, #394; ~16 → ~15 prefs still inert; G12: Appearance of Black › Printing / Exporting acts, #394; ~16 → ~15 prefs still inert; control transport deadlines, #1726; G1: Average and Join in one step with a corner or smooth choice added as missing (it was listed as done in ui-parity.md); Join after Average, #1533; guide objects, #1534; Keyboard Shortcuts, #1539; G9 and G11 fixes, #1528, #1530, #1544, #1553, #1512, #1537; estimates unchanged; Direct Selection anchor precedence, #1508; dialog slider rails, #1483; template layers in Outline view, #1486; Tabs panel stop drags, #1458; type that does not fit its frame is reported, #1471; 3D on outlined type, #1481, Twist range, #1524, twisted walls, #1525; print-size images, #1517; French catalog, #1511; estimates unchanged; G1: Split Into Grid options, #1462, Transform Objects and Reflect's angle, #1448 and #1460, Pathfinder on a group, #1450, value fields, #1457 and #1465 in part; G9: grayscale TIFFs with alpha, #1466 in part; G6: unknown parameter keys refused, #1442; G10: window surfaces past 8192 px, #1459 in part; G15: many-object edits, #1429 in part; estimates unchanged; G16: Vietnamese catalog added, complete under the tests; three key languages remain, 60–100 → 55–90 h; G3: Extrude & Bevel, Inflate and Rotate added, 45–75 → 25–45 h; G9: `.ai` compound path paint, #1410, overprint kept on import, #1388, text-only PDF parts, #1387; G1: Transform dialogs preview, #1412, transforms of direct-selected anchors, #1408, corner drags, #1229; G11: strokes wider than their shape, #1396; estimates unchanged; G9: `.ai` editing-data follow-ups (gradient stops, type placement, short area-type frames), #1390, #1391, #1392; G15: symbol instance art kept between frames, #1386; G1: dialog keyboard focus, #1367 and #1395; estimates unchanged; G9: placed (linked) files read from `.ai` editing data, #1264; G9: gradient shapes import as paths, #1304, and white constant alpha masks, #1286 in part; G15: linear deselect on hide or lock, #1353; estimates unchanged; G7: custom ruler origin done, #1212; G1: Artboard object clicks create a fitted artboard (#1227); remaining interaction pass and estimate unchanged; Pathfinder single-result source names and SVG IDs retained, #1293; DXF control-point SPLINE closure read, #1299 follow-up; G16: Korean catalog added, 70–110 → 60–100 h; Czech render-site localization follow-up; scores and estimates unchanged; Document Setup: Edit Artboards closes the modal after applying valid settings; pointer-click regressions; scores and hours unchanged; Artboards: Escape exits editing; an active drag cancels first; shortcut-routing regression; scores and hours unchanged; G11: valid selection after Simplify and Add Anchor Points (#1272); estimates unchanged; G2: Film Grain added; Photoshop effects 29/57; 28 filters remain; G10: RPM runtime dependency metadata (#1275); estimates unchanged; G15: texture-effect distance searches optimized (#1257); idle-machine/platform drag QA still pending; estimate unchanged; G1: Curvature point display improved (#1236, in part); estimates unchanged; G4: full-name font matching (#1258); Windows font-service QA pending; estimate unchanged; G11: no-op anchor Align keeps history and saved state (#1180 follow-up); estimates unchanged; G1: dedicated Eyedropper cursor and precise-cursor checks (#1269); estimates unchanged; G1: Eyedropper reads visible locked and outside-isolation sources (#1251); estimate unchanged; G11: Direct Selection segment Delete (#1278); estimates unchanged; G1: painted strokes regain the existing default after None/zero-width inheritance (#1253); estimate unchanged; G15: large-selection edits, cached live effects and faster Pathfinder, #1173; G4: proportional vertical metrics done, #966; G11: Shape Builder merge seams on grids of lines fixed, #893; scaled text controls corrected, #1034; group font-size preservation deferred; G9: `.ai` gradient matrices and meshes from the editing data; estimate unchanged; G9: `.ai` art styles from the editing data; estimate unchanged; G9: `.ai` editing-data follow-ups (gradient stops, type placement, short area-type frames), #1390, #1391, #1392; G15: symbol instance art kept between frames, #1386; G1: dialog keyboard focus, #1367 and #1395; estimates unchanged; G9: placed (linked) files read from `.ai` editing data, #1264; G9: gradient shapes import as paths, #1304, and white constant alpha masks, #1286 in part; G15: linear deselect on hide or lock, #1353; estimates unchanged; G7: custom ruler origin done, #1212; G1: Artboard object clicks create a fitted artboard (#1227); remaining interaction pass and estimate unchanged; Pathfinder single-result source names and SVG IDs retained, #1293; DXF control-point SPLINE closure read, #1299 follow-up; G16: Korean catalog added, 70–110 → 60–100 h; Czech render-site localization follow-up; scores and estimates unchanged; Document Setup: Edit Artboards closes the modal after applying valid settings; pointer-click regressions; scores and hours unchanged; Artboards: Escape exits editing; an active drag cancels first; shortcut-routing regression; scores and hours unchanged; G11: valid selection after Simplify and Add Anchor Points (#1272); estimates unchanged; G2: Film Grain added; Photoshop effects 29/57; 28 filters remain; G10: RPM runtime dependency metadata (#1275); estimates unchanged; G15: texture-effect distance searches optimized (#1257); idle-machine/platform drag QA still pending; estimate unchanged; G1: Curvature point display improved (#1236, in part); estimates unchanged; G4: full-name font matching (#1258); Windows font-service QA pending; estimate unchanged; G11: no-op anchor Align keeps history and saved state (#1180 follow-up); estimates unchanged; G1: dedicated Eyedropper cursor and precise-cursor checks (#1269); estimates unchanged; G1: Eyedropper reads visible locked and outside-isolation sources (#1251); estimate unchanged; G11: Direct Selection segment Delete (#1278); estimates unchanged; G1: painted strokes regain the existing default after None/zero-width inheritance (#1253); estimate unchanged; G15: large-selection edits, cached live effects and faster Pathfinder, #1173; G4: proportional vertical metrics done, #966; G11: Shape Builder merge seams on grids of lines fixed, #893; scaled text controls corrected, #1034; group font-size preservation deferred; G9: `.ai` gradient matrices and meshes from the editing data; estimate unchanged; G9: `.ai` editing-data follow-ups (gradient stops, type placement, short area-type frames), #1390, #1391, #1392; G15: symbol instance art kept between frames, #1386; G1: dialog keyboard focus, #1367 and #1395; estimates unchanged; G9: placed (linked) files read from `.ai` editing data, #1264; G9: gradient shapes import as paths, #1304, and white constant alpha masks, #1286 in part; G15: linear deselect on hide or lock, #1353; estimates unchanged; G7: custom ruler origin done, #1212; G1: Artboard object clicks create a fitted artboard (#1227); remaining interaction pass and estimate unchanged; Pathfinder single-result source names and SVG IDs retained, #1293; DXF control-point SPLINE closure read, #1299 follow-up; G16: Korean catalog added, 70–110 → 60–100 h; Czech render-site localization follow-up; scores and estimates unchanged; Document Setup: Edit Artboards closes the modal after applying valid settings; pointer-click regressions; scores and hours unchanged; Artboards: Escape exits editing; an active drag cancels first; shortcut-routing regression; scores and hours unchanged; G11: valid selection after Simplify and Add Anchor Points (#1272); estimates unchanged; G2: Film Grain added; Photoshop effects 29/57; 28 filters remain; G10: RPM runtime dependency metadata (#1275); estimates unchanged; G15: texture-effect distance searches optimized (#1257); idle-machine/platform drag QA still pending; estimate unchanged; G1: Curvature point display improved (#1236, in part); estimates unchanged; G4: full-name font matching (#1258); Windows font-service QA pending; estimate unchanged; G11: no-op anchor Align keeps history and saved state (#1180 follow-up); estimates unchanged; G1: dedicated Eyedropper cursor and precise-cursor checks (#1269); estimates unchanged; G1: Eyedropper reads visible locked and outside-isolation sources (#1251); estimate unchanged; G11: Direct Selection segment Delete (#1278); estimates unchanged; G1: painted strokes regain the existing default after None/zero-width inheritance (#1253); estimate unchanged; G15: large-selection edits, cached live effects and faster Pathfinder, #1173; G4: proportional vertical metrics done, #966; G11: Shape Builder merge seams on grids of lines fixed, #893; scaled text controls corrected, #1034; group font-size preservation deferred; SVG knockout groups of opaque art export without masks; G9: `.ai` editing-data follow-ups (gradient stops, type placement, short area-type frames), #1390, #1391, #1392; G15: symbol instance art kept between frames, #1386; G1: dialog keyboard focus, #1367 and #1395; estimates unchanged; G9: placed (linked) files read from `.ai` editing data, #1264; G9: gradient shapes import as paths, #1304, and white constant alpha masks, #1286 in part; G15: linear deselect on hide or lock, #1353; estimates unchanged; G7: custom ruler origin done, #1212; G1: Artboard object clicks create a fitted artboard (#1227); remaining interaction pass and estimate unchanged; Pathfinder single-result source names and SVG IDs retained, #1293; DXF control-point SPLINE closure read, #1299 follow-up; G16: Korean catalog added, 70–110 → 60–100 h; Czech render-site localization follow-up; scores and estimates unchanged; Document Setup: Edit Artboards closes the modal after applying valid settings; pointer-click regressions; scores and hours unchanged; Artboards: Escape exits editing; an active drag cancels first; shortcut-routing regression; scores and hours unchanged; G11: valid selection after Simplify and Add Anchor Points (#1272); estimates unchanged; G2: Film Grain added; Photoshop effects 29/57; 28 filters remain; G10: RPM runtime dependency metadata (#1275); estimates unchanged; G15: texture-effect distance searches optimized (#1257); idle-machine/platform drag QA still pending; estimate unchanged; G1: Curvature point display improved (#1236, in part); estimates unchanged; G4: full-name font matching (#1258); Windows font-service QA pending; estimate unchanged; G11: no-op anchor Align keeps history and saved state (#1180 follow-up); estimates unchanged; G1: dedicated Eyedropper cursor and precise-cursor checks (#1269); estimates unchanged; G1: Eyedropper reads visible locked and outside-isolation sources (#1251); estimate unchanged; G11: Direct Selection segment Delete (#1278); estimates unchanged; G1: painted strokes regain the existing default after None/zero-width inheritance (#1253); estimate unchanged; G15: large-selection edits, cached live effects and faster Pathfinder, #1173; G4: proportional vertical metrics done, #966; G11: Shape Builder merge seams on grids of lines fixed, #893; scaled text controls corrected, #1034; group font-size preservation deferred; G9: `.ai` keeps its RGB numbers, PDF import keeps strokes apart from fills faded or masked by their group; estimates unchanged) · **Target:** Adobe Illustrator 2026 (30.x) Every known shortfall against Illustrator, one entry each, **ranked by how much it stops a professional from switching**. This is the work list: unless you were given a task, pick the highest-ranked gap you can make @@ -202,6 +202,7 @@ from G14 and the budget run of G15: **~170–270 h** one agent, ~50–80 h wall ## G12 Preferences and panels that don't act yet +- **Done (2026-10-17):** Background Export shows its progress in a dialog with Cancel and Continue in Background (#2564), on the shared cancel token (#2546); a cancelled export writes no file. Save As, Export for Screens and Export Selection don't show it yet. Estimate unchanged. - **Done:** quick Image Trace presets trace at once and leave the panel as it was; the Image Trace button (Control bar, Properties) opens the controls with Threshold and Advanced (Paths, Corners, Noise), as the public documentation describes (). Other context-specific Properties gaps remain. - **Missing:** ~14 Preferences options stored but not acted on (#394: Open @@ -349,6 +350,7 @@ from G14 and the budget run of G15: **~170–270 h** one agent, ~50–80 h wall | Date | Change | Summary | |---|---|---| | 2026-10-17 | minor | Blend spine Anchor Point conversions, selection/stacking priority and Shift regression coverage (#2544); scores and hours unchanged. Windows native canvas checks and screenshots reviewed in PR #2643. | +| 2026-10-17 | minor | G12: Background Export progress dialog with Cancel and Continue in Background (#2564); no entry closed, estimates unchanged | | 2026-10-16 | minor | Native documents restore transparency grid visibility (#1968); save/open regression covers on/off, old files and SVG; scores and hours unchanged (estimated) | | 2026-10-16 | minor | G6: MCP cancellation, progress notifications and a busy answer during long exports (#2165, #2171, #2172); no entry closed, estimates unchanged | | 2026-10-16 | minor | G11: PDF export of a CMYK document reports its RGB colours and non-CMYK images, with a count, in the export warnings (#2162); no entry closed, estimates unchanged | diff --git a/docs/ui-parity.md b/docs/ui-parity.md index 4d361addb..94432f6f1 100644 --- a/docs/ui-parity.md +++ b/docs/ui-parity.md @@ -1,6 +1,6 @@ # UI and interaction parity -> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-17 · **Change:** minor (3D lighting: one to ten colored lights, smooth colored Inflate, legacy white-light appearance preserved; surface batching and restored seam minimum; scores and estimates unchanged; Blend spine Anchor Point conversions, selection/stacking priority and Shift regression coverage (#2544); scores and hours unchanged; Artboard Options' X and Y with a reference point, #1876; scores unchanged; library panels: one per library, dockable and tabbable, with Persistent, #2603; the Selection tool threads text from the out port, #2572 in part; scores unchanged; Group Selection picks and moves one path of a compound path, #2441 in part; the Move dialog opens on the last move and its last checkboxes; scores unchanged; transparency grid visibility saved in native documents, #1968; Live Corners radius up to the neighbouring cuts, #2332; Image Trace quick presets: wording corrected to the shipped behaviour, the Image Trace button opens the controls; batch exporting, #1935; document window scroll bars, #2322; Anchor Point tool: a click on a handle end retracts that handle only, #2267; scores unchanged; Smart Guides: the dragged end of a straight segment keeps to its extension at any angle, #2120; Warp Options controls verified by focused checks and original-image review; estimates unchanged; existing-guide Escape cancellation validated 2026-10-14; scores and hours unchanged; open-path anchor Delete keeps surviving runs separate; estimates unchanged; Artboard tab-switch follow-up historically validated on `a0671777`; scores and hours unchanged; Artboard clean active-tab close/Delete follow-up validated 2026-10-15; Curvature curve geometry, #1236 in part; Width-point finite-arithmetic follow-up validated; scores and hours unchanged; Image Trace adjustment discovery; later 2026-10-14 merges: macOS save sheet Format menu back to missing, #1855, #866 reopened; anchor X/Y/W/H in the Transform panel, Properties and Control bar, #1824; Snap to Grid over Snap to Pixel, #1816; toolbar labels, columns and the All Tools drawer, #1751; Actions panel selection, #1629 in part; scores unchanged; 2026-10-14 merges: anchor Align and Distribute, #1636; Delete on closed paths and whole objects, #1674; selection inside isolation, #1666; Escape in a Direct Selection drag and while typing, #1743, #1686; Pen clicks under 3 px, #1663; [ and ] on more tools, #1661 in part; fields commit on click away, #1787; command search keys, #1757; a guide's position in the Transform panel is a convenience with no parity claim, #1659; scores unchanged; intersect Smart Guides for drawn points and dragged anchors, #1602 in part; a ruler guide's position in the Transform panel, #1597 in part; Symbol Options before New Symbol, #1560; tool flyout widths, #1616; Pathfinder Trim, Merge and Crop strokes, #1570; scores unchanged; G1/M11: Artboard deletion and history selection normalization; history regressions and full workspace CI pass; scores and hours unchanged; Width Point Edit: Shift-double-click enables adjoining adjustment; plain double-click keeps it off; scores and hours unchanged; per-corner radius fields with a link toggle, split from #1594; Join merges anchors lying on one another, #1533; correction: Average and Join with a corner or smooth choice is missing, not done; guide objects take the guide colour and style and Lock Guides, #1534; Keyboard Shortcuts lists commands outside the menus, #1539; round corners next to curves and the Smooth tool's reach, #1512; Make Compound Path on a group, #1537; dialog slider rails, #1483; template layers stay in Preview in Outline view, #1486; Direct Selection takes the selected object's anchor where anchors coincide, #1508; Tabs panel stop drags, #1458; Split Into Grid dialog, #1462; Transform Objects and Reflect's angle, #1448 and #1460; Pathfinder panel on a group, #1450; value fields: readable selection, #1465 in part, and a focused field follows its value, #1457; scores unchanged; Transform dialogs have a live Preview and step with the arrow keys, #1412; transform tools, dialogs and Move act on direct-selected anchors, #1408; the bounding box hides during a corner drag, #1229; scores unchanged; dialogs focus their first field on every open, #1367, and a first field that starts greyed out once it is enabled, #1395; scores unchanged; Rectangular Grid and Polar Grid click dialogs take the skew, frame, compound path and Fill Grid options, #1377 in part, score unchanged; Live Corners: a Direct Selection click on a widget selects its corner, #1239; custom ruler origin, #1212; Artboard tool clicks fit the topmost editable object in one undo step (#1227); scores unchanged; Pathfinder single-result source names and SVG IDs retained, #1293; DXF control-point SPLINE closure read, #1299 follow-up; Document Setup: Edit Artboards closes the modal after applying valid settings; pointer-click regressions; scores and hours unchanged; Artboards: Escape exits editing; an active drag cancels first; shortcut-routing regression; scores and hours unchanged; Simplify preview/counts and valid rewritten-path selection (#1272); parity remains partial; Texture-effect drag redraws optimized (#1257); idle Windows interaction still unverified; scores unchanged; Curvature point display improved (#1236, in part); scores unchanged; Direct Selection: no-op anchor Align preserves Undo, Redo and saved state (#1180 follow-up); Eyedropper cursor distinguished from precise crosshair (#1269); scores unchanged; Eyedropper can read visible locked and outside-isolation sources (#1251); scores unchanged; Direct Selection: independent segment picks and Delete (#1278); scores unchanged; Adding paint to an unpainted zero-width stroke restores the 1 pt default (#1253); scores unchanged; macOS save sheets offer a Format menu, #866; Attributes fill-rule controls edit the compound when a member is selected, #1197; a marquee needs to touch a path, not just lie inside its fill, #1103; earlier: Live Corners round corners between curved sides, #1144) · **Target:** Adobe Illustrator 2026 (30.x) +> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-17 · **Change:** minor (Background Export progress dialog with Cancel, #2564; scores unchanged; 3D lighting: one to ten colored lights, smooth colored Inflate, legacy white-light appearance preserved; surface batching and restored seam minimum; scores and estimates unchanged; Blend spine Anchor Point conversions, selection/stacking priority and Shift regression coverage (#2544); scores and hours unchanged; Artboard Options' X and Y with a reference point, #1876; scores unchanged; library panels: one per library, dockable and tabbable, with Persistent, #2603; the Selection tool threads text from the out port, #2572 in part; scores unchanged; Group Selection picks and moves one path of a compound path, #2441 in part; the Move dialog opens on the last move and its last checkboxes; scores unchanged; transparency grid visibility saved in native documents, #1968; Live Corners radius up to the neighbouring cuts, #2332; Image Trace quick presets: wording corrected to the shipped behaviour, the Image Trace button opens the controls; batch exporting, #1935; document window scroll bars, #2322; Anchor Point tool: a click on a handle end retracts that handle only, #2267; scores unchanged; Smart Guides: the dragged end of a straight segment keeps to its extension at any angle, #2120; Warp Options controls verified by focused checks and original-image review; estimates unchanged; existing-guide Escape cancellation validated 2026-10-14; scores and hours unchanged; open-path anchor Delete keeps surviving runs separate; estimates unchanged; Artboard tab-switch follow-up historically validated on `a0671777`; scores and hours unchanged; Artboard clean active-tab close/Delete follow-up validated 2026-10-15; Curvature curve geometry, #1236 in part; Width-point finite-arithmetic follow-up validated; scores and hours unchanged; Image Trace adjustment discovery; later 2026-10-14 merges: macOS save sheet Format menu back to missing, #1855, #866 reopened; anchor X/Y/W/H in the Transform panel, Properties and Control bar, #1824; Snap to Grid over Snap to Pixel, #1816; toolbar labels, columns and the All Tools drawer, #1751; Actions panel selection, #1629 in part; scores unchanged; 2026-10-14 merges: anchor Align and Distribute, #1636; Delete on closed paths and whole objects, #1674; selection inside isolation, #1666; Escape in a Direct Selection drag and while typing, #1743, #1686; Pen clicks under 3 px, #1663; [ and ] on more tools, #1661 in part; fields commit on click away, #1787; command search keys, #1757; a guide's position in the Transform panel is a convenience with no parity claim, #1659; scores unchanged; intersect Smart Guides for drawn points and dragged anchors, #1602 in part; a ruler guide's position in the Transform panel, #1597 in part; Symbol Options before New Symbol, #1560; tool flyout widths, #1616; Pathfinder Trim, Merge and Crop strokes, #1570; scores unchanged; G1/M11: Artboard deletion and history selection normalization; history regressions and full workspace CI pass; scores and hours unchanged; Width Point Edit: Shift-double-click enables adjoining adjustment; plain double-click keeps it off; scores and hours unchanged; per-corner radius fields with a link toggle, split from #1594; Join merges anchors lying on one another, #1533; correction: Average and Join with a corner or smooth choice is missing, not done; guide objects take the guide colour and style and Lock Guides, #1534; Keyboard Shortcuts lists commands outside the menus, #1539; round corners next to curves and the Smooth tool's reach, #1512; Make Compound Path on a group, #1537; dialog slider rails, #1483; template layers stay in Preview in Outline view, #1486; Direct Selection takes the selected object's anchor where anchors coincide, #1508; Tabs panel stop drags, #1458; Split Into Grid dialog, #1462; Transform Objects and Reflect's angle, #1448 and #1460; Pathfinder panel on a group, #1450; value fields: readable selection, #1465 in part, and a focused field follows its value, #1457; scores unchanged; Transform dialogs have a live Preview and step with the arrow keys, #1412; transform tools, dialogs and Move act on direct-selected anchors, #1408; the bounding box hides during a corner drag, #1229; scores unchanged; dialogs focus their first field on every open, #1367, and a first field that starts greyed out once it is enabled, #1395; scores unchanged; Rectangular Grid and Polar Grid click dialogs take the skew, frame, compound path and Fill Grid options, #1377 in part, score unchanged; Live Corners: a Direct Selection click on a widget selects its corner, #1239; custom ruler origin, #1212; Artboard tool clicks fit the topmost editable object in one undo step (#1227); scores unchanged; Pathfinder single-result source names and SVG IDs retained, #1293; DXF control-point SPLINE closure read, #1299 follow-up; Document Setup: Edit Artboards closes the modal after applying valid settings; pointer-click regressions; scores and hours unchanged; Artboards: Escape exits editing; an active drag cancels first; shortcut-routing regression; scores and hours unchanged; Simplify preview/counts and valid rewritten-path selection (#1272); parity remains partial; Texture-effect drag redraws optimized (#1257); idle Windows interaction still unverified; scores unchanged; Curvature point display improved (#1236, in part); scores unchanged; Direct Selection: no-op anchor Align preserves Undo, Redo and saved state (#1180 follow-up); Eyedropper cursor distinguished from precise crosshair (#1269); scores unchanged; Eyedropper can read visible locked and outside-isolation sources (#1251); scores unchanged; Direct Selection: independent segment picks and Delete (#1278); scores unchanged; Adding paint to an unpainted zero-width stroke restores the 1 pt default (#1253); scores unchanged; macOS save sheets offer a Format menu, #866; Attributes fill-rule controls edit the compound when a member is selected, #1197; a marquee needs to touch a path, not just lie inside its fill, #1103; earlier: Live Corners round corners between curved sides, #1144) · **Target:** Adobe Illustrator 2026 (30.x) How VectorCraft's tools, handles, snapping and panels behave next to Illustrator's. A vector illustrator's trust is in the details: where a handle lands, what Shift and Alt do mid-drag, what a smart guide snaps to, @@ -226,6 +226,7 @@ panel-menu commands such as Break Link to Symbol), so they can be given shortcut | Blend panel (30.7) | partial | Window › Blend: Make, Spacing, Orientation, Reverse Front to Back, Reverse Spine, Replace Spine, Expand and Release, running the Object › Blend commands; the easing and color controls are missing | | SVG Interactivity, Retype, Comments, Version History panels | missing | the last two are cloud features (out of scope) | | Workspaces (save, reset, switch) | done | | +| Background Export: a progress dialog with the file name, the artboard count and a bar (busy for one artboard and for formats made in one go), Cancel (Esc) and Continue in Background; it opens after 0.4 s and never over another dialog. A cancelled export writes no file | done | #2564; on the shared `vectorcraft_doc::cancel` token (#2546), so a cancelled render keeps no half-drawn pattern tile or effect raster. The bar moves between artboards, not within one. Save As, Export for Screens and Export Selection don't show it yet | | Swatch and graphic style library panels: each library opens in a panel of its own, several at once, that floats, groups, docks and closes like any panel; opening an open library brings its panel forward; Previous / Next Library change the library in place; the panel menu's Persistent reopens the panel at startup with its view and find field (a library loaded from a file is read again, a missing file is skipped with a status-bar note) | done | #2603; panel ids `library::`, `ui.libraryPanel {panel, library?, view?, showFind?, persistent?}`; the library panels have no sort option, as before | | DXF export/import preserves intended curve closure | done | #1299; R13–2018 closed SPLINE bit set/read; an open curve ending at its start stays open; knots and control points unchanged; native CIRCLE/ARC export and CNC preset remain pending | | macOS save sheet: choose the format from a menu | done | #866; the save panel is VectorCraft's own `NSSavePanel` with its Format menu set before the sheet opens (`apps/vectorcraft/src/mac_save_format.rs`); #1855 had withdrawn the menu that was added after the sheet began, which made Save As, Save a Copy and the first Save show no sheet | @@ -248,6 +249,7 @@ re-measure of the panels added since. Slider rails show on Medium Dark (#1808, # |---|---|---| | 2026-10-17 | minor | Blend spine Anchor Point conversions, selection/stacking priority and Shift regression coverage (#2544); scores and hours unchanged. Windows native canvas checks and screenshots reviewed in PR #2643. | | 2026-10-17 | minor | Artboard Options has X and Y beside a reference point locator, counted from the ruler origin as the Transform panel's are; a typed X or Y moves the artboard's reference point there and a new Width or Height keeps it in place (#1876); scores unchanged (estimated) | +| 2026-10-17 | minor | Background Export shows a progress dialog with Cancel and Continue in Background (#2564), built on the shared cancel token of #2546; scores and hours unchanged (estimated) | | 2026-10-17 | minor | Library panels (#2603): each swatch or graphic style library opens in its own dockable, tabbable panel, several at once; the panel menu's Persistent reopens a panel at startup with its view and find field, a library loaded from a file read again from its path; scores unchanged (estimated) | | 2026-10-17 | minor | Selection tool: clicking the red out port of overflowing area type, then a closed path, empty canvas or a dragged rectangle threads the text there (#2572 in part); Escape cancels. Scores and hours unchanged. | | 2026-10-17 | minor | Group Selection picks one path of a compound path (the outer shape or a hole) and moves it on its own; a second click adds the compound path (#2441, in part) |