Export for Screens: a leading dot in an artboard name is a dash

A name like .draft would otherwise write a hidden file on Unix-like systems.
This commit is contained in:
SBaksa
2026-11-09 02:03:08 +07:00
committed by bflatastic
parent 0b3ea7c1d2
commit d329dd05e1
2 changed files with 13 additions and 7 deletions
+8 -3
View File
@@ -31,12 +31,17 @@ fn unique_file_names<'a>(names: impl IntoIterator<Item = (&'a str, String)>) ->
/// [`unique_file_names`] keeping `.` in a name (an artboard called `example.artboard` exports as
/// `example.artboard.png`, #6143). A name of only dots (`.` and `..` name other folders) takes the
/// fallback; the caller still runs each stem through `safe_file_name`, which drops trailing periods
/// and guards Windows device names.
/// fallback, and leading dots are dashes (`.draft` would be a hidden file); the caller still runs
/// each stem through `safe_file_name`, which drops trailing periods and guards Windows device names.
pub(crate) fn unique_file_names_keeping_dots<'a>(names: impl IntoIterator<Item = (&'a str, String)>) -> Vec<String> {
distinct_names(names.into_iter().map(|(name, fallback)| {
let base: String = name.chars().map(|c| if c.is_alphanumeric() || matches!(c, '-' | '_' | '.') { c } else { '-' }).collect();
if base.trim_matches('.').is_empty() { fallback } else { base }
if base.trim_matches('.').is_empty() {
return fallback;
}
// A leading dot would hide the file or folder on Unix-like systems: it is a dash, as before.
let rest = base.trim_start_matches('.');
format!("{}{rest}", "-".repeat(base.len() - rest.len()))
}))
}
@@ -149,15 +149,16 @@ fn device_names_and_trailing_periods_follow_windows_naming_rules() {
}
/// A dot in an artboard's name stays a dot (`example.artboard.png`, #6143); a name of only dots, or
/// ending in one, still can't name another folder or end in a period, and `/` and `:` are dashes.
/// ending in one, still can't name another folder or end in a period, a leading dot is a dash (no
/// hidden `.draft.png`), and `/` and `:` are dashes.
#[test]
fn an_artboard_name_keeps_its_dots() {
let mut s = session(5);
for (index, name) in ["example.artboard", "v1.2.final", "..", "a/b:c", "trail."].iter().enumerate() {
let mut s = session(7);
for (index, name) in ["example.artboard", "v1.2.final", "..", "a/b:c", "trail.", ".draft", "..x.y"].iter().enumerate() {
s.execute("artboard.setProps", &json!({"index": index, "name": name})).unwrap();
}
let r = export(&mut s, json!({"formats": [{"format": "png"}]}));
assert_eq!(names(&r), ["example.artboard.png", "v1.2.final.png", "Artboard-3.png", "a-b-c.png", "trail.png"]);
assert_eq!(names(&r), ["example.artboard.png", "v1.2.final.png", "Artboard-3.png", "a-b-c.png", "trail.png", "-draft.png", "--x.y.png"]);
}
/// `first_file` names the file the export writes first, for the same params: the item's file name,