Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4176c0f297 | ||
|
|
349668a1e6 |
No files matched your search
+1
-1
@@ -4,7 +4,7 @@ VectorCraft is a clean-room, open-source, pure-Rust reimplementation of the Adob
|
||||
|
||||
This file tracks **how far we are and what's left**. Time estimates are wall-clock hours of continuous Claude Opus 5.5 agent work (including builds and the CI gate), given both for **one agent** and for **4–6 parallel agents** on disjoint crates. They are counted from the remaining work (see [Parity estimate](#parity-estimate)), calibrated against measured throughput, and updated as work lands.
|
||||
|
||||
_Last updated: 2026-10-09 (after placed documents (File › Place of a `.vectorcraft` file, linked), M4.14–M4.98, M8.1–M8.20, M14.4–M14.7, the MCP prompts, resource templates, completions and logging, the 2026-10-07 issue fixes, the Layers panel rework and the 2026-10-08 community contributions: Pen and shape-tool modifiers, path editing, Layers, Artboards, Swatches/Brushes and dialogs, Japanese composition and PDF text, Hebrew/Arabic type, interface languages (Spanish among them), the font menu, saved selections, MCP and EPS, the missing-font export warning (#319), open paths in the Shape Builder (#374), PDF/`.ai` import keeping strokes live and hidden and nested layers (#372), per-corner Live Corners (#381), SVG type opening at the size it draws at (#396), the Wayland clipboard with copied-file paste (#398), ruler guides that can be selected, moved and deleted (#414), two batches of Preferences options that were stored but unused (#394), skeleton reads with `document.find` for large documents over MCP (#416) and anchors dragged with Direct Selection snapping to points, segments and smart guides, clipped SVG layers opening as clipping layers (#420), PDF/`.ai` layers read on pages of tens of millions of operators (#419), Document Color Mode putting RGB greys on the black plate on request and converting pattern tiles (#421) and movable dialogs with Offset Path's Joins dropdown (#423) and numeric fields scrubbed by dragging their label (#400), stroke weight presets in the stroke unit (#426), the Appearance panel's Opacity popup (#430), type on a path brackets, flip and Type on a Path Options that keep the type in place (#429), Layers rows dragged with a ghost and dropped on the trash or New Layer (#435), tool flyouts opening on a right or corner press (#436), tool flyouts torn off into floating toolbars (#437), the Shape Builder's erase mode deleting edges too (#439), installed fonts registered outside the font folders and fonts installed while the app runs listed in the font menus (#443), alike circular corners on rectangles that aren't square (#442), tool cursors as OS cursors so the pointer no longer trails over the canvas (#444), artboards copied and pasted with their art (#446), guides dragged out of the rulers snapping to the art's side midpoints and the artboards, artboard guides (#451), the toolbar's double arrow and Window › Toolbars › Double Column switching between one and two columns in any window height (#455), the Smart Guides display options and Snapping Tolerance (#394), Font Size and Leading combo boxes with preset lists in the Character and Properties panels and the Control bar (#458), screenshots pasted from the clipboard on macOS, Windows and the web (#457), the Properties panel's Fill and Stroke matching the Control bar's, Illustrator's Window menu panel keys (Shift+F7 for Align…) and Alt-wheel zoom whatever the wheel preference (Discord feedback), the point/area type widget (Discord feedback), groups in legacy Illustrator .ai files (Discord feedback), a rotating desktop log file (#465), image controls in the Control bar and Properties panel: Image Trace with its presets, Mask, Crop Image, Embed/Unembed, and an Image Trace object's preset, Expand and Release (Discord feedback), the Pen working as the Anchor Point tool with Alt held over a selected path's handles and anchors, and anchor buttons that convert, connect and cut paths at the selected anchors (Discord feedback), Japanese hanging punctuation (burasagari, #463), Escape leaving Presentation Mode with no screen mode saved across restarts, documents dropped on the desktop app opening as tabs of their own and a note that an `.ai` file's art off its artboards isn't in the part read (#472), Add/Delete Anchor Point tool icons drawn from the Pen tool's (#479), EPS import running more of PostScript Level 3 (patterns, Type 3 fonts, mesh shadings, masked images, VM and resource probes), and a preview that stands in saying why (Discord feedback, #466, #469, #474), chords with modifiers recorded in Edit › Keyboard Shortcuts (#487), Type Object Selection by Path Only (#394), Anti-aliased Artwork (#394), Show Pixel Grid (#394), pen and touch taps pressed and lifted within one frame clicking where they tapped and the Linux/Wayland pen-tablet workaround (#491), bounding-box resizes landing on the other objects' anchors and centres with labels, and the Rotate, Scale, Shear and Reflect tools snapping their drags (#482), Shift-drag marquees that toggle the objects or anchors they reach and a Lasso whose Alt subtraction keeps the rest of the selection (#483), the mouse wheel stepping the focused numeric field, the dialogs' and preferences' drag values made such fields (#485), Show The Home Screen When No Documents Are Open (#394), Numbers Without Units Are Points (#394), Auto-Collapse Iconic Panels (#394), the Contextual Task Bar moved by its handle and its More Options menu pinning and resetting its position, a designed macOS DMG windoLine truncated
|
||||
_Last updated: 2026-10-09 (after placed documents (File › Place of a `.vectorcraft` file, linked), M4.14–M4.98, M8.1–M8.20, M14.4–M14.7, the MCP prompts, resource templates, completions and logging, the 2026-10-07 issue fixes, the Layers panel rework and the 2026-10-08 community contributions: Pen and shape-tool modifiers, path editing, Layers, Artboards, Swatches/Brushes and dialogs, Japanese composition and PDF text, Hebrew/Arabic type, interface languages (Spanish among them), the font menu, saved selections, MCP and EPS, the missing-font export warning (#319), open paths in the Shape Builder (#374), PDF/`.ai` import keeping strokes live and hidden and nested layers (#372), per-corner Live Corners (#381), SVG type opening at the size it draws at (#396), the Wayland clipboard with copied-file paste (#398), ruler guides that can be selected, moved and deleted (#414), two batches of Preferences options that were stored but unused (#394), skeleton reads with `document.find` for large documents over MCP (#416) and anchors dragged with Direct Selection snapping to points, segments and smart guides, clipped SVG layers opening as clipping layers (#420), PDF/`.ai` layers read on pages of tens of millions of operators (#419), Document Color Mode putting RGB greys on the black plate on request and converting pattern tiles (#421) and movable dialogs with Offset Path's Joins dropdown (#423) and numeric fields scrubbed by dragging their label (#400), stroke weight presets in the stroke unit (#426), the Appearance panel's Opacity popup (#430), type on a path brackets, flip and Type on a Path Options that keep the type in place (#429), Layers rows dragged with a ghost and dropped on the trash or New Layer (#435), tool flyouts opening on a right or corner press (#436), tool flyouts torn off into floating toolbars (#437), the Shape Builder's erase mode deleting edges too (#439), installed fonts registered outside the font folders and fonts installed while the app runs listed in the font menus (#443), alike circular corners on rectangles that aren't square (#442), tool cursors as OS cursors so the pointer no longer trails over the canvas (#444), artboards copied and pasted with their art (#446), guides dragged out of the rulers snapping to the art's side midpoints and the artboards, artboard guides (#451), the toolbar's double arrow and Window › Toolbars › Double Column switching between one and two columns in any window height (#455), the Smart Guides display options and Snapping Tolerance (#394), Font Size and Leading combo boxes with preset lists in the Character and Properties panels and the Control bar (#458), screenshots pasted from the clipboard on macOS, Windows and the web (#457), the Properties panel's Fill and Stroke matching the Control bar's, Illustrator's Window menu panel keys (Shift+F7 for Align…) and Alt-wheel zoom whatever the wheel preference (Discord feedback), the point/area type widget (Discord feedback), groups in legacy Illustrator .ai files (Discord feedback), a rotating desktop log file (#465), image controls in the Control bar and Properties panel: Image Trace with its presets, Mask, Crop Image, Embed/Unembed, and an Image Trace object's preset, Expand and Release (Discord feedback), the Pen working as the Anchor Point tool with Alt held over a selected path's handles and anchors, and anchor buttons that convert, connect and cut paths at the selected anchors (Discord feedback), Japanese hanging punctuation (burasagari, #463), Escape leaving Presentation Mode with no screen mode saved across restarts, documents dropped on the desktop app opening as tabs of their own and a note that an `.ai` file's art off its artboards isn't in the part read (#472), Add/Delete Anchor Point tool icons drawn from the Pen tool's (#479), EPS import running more of PostScript Level 3 (patterns, Type 3 fonts, mesh shadings, masked images, VM and resource probes), and a preview that stands in saying why (Discord feedback, #466, #469, #474), chords with modifiers recorded in Edit › Keyboard Shortcuts (#487), Type Object Selection by Path Only (#394), Anti-aliased Artwork (#394), Show Pixel Grid (#394), pen and touch taps pressed and lifted within one frame clicking where they tapped and the Linux/Wayland pen-tablet workaround (#491), bounding-box resizes landing on the other objects' anchors and centres with labels, and the Rotate, Scale, Shear and Reflect tools snapping their drags (#482), Shift-drag marquees that toggle the objects or anchors they reach and a Lasso whose Alt subtraction keeps the rest of the selection (#483), the mouse wheel stepping the focused numeric field, the dialogs' and preferences' drag values made such fields (#485), Show The Home Screen When No Documents Are Open (#394), Numbers Without Units Are Points (#394), Auto-Collapse Iconic Panels (#394), the Contextual Task Bar moved by its handle and its More Options menu pinning and resetting its position, a designed macOS DMG windoLine truncated
|
||||
|
||||
## Where we are
|
||||
|
||||
|
||||
@@ -94,6 +94,7 @@ pub use export_for_screens::open as open_export_for_screens;
|
||||
pub(crate) use export_for_screens::{
|
||||
KIND as EXPORT_FOR_SCREENS, formats as screen_formats, open_assets as open_export_for_screens_assets, saved_rows as screen_saved_rows,
|
||||
};
|
||||
pub(crate) use new_document::preset_name;
|
||||
pub use new_document::{open as open_new_document, preset_card};
|
||||
pub use png_options::open as open_raster_options;
|
||||
pub use save_pdf::{open as open_save_pdf, open_preset as open_pdf_preset};
|
||||
|
||||
@@ -255,7 +255,7 @@ fn is_builtin_preset(name: &str) -> bool {
|
||||
}
|
||||
|
||||
/// A preset's name as shown: a built-in one translated, the user's as it is.
|
||||
fn preset_name(name: &str) -> &str {
|
||||
pub(crate) fn preset_name(name: &str) -> &str {
|
||||
if is_builtin_preset(name) { tl!(name) } else { name }
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
use egui::Ui;
|
||||
use serde_json::json;
|
||||
use vectorcraft_doc::{LiveShape, NodeKind, Unit};
|
||||
use vectorcraft_engine::cmd::newdoc;
|
||||
|
||||
use super::{corner_radius_row, first_selected, pstate, set_pstate};
|
||||
use crate::theme::Tokens;
|
||||
@@ -196,8 +197,8 @@ fn multi_color(app: &mut VectorcraftApp, ctx: &egui::Context) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
/// Edit Artboards (the Artboard tool): the active artboard's name, position and size, New Artboard
|
||||
/// and Delete Artboard, and Exit back to the Selection tool (#530).
|
||||
/// Edit Artboards (the Artboard tool): the active artboard's name, position and size, its preset and
|
||||
/// orientation (#671), New Artboard and Delete Artboard, and Exit back to the Selection tool (#530).
|
||||
fn artboard_sections(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
let units = app.session.general_unit();
|
||||
let n = app.session.active().map_or(0, |d| d.doc.artboards.len());
|
||||
@@ -220,6 +221,7 @@ fn artboard_sections(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
ui.end_row();
|
||||
}
|
||||
});
|
||||
artboard_size_row(app, ui, i, ab.rect.width(), ab.rect.height());
|
||||
}
|
||||
ui.add_space(4.0);
|
||||
let w = (ui.available_width() - 6.0) / 2.0;
|
||||
@@ -238,6 +240,55 @@ fn artboard_sections(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Artboard `i`'s size preset (New Document's saved and built-in presets, by category, either way
|
||||
/// round; Custom when its `w` × `h` points match none) and its orientation. A preset sizes it in its
|
||||
/// current orientation; the other orientation swaps its width and height. Its top-left corner stays
|
||||
/// put either way.
|
||||
fn artboard_size_row(app: &mut VectorcraftApp, ui: &mut Ui, i: usize, w: f64, h: f64) {
|
||||
let landscape = w > h;
|
||||
// A preset `pw` × `ph` turned to the artboard's orientation.
|
||||
let turned = |pw: f64, ph: f64| if (pw > ph) == landscape || pw == ph { (pw, ph) } else { (ph, pw) };
|
||||
let same = |s: &newdoc::DocSettings| {
|
||||
let (pw, ph) = turned(s.width, s.height);
|
||||
(pw - w).abs() < 0.01 && (ph - h).abs() < 0.01
|
||||
};
|
||||
// Recent holds documents made, not presets.
|
||||
let cats: Vec<(&str, Vec<newdoc::DocSettings>)> = newdoc::category_names()
|
||||
.filter(|c| !c.eq_ignore_ascii_case("Recent"))
|
||||
.filter_map(|c| Some((c, newdoc::category(&app.session, c)?)))
|
||||
.filter(|(_, l)| !l.is_empty())
|
||||
.collect();
|
||||
let current = cats.iter().flat_map(|(_, l)| l).find(|s| same(s)).map_or(tl!("Custom"), |s| crate::dialogs::preset_name(&s.name)).to_string();
|
||||
let mut size = None;
|
||||
ui.horizontal(|ui| {
|
||||
dim_label(ui, tl!("Preset:"));
|
||||
size = widgets::combo(ui, "ab-preset", ¤t, 140.0, false, |ui| {
|
||||
let mut chosen = None;
|
||||
for (cat, list) in &cats {
|
||||
ui.menu_button(tl!(cat), |ui| {
|
||||
widgets::menu_scroll(ui, |ui| {
|
||||
for s in list {
|
||||
if widgets::menu_item_name(ui, crate::dialogs::preset_name(&s.name), true, same(s)) {
|
||||
chosen = Some(turned(s.width, s.height));
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
chosen
|
||||
});
|
||||
ui.spacing_mut().item_spacing.x = 2.0;
|
||||
for (is_landscape, tip) in [(false, tl!("Portrait")), (true, tl!("Landscape"))] {
|
||||
if widgets::orientation_button(ui, is_landscape, landscape == is_landscape, tip) && landscape != is_landscape {
|
||||
size = Some((h, w));
|
||||
}
|
||||
}
|
||||
});
|
||||
if let Some((width, height)) = size {
|
||||
app.run("artboard.setProps", json!({"index": i, "width": width, "height": height})).ok();
|
||||
}
|
||||
}
|
||||
|
||||
fn document_sections(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
let units = app.session.general_unit();
|
||||
section_header(ui, tl!("Document"));
|
||||
@@ -520,4 +571,41 @@ mod tests {
|
||||
click(&mut app, &ctx, &texts, "Exit");
|
||||
assert_eq!(app.session.tool_id(), "selection");
|
||||
}
|
||||
|
||||
/// #671: Edit Artboards shows the artboard's preset (Custom when it matches none) and its
|
||||
/// orientation; the other orientation swaps its width and height, its corner staying put.
|
||||
#[test]
|
||||
fn edit_artboards_shows_the_preset_and_flips_the_orientation() {
|
||||
let mut app = VectorcraftApp::new(Session::new(), Default::default());
|
||||
app.run("file.new", json!({"preset": "Letter"})).unwrap();
|
||||
app.run("artboard.setProps", json!({"index": 0, "x": 10, "y": 20})).unwrap();
|
||||
app.select_tool("artboard");
|
||||
let ctx = egui::Context::default();
|
||||
let texts = frame(&mut app, &ctx, vec![]);
|
||||
assert!(texts.iter().any(|(t, _)| t == "Preset:") && texts.iter().any(|(t, _)| t == "Letter"), "{texts:?}");
|
||||
// The row's clickable widgets, left to right: the preset combo, Portrait, Landscape.
|
||||
let row = texts.iter().find(|(t, _)| t == "Preset:").map(|(_, r)| *r).unwrap();
|
||||
let buttons: Vec<Rect> = ctx.viewport(|vp| {
|
||||
let mut r: Vec<Rect> = vp
|
||||
.prev_pass
|
||||
.widgets
|
||||
.layers()
|
||||
.flat_map(|(_, w)| w.iter())
|
||||
.filter(|w| w.sense.senses_click() && w.rect.y_range().contains(row.center().y) && w.rect.left() > row.right())
|
||||
.map(|w| w.rect)
|
||||
.collect();
|
||||
r.sort_by(|a, b| a.left().total_cmp(&b.left()));
|
||||
r
|
||||
});
|
||||
let landscape = buttons.last().unwrap().center();
|
||||
let press = |pressed| Event::PointerButton { pos: landscape, button: egui::PointerButton::Primary, pressed, modifiers: Default::default() };
|
||||
frame(&mut app, &ctx, vec![Event::PointerMoved(landscape), press(true)]);
|
||||
frame(&mut app, &ctx, vec![press(false)]);
|
||||
let rect = app.session.active().unwrap().doc.artboards[0].rect;
|
||||
assert_eq!((rect.x0, rect.y0, rect.width(), rect.height()), (10.0, 20.0, 792.0, 612.0), "landscape, same corner");
|
||||
let texts = frame(&mut app, &ctx, vec![]);
|
||||
assert!(texts.iter().any(|(t, _)| t == "Letter"), "still Letter, turned: {texts:?}");
|
||||
app.run("artboard.setProps", json!({"index": 0, "width": 333})).unwrap();
|
||||
assert!(frame(&mut app, &ctx, vec![]).iter().any(|(t, _)| t == "Custom"));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user