mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-28 19:57:05 -06:00
Select: saved selections checked when a file opens, names capped, docs (review of #318)
A file's saved selections are untrusted: on load at most 25 are kept, names are trimmed, capped at 255 characters and unique, and ids the document doesn't have are dropped (after fix_next_id such an id would select whatever object is drawn next). Save and Edit Selection cap names the same way. The limit, the name cleanup and the default "Selection N" live on SavedSelection, shared by the engine, the Save Selection dialog and the Select menu's slot table. docs/mcp.md documents the select.save / savedList / recall / editSaved commands; ROADMAP counts 13 stubbed menu items and names what's still missing (Update Selection, the Layers panel menu). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
87d1acd589
commit
249b34fc7c
@@ -93,7 +93,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"Save Selection…",
|
||||
["Select"],
|
||||
None,
|
||||
"{name?} save the current selection under a name, in the document (default \"Selection N\"; an existing name is replaced; at most 25) → {name}",
|
||||
"{name?} save the current selection under a name, in the document (default \"Selection N\"; an existing name is replaced; at most 25, names up to 255 characters) → {name}",
|
||||
has_selection,
|
||||
save_selection
|
||||
),
|
||||
@@ -273,21 +273,15 @@ fn direction_handles(s: &mut Session, _: &Value) -> Result<Value> {
|
||||
Ok(json!({ "anchors": total }))
|
||||
}
|
||||
|
||||
/// Illustrator-style saved selections per document, as many as the Select menu lists.
|
||||
pub const MAX_SAVED_SELECTIONS: usize = 25;
|
||||
|
||||
fn save_selection(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
const C: &str = "select.save";
|
||||
let st = s.doc()?;
|
||||
let ids = st.selection.objects.clone();
|
||||
let saved = &st.doc.saved_selections;
|
||||
let name = match str_param(p, "name").map(str::trim).filter(|n| !n.is_empty()) {
|
||||
Some(n) => n.to_string(),
|
||||
None => (1..).map(|i| format!("Selection {i}")).find(|n| !saved.iter().any(|x| &x.name == n)).unwrap_or_default(),
|
||||
};
|
||||
let name = str_param(p, "name").and_then(SavedSelection::clean_name).unwrap_or_else(|| SavedSelection::default_name(saved));
|
||||
let existing = saved.iter().position(|x| x.name == name);
|
||||
if existing.is_none() && saved.len() >= MAX_SAVED_SELECTIONS {
|
||||
return Err(bad(C, format!("a document keeps at most {MAX_SAVED_SELECTIONS} saved selections")));
|
||||
if existing.is_none() && saved.len() >= SavedSelection::MAX {
|
||||
return Err(bad(C, format!("a document keeps at most {} saved selections", SavedSelection::MAX)));
|
||||
}
|
||||
s.edit("Save Selection", |d, _| {
|
||||
let entry = SavedSelection { name: name.clone(), objects: ids };
|
||||
@@ -335,10 +329,10 @@ fn edit_saved(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let Some(slot) = after.get_mut(i) else { continue };
|
||||
if bool_or(e, "delete", false) {
|
||||
*slot = None;
|
||||
} else if let Some(n) = str_param(e, "newName").map(str::trim).filter(|n| !n.is_empty())
|
||||
} else if let Some(n) = str_param(e, "newName").and_then(SavedSelection::clean_name)
|
||||
&& let Some(x) = slot
|
||||
{
|
||||
x.name = n.to_string();
|
||||
x.name = n;
|
||||
}
|
||||
}
|
||||
let after: Vec<SavedSelection> = after.into_iter().flatten().collect();
|
||||
|
||||
@@ -15,7 +15,6 @@ mod cut;
|
||||
pub(crate) mod distortcmds;
|
||||
mod docinfo;
|
||||
mod docmenu;
|
||||
pub use docmenu::MAX_SAVED_SELECTIONS;
|
||||
pub(crate) mod docsetup;
|
||||
mod draw2;
|
||||
mod edit;
|
||||
|
||||
Reference in New Issue
Block a user