mirror of
https://github.com/storytold/figcraft.git
synced 2026-10-31 05:46:46 -06:00
Plugins can hand the host a file name, MIME type and bytes through the new `save_file` import (plugin API 4, `permissions.saveFile`). The host asks the user where to save (browser download on the web), so the plugin never sees a path. Includes the SDK wrapper and the `export-names` example plugin. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Assisted-by: Claude (Anthropic)
56 lines
1.9 KiB
Rust
56 lines
1.9 KiB
Rust
//! Export names: saves the names of the selected layers (optionally the layers inside them too) as
|
|
//! the text file `layer-names.txt`, through the host's own Save dialog (`save_file`).
|
|
|
|
use figcraft_plugin_sdk::{Input, Result, Value, call, entry, json, notify, save_file};
|
|
|
|
/// Layers looked at in one run, at most.
|
|
const MAX_LAYERS: usize = 10_000;
|
|
|
|
/// The names as the file's text: one per line, a line break at the end, line breaks inside a name
|
|
/// turned into spaces.
|
|
fn names_text(names: &[String]) -> String {
|
|
let mut text = String::new();
|
|
for name in names {
|
|
text.extend(name.chars().map(|c| if c == '\n' || c == '\r' { ' ' } else { c }));
|
|
text.push('\n');
|
|
}
|
|
text
|
|
}
|
|
|
|
fn run(input: Input) -> Result<Value> {
|
|
if input.selection.is_empty() {
|
|
return Err("Select the layers to export first".into());
|
|
}
|
|
let inside = input.bool("children").unwrap_or(true);
|
|
// Depth first, in layer order: the stack takes the children reversed.
|
|
let mut stack: Vec<u64> = input.selection.iter().rev().copied().collect();
|
|
let mut names = Vec::new();
|
|
while let Some(id) = stack.pop() {
|
|
if names.len() >= MAX_LAYERS {
|
|
break;
|
|
}
|
|
let node = call("node.get", &json!({"id": id}))?;
|
|
names.push(node["name"].as_str().unwrap_or_default().to_string());
|
|
if inside && let Some(children) = node["children"].as_array() {
|
|
stack.extend(children.iter().rev().filter_map(|c| c["id"].as_u64()));
|
|
}
|
|
}
|
|
save_file("layer-names.txt", "text/plain", names_text(&names).as_bytes())?;
|
|
notify(&format!("Exporting {} names", names.len()));
|
|
Ok(json!({"names": names.len()}))
|
|
}
|
|
|
|
entry!(run);
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::names_text;
|
|
|
|
#[test]
|
|
fn names_become_lines() {
|
|
let names = ["Card".to_string(), "Title\nline".to_string(), String::new()];
|
|
assert_eq!(names_text(&names), "Card\nTitle line\n\n");
|
|
assert_eq!(names_text(&[]), "");
|
|
}
|
|
}
|