Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6a39ee0a1c | ||
|
|
dcc0de7487 | ||
|
|
0924bc16ae | ||
|
|
f18cd1c99b |
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
|
||||
|
||||
|
||||
@@ -261,7 +261,19 @@ pub const PREF_SPECS: &[PrefSpec] = &[
|
||||
"Brightness",
|
||||
choice(&[("dark", "Dark"), ("mediumDark", "Medium Dark"), ("mediumLight", "Medium Light"), ("light", "Light")])
|
||||
),
|
||||
p!("canvasColor", "User Interface", "", "Canvas Color", choice(&[("matchUi", "Match User Interface Brightness"), ("white", "White")])),
|
||||
p!(
|
||||
"canvasColor",
|
||||
"User Interface",
|
||||
"",
|
||||
"Canvas Color",
|
||||
choice(&[
|
||||
("matchUi", "Match User Interface Brightness"),
|
||||
("white", "White"),
|
||||
("lightGray", "Light Gray"),
|
||||
("mediumGray", "Medium Gray"),
|
||||
("darkGray", "Dark Gray")
|
||||
])
|
||||
),
|
||||
p!("autoCollapseIconPanels", "User Interface", "", "Auto-Collapse Iconic Panels", bool),
|
||||
p!("openDocumentsAsTabs", "User Interface", "", "Open Documents As Tabs", bool),
|
||||
p!("largeTabs", "User Interface", "", "Large Tabs", bool),
|
||||
|
||||
@@ -319,6 +319,10 @@ fn tab_title(d: &vectorcraft_engine::DocState, zoom: f64, outline: bool) -> Stri
|
||||
format!("{}{} @ {} ({mode})", d.title(), if d.is_dirty() { "*" } else { "" }, zoom_label(zoom).replace('%', " %"))
|
||||
}
|
||||
|
||||
/// Whether a document tab's × comes before its title (macOS) rather than after it (Windows,
|
||||
/// Linux and the web).
|
||||
const CLOSE_BEFORE_TITLE: bool = cfg!(target_os = "macos");
|
||||
|
||||
/// Document tab strip: "Name* @ 66.67% (RGB/Preview)". User Interface › Large Tabs makes the tabs
|
||||
/// taller, with larger titles.
|
||||
pub fn doc_tabs(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
@@ -346,11 +350,13 @@ pub fn doc_tabs(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
ui.painter().rect_filled(r, 0.0, t.hover.gamma_multiply(0.4));
|
||||
}
|
||||
ui.painter().line_segment([r.right_top(), r.right_bottom()], Stroke::new(1.5, t.border));
|
||||
// × at the left like Illustrator.
|
||||
let xr = egui::Rect::from_center_size(egui::pos2(r.left() + 16.0, r.center().y), vec2(12.0, 12.0));
|
||||
// The × where each platform puts a tab's: before the title on macOS, after it elsewhere
|
||||
// (#673).
|
||||
let (x_center, title_left) = if CLOSE_BEFORE_TITLE { (r.left() + 16.0, r.left() + 32.0) } else { (r.right() - 16.0, r.left() + 14.0) };
|
||||
let xr = egui::Rect::from_center_size(egui::pos2(x_center, r.center().y), vec2(12.0, 12.0));
|
||||
let xresp = ui.interact(xr.expand(3.0), ui.id().with(("tabx", i)), Sense::click());
|
||||
icons::paint(ui, "x", xr, if xresp.hovered() { t.text_strong } else { t.text });
|
||||
ui.painter().galley(egui::pos2(r.left() + 32.0, r.center().y - galley.size().y / 2.0), galley, t.text);
|
||||
ui.painter().galley(egui::pos2(title_left, r.center().y - galley.size().y / 2.0), galley, t.text);
|
||||
if xresp.clicked() {
|
||||
close = Some(i);
|
||||
} else if resp.clicked() {
|
||||
@@ -782,6 +788,44 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
/// A document tab's × sits where the platform puts it (#673): after the title on Windows,
|
||||
/// Linux and the web, before it on macOS; clicking it closes that document.
|
||||
#[test]
|
||||
fn a_tabs_close_box_sits_where_the_platform_puts_it() {
|
||||
let mut app = crate::VectorcraftApp::new(Session::new(), Default::default());
|
||||
app.session.execute("file.new", &json!({"width": 100, "height": 100})).unwrap();
|
||||
app.session.execute("file.new", &json!({"width": 100, "height": 100})).unwrap();
|
||||
let ctx = egui::Context::default();
|
||||
crate::theme::install_fonts(&ctx);
|
||||
let screen = egui::Rect::from_min_size(egui::Pos2::ZERO, egui::vec2(1200.0, 200.0));
|
||||
let frame = |app: &mut crate::VectorcraftApp, events: Vec<egui::Event>| {
|
||||
let mut out = ctx.run_ui(egui::RawInput { screen_rect: Some(screen), events, ..Default::default() }, |ui| super::doc_tabs(app, ui));
|
||||
out.textures_delta.clear();
|
||||
let mut titles = vec![];
|
||||
for c in &out.shapes {
|
||||
// The titles, not the × icons.
|
||||
if let egui::Shape::Text(t) = &c.shape
|
||||
&& t.visual_bounding_rect().width() > 30.0
|
||||
{
|
||||
titles.push(t.visual_bounding_rect());
|
||||
}
|
||||
}
|
||||
titles.sort_by(|a, b| a.left().total_cmp(&b.left()));
|
||||
titles
|
||||
};
|
||||
let titles = frame(&mut app, vec![]);
|
||||
assert_eq!(titles.len(), 2, "{titles:?}");
|
||||
// The first tab's ×: 20 pt past its title's end, or 16 pt before its start.
|
||||
let first = titles[0];
|
||||
let x = if super::CLOSE_BEFORE_TITLE { first.left() - 16.0 } else { first.right() + 20.0 };
|
||||
let at = egui::pos2(x, first.center().y);
|
||||
let press = |pressed| egui::Event::PointerButton { pos: at, button: egui::PointerButton::Primary, pressed, modifiers: Default::default() };
|
||||
frame(&mut app, vec![egui::Event::PointerMoved(at), press(true)]);
|
||||
frame(&mut app, vec![press(false)]);
|
||||
assert_eq!(app.session.documents().len(), 1, "the × closed the first document");
|
||||
assert!(super::CLOSE_BEFORE_TITLE == cfg!(target_os = "macos"));
|
||||
}
|
||||
|
||||
/// One headless frame of the status bar; returns the artboard navigator's buttons, left to
|
||||
/// right: first, previous, next, last.
|
||||
fn navigator(app: &mut crate::VectorcraftApp, ctx: &egui::Context, events: Vec<egui::Event>) -> Vec<egui::Rect> {
|
||||
|
||||
@@ -1400,6 +1400,9 @@
|
||||
Marks and Bleed Marcas y sangrado
|
||||
Marks and Bleeds Marcas y sangrados
|
||||
Match User Interface Brightness Igualar brillo de la interfaz de usuario
|
||||
Light Gray Gris claro
|
||||
Medium Gray Gris medio
|
||||
Dark Gray Gris oscuro
|
||||
Materials… Materiales…
|
||||
Matte Mate
|
||||
Matte: Mate:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -1399,6 +1399,9 @@
|
||||
Marks and Bleed Marques et fond perdu
|
||||
Marks and Bleeds Marques et fonds perdus
|
||||
Match User Interface Brightness Adapter à la luminosité de l’interface
|
||||
Light Gray Gris clair
|
||||
Medium Gray Gris moyen
|
||||
Dark Gray Gris foncé
|
||||
Materials… Matériaux…
|
||||
Matte Détourage
|
||||
Matte: Détourage :
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -1400,6 +1400,9 @@
|
||||
Marks and Bleed Segni e abbondanza
|
||||
Marks and Bleeds Segni e abbondanze
|
||||
Match User Interface Brightness Adatta alla luminosità dell'interfaccia utente
|
||||
Light Gray Grigio chiaro
|
||||
Medium Gray Grigio medio
|
||||
Dark Gray Grigio scuro
|
||||
Materials… Materiali…
|
||||
Matte Colore alone
|
||||
Matte: Colore alone:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -1577,6 +1577,9 @@
|
||||
Mark as trapped トラップ済みとしてマーク
|
||||
Marks トンボ
|
||||
Match User Interface Brightness ユーザーインターフェイスの明るさに合わせる
|
||||
Light Gray ライトグレー
|
||||
Medium Gray ミディアムグレー
|
||||
Dark Gray ダークグレー
|
||||
Matte: マット:
|
||||
Maximize 最大化
|
||||
Maximum Editability 編集機能を最大限に保持
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -1262,6 +1262,9 @@
|
||||
Marker… Marcador…
|
||||
Marks Marcas
|
||||
Match User Interface Brightness Corresponder ao Brilho da Interface do Usuário
|
||||
Light Gray Cinza Claro
|
||||
Medium Gray Cinza Médio
|
||||
Dark Gray Cinza Escuro
|
||||
Materials… Materiais…
|
||||
Matte: Fosco:
|
||||
Maximize Maximizar
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -1398,6 +1398,9 @@
|
||||
Marks and Bleed Метки и вылет
|
||||
Marks and Bleeds Метки и вылеты
|
||||
Match User Interface Brightness Согласовать с яркостью интерфейса
|
||||
Light Gray Светло-серый
|
||||
Medium Gray Средне-серый
|
||||
Dark Gray Тёмно-серый
|
||||
Materials… Материалы…
|
||||
Matte Мат
|
||||
Matte: Мат:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -1382,6 +1382,9 @@
|
||||
Marks and Bleed 标记和出血
|
||||
Marks and Bleeds 标记和出血
|
||||
Match User Interface Brightness 匹配用户界面亮度
|
||||
Light Gray 浅灰色
|
||||
Medium Gray 中灰色
|
||||
Dark Gray 深灰色
|
||||
Materials… 材质…
|
||||
Matte 杂边
|
||||
Matte: 杂边:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -1418,6 +1418,9 @@
|
||||
Marks and Bleed 標記與出血
|
||||
Marks and Bleeds 標記與出血
|
||||
Match User Interface Brightness 符合使用者介面亮度
|
||||
Light Gray 淺灰色
|
||||
Medium Gray 中灰色
|
||||
Dark Gray 深灰色
|
||||
Materials… 材質…
|
||||
Matte 邊緣色
|
||||
Matte: 遮色:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -80,13 +80,26 @@ pub fn restore(app: &mut VectorcraftApp) {
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
struct Applied {
|
||||
brightness: Brightness,
|
||||
white_canvas: bool,
|
||||
/// User Interface › Canvas Color, unless it matches the interface brightness.
|
||||
canvas: Option<egui::Color32>,
|
||||
threads: i32,
|
||||
tool_tips: bool,
|
||||
scrub: bool,
|
||||
bare_points: bool,
|
||||
}
|
||||
|
||||
/// User Interface › Canvas Color: the canvas around the artboards, or `None` to match the
|
||||
/// interface brightness. The greys keep the white page's edge in view (#663).
|
||||
fn canvas_color(key: &str) -> Option<egui::Color32> {
|
||||
match key {
|
||||
"white" => Some(egui::Color32::WHITE),
|
||||
"lightGray" => Some(egui::Color32::from_gray(0xc8)),
|
||||
"mediumGray" => Some(egui::Color32::from_gray(0x96)),
|
||||
"darkGray" => Some(egui::Color32::from_gray(0x5a)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Per frame: push UI-side preferences into egui / the renderer when they change.
|
||||
pub fn apply_runtime(app: &mut VectorcraftApp, ctx: &egui::Context) {
|
||||
let p = &app.session.prefs;
|
||||
@@ -97,7 +110,7 @@ pub fn apply_runtime(app: &mut VectorcraftApp, ctx: &egui::Context) {
|
||||
}
|
||||
let want = Applied {
|
||||
brightness: app.ui.brightness,
|
||||
white_canvas: p.canvas_color == "white",
|
||||
canvas: canvas_color(&p.canvas_color),
|
||||
threads: p.render_threads,
|
||||
tool_tips: p.show_tool_tips,
|
||||
scrub: p.scrub_numeric_fields,
|
||||
@@ -113,9 +126,9 @@ pub fn apply_runtime(app: &mut VectorcraftApp, ctx: &egui::Context) {
|
||||
ctx.global_style_mut(|s| s.interaction.tooltip_delay = delay);
|
||||
crate::scrub::set_enabled(ctx, want.scrub);
|
||||
crate::widgets::set_bare_numbers_are_points(ctx, want.bare_points);
|
||||
if want.white_canvas {
|
||||
if let Some(c) = want.canvas {
|
||||
let mut t = Tokens::get(ctx);
|
||||
t.pasteboard = egui::Color32::WHITE;
|
||||
t.pasteboard = c;
|
||||
ctx.data_mut(|d| d.insert_temp(egui::Id::NULL, t));
|
||||
}
|
||||
if prev.is_some_and(|pr| pr.threads != want.threads) {
|
||||
@@ -365,6 +378,28 @@ mod tests {
|
||||
use crate::Services;
|
||||
use vectorcraft_engine::Session;
|
||||
|
||||
/// User Interface › Canvas Color (#663): White and the three greys paint the canvas around the
|
||||
/// artboards; Match User Interface Brightness keeps the theme's.
|
||||
#[test]
|
||||
fn canvas_color_choices_paint_the_pasteboard() {
|
||||
let mut app = VectorcraftApp::new(Session::new(), Services::default());
|
||||
let ctx = egui::Context::default();
|
||||
apply_runtime(&mut app, &ctx);
|
||||
let themed = Tokens::get(&ctx).pasteboard;
|
||||
for (key, want) in [
|
||||
("white", egui::Color32::WHITE),
|
||||
("lightGray", egui::Color32::from_gray(0xc8)),
|
||||
("mediumGray", egui::Color32::from_gray(0x96)),
|
||||
("darkGray", egui::Color32::from_gray(0x5a)),
|
||||
("matchUi", themed),
|
||||
] {
|
||||
app.run("prefs.set", serde_json::json!({"key": "canvasColor", "value": key})).unwrap();
|
||||
apply_runtime(&mut app, &ctx);
|
||||
assert_eq!(Tokens::get(&ctx).pasteboard, want, "{key}");
|
||||
}
|
||||
assert!(app.run("prefs.set", serde_json::json!({"key": "canvasColor", "value": "pink"})).is_err());
|
||||
}
|
||||
|
||||
fn app() -> VectorcraftApp {
|
||||
VectorcraftApp::new(Session::new(), Services::default())
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user