Commands: path.create with path data refuses closed given as text, and the grids refuse a count

path.create read closed only when it drew from anchors. Given path
data, {"d": "M0 0 L10 0 L10 10", "closed": "true"} drew the path and
recorded an undo step, while the same closed with anchors fails with
"`closed` must be true or false". shape.rectangularGrid and
shape.polarGrid read rows, columns, concentric and radial given as text
as the count left out, so {"rows": "3"} drew the default 5 horizontal
dividers instead of 3.

path.create now checks closed before it reads d or anchors, and the
grid commands refuse such a count with "`rows` must be a number, not
"3"". Neither command changes the document or its undo history then.
With d, a closed of true, false or null still leaves the path as its
path data draws it.

Docs: docs/mcp.md, docs/target-app-parity.md.
This commit is contained in:
dotCooCoo
2026-10-22 08:24:09 +07:00
committed by bflatastic
parent aef00190ed
commit bfc3f5251a
5 changed files with 34 additions and 8 deletions
+7 -3
View File
@@ -428,9 +428,11 @@ fn grid_group(s: &mut Session, label: &str, paths: Vec<PathData>, fill: bool) ->
Ok(json!({ "id": id.0 }))
}
/// A grid's divider count `count` with its skew `key` in percent (0 when absent, clamped to
/// ±[`shapes::GRID_SKEW_MAX`]).
/// A grid's divider count `count` (an error unless it is a number or null; 5 unless it is a JSON
/// integer of 0 or more, so 4.0 and 2.5 give 5; at most 999) with its skew `key` in percent (0 when
/// absent, clamped to ±[`shapes::GRID_SKEW_MAX`]).
fn dividers(p: &Value, count: &str, key: &str, cmd: &str) -> Result<(u32, f64)> {
check_numbers(p, &[count], cmd)?;
let n = p.get(count).and_then(Value::as_u64).unwrap_or(5).min(999) as u32;
let skew = match p.get(key) {
None | Some(Value::Null) => 0.0,
@@ -488,6 +490,8 @@ pub(crate) fn anchor_from_json(v: &Value, cmd: &str) -> Result<Anchor> {
}
fn path_create(s: &mut Session, p: &Value) -> Result<Value> {
// Checked with `d` too, where the path data alone decides whether the path is closed.
let closed = switch_or(p, "closed", false, "path.create")?;
let path = if let Some(d) = str_param(p, "d") {
let bp = vectorcraft_geom::BezPath::from_svg(d).map_err(|e| bad("path.create", format!("bad path data: {e}")))?;
PathData::from_bezpath(&bp)
@@ -502,7 +506,7 @@ fn path_create(s: &mut Session, p: &Value) -> Result<Value> {
if anchors.is_empty() {
return Err(bad("path.create", "need at least one anchor"));
}
PathData::single(SubPath::new(anchors, switch_or(p, "closed", false, "path.create")?))
PathData::single(SubPath::new(anchors, closed))
};
let mut look = s.new_art_look(s.paint.fill.clone(), s.paint.stroke.clone(), s.paint.stroke_width);
// Open paths drawn with a stroke of None would be invisible: mimic Illustrator and keep what the user set.