mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-13 01:29:04 +00:00
Scripts: File › Scripts lists the Scripts folder, subfolders as submenus
File › Scripts now lists the .js and .jsx files of the Scripts folder next to the preferences, each named after its file without the extension, with each subfolder as a submenu, above Check Script… and Other Script…. The help page describes this listing: https://helpx.adobe.com/illustrator/desktop/automate-visualize-data/automate-actions/install-and-run-scripts.html Choosing a listed script runs it through script.run without the warning Other Script… shows. script.listInstalled returns the same listing, and ui.runInstalledScript {path} runs one entry. VectorCraft's own rules: the folder is read off the UI thread when the app starts, when its window becomes active and when the File menu of the window's menu bar opens; only names are read; links, junctions, hidden and system entries are left out and checked again before a run; one read stops at 2,048 entries, 512 scripts, 128 subfolders, 4 levels or 250 ms and the menu then notes that some scripts were not listed. Under an automation read root, script.listInstalled and ui.menu.list leave out a folder outside the root. #1183 (LloydNicholson) also proposed a Scripts menu listing a folder.
This commit is contained in:
@@ -45,6 +45,15 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
always,
|
||||
stop
|
||||
),
|
||||
cmd!(
|
||||
query "script.listInstalled",
|
||||
"List Installed Scripts",
|
||||
[],
|
||||
None,
|
||||
"{} list the .js and .jsx files of the Scripts folder next to the settings, which File › Scripts lists, each subfolder as a submenu, in menu order; only names are read, never a script's text; links, hidden files, names starting with a dot and folders without a script are left out. VectorCraft's own limits: 2048 folder entries looked at, 512 scripts, 128 subfolders, 4 levels of subfolders, names of 255 bytes, labels of 128 characters, 250 ms → {folder: the Scripts folder's path | null (a session without one, and the web), scripts: [{path, label: the file name without its extension (the whole file name when another row of its folder has that label), folders: [the labels of the subfolders it is in]}], truncated: a limit was reached or a subfolder could not be read, unreadable: the folder is there and could not be read, or is a link}",
|
||||
always,
|
||||
list_installed
|
||||
),
|
||||
cmd!(
|
||||
query "script.check",
|
||||
"Check Script",
|
||||
@@ -146,6 +155,52 @@ fn stop(s: &mut Session, _: &Value) -> Result<Value> {
|
||||
Ok(json!({"stopped": s.script_request_stop()}))
|
||||
}
|
||||
|
||||
/// The error for a script that File › Scripts listed and that [`script::folder::read`] no longer
|
||||
/// reads.
|
||||
pub const NOT_LISTED: &str = "the script is no longer in the Scripts folder";
|
||||
|
||||
impl Session {
|
||||
/// Have File › Scripts and `script.listInstalled` list the scripts of the folder `dir`. The
|
||||
/// folder is only read, and never made.
|
||||
pub fn attach_scripts_dir(&mut self, dir: std::path::PathBuf) {
|
||||
self.scripts_dir = Some(dir);
|
||||
}
|
||||
|
||||
/// The Scripts folder ([`Session::attach_scripts_dir`]); none on the web, which has no folders.
|
||||
pub fn scripts_dir(&self) -> Option<&std::path::Path> {
|
||||
self.scripts_dir.as_deref().filter(|_| !cfg!(target_arch = "wasm32"))
|
||||
}
|
||||
|
||||
/// The text of the script at `path`, which a listing of the Scripts folder gave
|
||||
/// ([`script::folder::read`]), checked as `script.run` checks a file's.
|
||||
pub fn installed_script(&self, path: &str) -> Result<String> {
|
||||
let root = self.scripts_dir().ok_or_else(|| EngineError::Other(NOT_LISTED.to_string()))?;
|
||||
crate::file_access::check_read(path).map_err(EngineError::Other)?;
|
||||
// One byte more than a script may have shows that the file is too large.
|
||||
let bytes = script::folder::read(root, std::path::Path::new(path), script::MAX_SOURCE_BYTES + 1)
|
||||
.ok_or_else(|| EngineError::Other(NOT_LISTED.to_string()))?;
|
||||
if bytes.len() > script::MAX_SOURCE_BYTES {
|
||||
return Err(EngineError::Other(TOO_LARGE.to_string()));
|
||||
}
|
||||
script_text(&bytes)
|
||||
}
|
||||
}
|
||||
|
||||
fn list_installed(s: &mut Session, _: &Value) -> Result<Value> {
|
||||
let Some(root) = s.scripts_dir() else {
|
||||
return Ok(json!({"folder": null, "scripts": [], "truncated": false, "unreadable": false}));
|
||||
};
|
||||
let folder = root.to_string_lossy();
|
||||
crate::file_access::check_read(&folder).map_err(EngineError::Other)?;
|
||||
let listing = script::folder::list(root);
|
||||
let scripts: Vec<Value> = listing
|
||||
.scripts()
|
||||
.into_iter()
|
||||
.map(|(folders, label, path)| json!({"path": path.to_string_lossy(), "label": label, "folders": folders}))
|
||||
.collect();
|
||||
Ok(json!({"folder": folder, "scripts": scripts, "truncated": listing.truncated, "unreadable": listing.unreadable}))
|
||||
}
|
||||
|
||||
/// The folder a script is in, which its included files are read from.
|
||||
struct Folder {
|
||||
dir: std::path::PathBuf,
|
||||
|
||||
Reference in New Issue
Block a user