Merge main into #1340; add Polish to main's Option-based undo/redo wording table, keep main's recounted rows and the Polish row in localization-parity.md

This commit is contained in:
bflatastic
2026-10-30 06:08:35 +07:00
394 changed files with 21282 additions and 1877 deletions
+23
View File
@@ -24,7 +24,9 @@ pub trait ConfigStore: Send + Sync {
}
Ok(text)
}
/// Write entry `name`, replacing any earlier text.
fn write(&self, name: &str, data: &str) -> std::io::Result<()>;
/// Remove entry `name`; a missing entry is no error.
fn remove(&self, name: &str) -> std::io::Result<()>;
/// Names of the entries directly in `dir` (a `/`-separated name prefix such as `Scripts`),
/// sorted. Stores that can't list return nothing.
@@ -32,6 +34,11 @@ pub trait ConfigStore: Send + Sync {
let _ = dir;
vec![]
}
/// Names of the folders directly in `dir`, sorted, like [`ConfigStore::list`].
fn list_dirs(&self, dir: &str) -> Vec<String> {
let _ = dir;
vec![]
}
/// The folder next to the configuration (default Auto-Save folder for untitled projects),
/// in the namespace of [`ConfigStore::files`]; `None` when there is none.
fn dir(&self) -> Option<PathBuf> {
@@ -107,6 +114,15 @@ impl ConfigStore for MemoryConfig {
let Ok(m) = self.files.lock() else { return vec![] };
m.keys().filter_map(|k| k.strip_prefix(&pre)).filter(|n| !n.is_empty() && !n.contains('/')).map(str::to_string).collect()
}
/// The first name part of the keys under `dir/`.
fn list_dirs(&self, dir: &str) -> Vec<String> {
let pre = format!("{}/", dir.trim_end_matches('/'));
let Ok(m) = self.files.lock() else { return vec![] };
let mut v: Vec<String> = m.keys().filter_map(|k| k.strip_prefix(&pre)?.split_once('/')).map(|(d, _)| d.to_string()).filter(|d| !d.is_empty()).collect();
v.sort();
v.dedup();
v
}
}
/// Files in one directory, written atomically.
@@ -153,6 +169,13 @@ impl ConfigStore for DirConfig {
v.sort();
v
}
/// The folders in `dir`, following links like [`ConfigStore::list`] does for files.
fn list_dirs(&self, dir: &str) -> Vec<String> {
let Ok(rd) = std::fs::read_dir(self.dir.join(dir)) else { return vec![] };
let mut v: Vec<String> = rd.flatten().filter(|e| e.path().is_dir()).map(|e| e.file_name().to_string_lossy().to_string()).collect();
v.sort();
v
}
fn remove(&self, name: &str) -> std::io::Result<()> {
match std::fs::remove_file(self.dir.join(name)) {
Err(e) if e.kind() != std::io::ErrorKind::NotFound => Err(e),
@@ -122,6 +122,11 @@ pub fn pages() -> Vec<Page> {
Section("New Project"),
s("project.useTemplate", "New Project Loads Template", B, true),
s("project.templatePath", "Template Project", Kind::Path, true),
Section("Project Files"),
s("project.compactProjectFiles", "Save Compact Project Files", B, true),
Note(
"Saves .ecproj files as compact JSON on one line: smaller and faster to save and open, but hard to read or diff in version control. Off, they are indented, one value per line.",
),
Section("Auto-Save"),
s("autoSave.enabled", "Automatically Save Projects", B, true),
s("autoSave.intervalMinutes", "Save Every", Kind::Int(1, 240, "Minutes"), true),
@@ -62,6 +62,9 @@ page!(ProjectPrefs {
/// Open a template project for File ▸ New ▸ New Project.
use_template: bool = false,
template_path: String = String::new(),
/// Write `.ecproj` files as compact JSON on one line (smaller, faster to save and open). Off:
/// indented, one value per line, so they read and diff cleanly in version control.
compact_project_files: bool = false,
});
page!(AutoSave {