mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-26 02:37:01 -06:00
G6: graph variables (CSV data merge)
VariableKind::Graph binds a graph root; dataset rows supply CSV as graph.setData. Make Graph Dynamic, Variables dialog, capture/apply, and pt-br for the new strings. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
committed by
bflatastic
co-authored by
Cursor
parent
799f94ecf1
commit
dc390aba37
@@ -1,11 +1,12 @@
|
||||
//! Variables (data merge): named values bound to objects, applied per dataset.
|
||||
//!
|
||||
//! One template document produces many variants: bind a type object to a `text`
|
||||
//! variable, anything to a `visibility` variable, and a linked image to an `image`
|
||||
//! variable, put a row of values in each dataset, and `dataset.select` swaps the
|
||||
//! whole document to that row in one undo step. `dataset.import` / `dataset.export`
|
||||
//! move those rows as CSV (header: `name` then variable names). Agents reach every
|
||||
//! command through `run_command`, like all engine commands.
|
||||
//! variable, anything to a `visibility` variable, a linked image to an `image`
|
||||
//! variable, or a graph root to a `graph` variable (CSV data), put a row of values in
|
||||
//! each dataset, and `dataset.select` swaps the whole document to that row in one undo
|
||||
//! step. `dataset.import` / `dataset.export` move those rows as CSV (header: `name`
|
||||
//! then variable names). Agents reach every command through `run_command`, like all
|
||||
//! engine commands.
|
||||
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
@@ -19,6 +20,8 @@ use super::*;
|
||||
|
||||
/// The longest variable or dataset name, in characters (names arrive from MCP and files).
|
||||
const MAX_NAME: usize = 255;
|
||||
/// Cap on a graph variable's CSV (MCP and files are untrusted).
|
||||
const MAX_GRAPH_CSV: usize = 256 * 1024;
|
||||
|
||||
/// Caps on a datasets CSV (paths and MCP payloads are untrusted).
|
||||
const MAX_CSV_BYTES: usize = 1_048_576;
|
||||
@@ -71,6 +74,9 @@ fn check_bindable(doc: &vectorcraft_doc::Document, ids: &[NodeId], kind: Variabl
|
||||
Some(n) if kind == VariableKind::Image && !matches!(n.kind, NodeKind::Image(_)) => {
|
||||
return Err(bad(cmd, format!("node {} is not an image", id.0)));
|
||||
}
|
||||
Some(n) if kind == VariableKind::Graph && n.extras().graph.is_none() => {
|
||||
return Err(bad(cmd, format!("node {} is not a graph", id.0)));
|
||||
}
|
||||
Some(_) => {}
|
||||
}
|
||||
}
|
||||
@@ -97,6 +103,11 @@ fn dataset_value(cmd: &str, var: &str, kind: VariableKind, v: &Value) -> Result<
|
||||
VariableKind::Visibility => v.as_bool().map(DataValue::Visible),
|
||||
// An image value is a path string; empty is not a file to open.
|
||||
VariableKind::Image => v.as_str().map(str::trim).filter(|t| !t.is_empty()).map(|t| DataValue::Image(t.to_string())),
|
||||
VariableKind::Graph => v
|
||||
.as_str()
|
||||
.map(str::trim)
|
||||
.filter(|t| !t.is_empty())
|
||||
.and_then(|t| if t.len() > MAX_GRAPH_CSV { None } else { Some(DataValue::Graph(t.to_string())) }),
|
||||
}
|
||||
.ok_or_else(|| bad(cmd, format!("value for `{var}` must match its variable kind")))
|
||||
}
|
||||
@@ -118,7 +129,8 @@ fn values_param(vars: &Variables, p: &Value, cmd: &str) -> Result<BTreeMap<Strin
|
||||
fn define(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
const C: &str = "variable.define";
|
||||
let name = var_name(p, C)?;
|
||||
let kind = str_param(p, "kind").and_then(VariableKind::parse).ok_or_else(|| bad(C, "`kind` must be `text`, `visibility` or `image`"))?;
|
||||
let kind =
|
||||
str_param(p, "kind").and_then(VariableKind::parse).ok_or_else(|| bad(C, "`kind` must be `text`, `visibility`, `image` or `graph`"))?;
|
||||
if s.doc()?.doc.variables.variable(&name).is_some() {
|
||||
return Err(bad(C, format!("variable `{name}` is already defined")));
|
||||
}
|
||||
@@ -161,7 +173,7 @@ fn rename(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
Ok(json!({"from": from, "name": to}))
|
||||
}
|
||||
|
||||
/// Make Text / Visibility / Image Dynamic: define a variable of that kind and bind the
|
||||
/// Make Text / Visibility / Image / Graph Dynamic: define a variable of that kind and bind the
|
||||
/// selection to it in one step, naming it after what it drives — the panel's buttons.
|
||||
fn make_dynamic(s: &mut Session, kind: VariableKind, stem: &str) -> Result<Value> {
|
||||
// Errors name the command that ran: `variable.make{stem}Dynamic`.
|
||||
@@ -479,6 +491,7 @@ fn apply_dataset(s: &mut Session, name: &str) -> Result<Value> {
|
||||
}),
|
||||
// Swap the linked file; keep the object's bounds. A missing path skips.
|
||||
(VariableKind::Image, DataValue::Image(path)) => links::set_image_from_path(d, id, path).is_ok(),
|
||||
(VariableKind::Graph, DataValue::Graph(csv)) => super::graph::apply_csv(d, id, csv).is_ok(),
|
||||
_ => false,
|
||||
};
|
||||
if done {
|
||||
@@ -740,7 +753,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"New Variable…",
|
||||
["Window", "Variables"],
|
||||
None,
|
||||
"{name, kind: text|visibility|image} define a variable",
|
||||
"{name, kind: text|visibility|image|graph} define a variable (graph: CSV data on a graph root)",
|
||||
has_doc,
|
||||
define
|
||||
),
|
||||
@@ -790,12 +803,21 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
has_selection,
|
||||
|s, _| make_dynamic(s, VariableKind::Image, "Image")
|
||||
),
|
||||
cmd!(
|
||||
"variable.makeGraphDynamic",
|
||||
"Make Graph Dynamic",
|
||||
["Window", "Variables"],
|
||||
None,
|
||||
"{} define a graph variable over the selected graph root(s), named after it and bound to it; each dataset row supplies CSV as graph.setData",
|
||||
has_selection,
|
||||
|s, _| make_dynamic(s, VariableKind::Graph, "Graph")
|
||||
),
|
||||
cmd!(
|
||||
"variable.bind",
|
||||
"Bind Variable",
|
||||
["Window", "Variables"],
|
||||
None,
|
||||
"{variable, ids?} bind objects (default: the selection) to a variable; text needs type, image needs an image object, locked art is refused. An object may hold several variables, and binding it to one it already has changes nothing → {bound, already}",
|
||||
"{variable, ids?} bind objects (default: the selection) to a variable; text needs type, image needs an image object, graph needs a graph root, locked art is refused. An object may hold several variables, and binding it to one it already has changes nothing → {bound, already}",
|
||||
has_doc,
|
||||
bind
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user