Add SaraSortd integration: show incoming/outgoing sort rules for the browsed directory

This commit is contained in:
sarah committed 2026-09-26 18:11:18 +02:00
1 parent 31b6c554e6
commit d588d445b4
4 files changed
+436 -9

No files matched your search

+75
View File
@@ -155,6 +155,16 @@ class SaraTagIntegrationConfig:
)
@dataclass
class SaraSortdIntegrationConfig:
"""Points at a SaraSortd checkout's GlobalConf.toml (read-only — SaraFem
never writes to it). Disabled until pointed at one, e.g.
GlobalConfPath = "/home/sarah/Applications/SaraSortd/GlobalConf.toml"."""
Enabled: bool = False
GlobalConfPath: str = ""
@dataclass
class AppConfig:
"""Persisted user settings. Defaults match SaraFem's existing behaviour
@@ -192,6 +202,9 @@ class AppConfig:
# Grid view.
GridIconSize: int = 64
GridNameBelowIcon: bool = True
ListIconSize: int = 32
FontSize: int = 14 # entry name text, list and grid alike; metadata/header text scales with it
ToolbarHamburger: bool = False # collapse the toolbar's buttons into one overflow menu
# Batch-rename pattern for multi-select rename, syntax modeled on
# SaraSortd's own placeholder characters (see ~/Applications/SaraSortd's
@@ -257,6 +270,7 @@ class AppConfig:
# MaxHeight when on, since a fixed height and an even flex split conflict.
SplitPreviewTagsEvenly: bool = False
SaraTag: SaraTagIntegrationConfig = field(default_factory=SaraTagIntegrationConfig)
SaraSortd: SaraSortdIntegrationConfig = field(default_factory=SaraSortdIntegrationConfig)
def Save(self) -> None:
ConfigDir.mkdir(parents=True, exist_ok=True)
@@ -285,6 +299,11 @@ def LoadConfig() -> AppConfig:
SaraTagFiltered = {Key: Value for Key, Value in SaraTagData.items() if Key in KnownSaraTagFields}
Filtered["SaraTag"] = SaraTagIntegrationConfig(**SaraTagFiltered)
SaraSortdData = Filtered.pop("SaraSortd", {})
KnownSaraSortdFields = {Field for Field in SaraSortdIntegrationConfig.__dataclass_fields__}
SaraSortdFiltered = {Key: Value for Key, Value in SaraSortdData.items() if Key in KnownSaraSortdFields}
Filtered["SaraSortd"] = SaraSortdIntegrationConfig(**SaraSortdFiltered)
return AppConfig(**Filtered)
@@ -330,6 +349,62 @@ def EffectiveSettings(Config: AppConfig) -> SharedSettings:
return LoadSharedSettings(Config) if Config.UseSharedSettings else _OwnSettings(Config)
def ExportSettings(Config: AppConfig, DestPath: Path) -> None:
"""One portable file bundling both the cross-program shared settings
(Theme/Font/TitleBar) and SaraFem's own, under separate top-level
sections (Features/Major: "Exporting and importing settings as one
file... applies to every other SaraSuite program"). A future SaraView/
SaraTag/etc. import would read the same [SaraSuite] section plus its
own top-level one, ignoring [SaraFem] — the same convention
ImportSettings below uses to ignore sections that aren't its own."""
Data = {"SaraSuite": asdict(_OwnSettings(Config)), "SaraFem": asdict(Config)}
DestPath.write_text(toml.dumps(Data))
def ImportSettings(Config: AppConfig, SrcPath: Path) -> None:
"""Applies a [SaraSuite] section (if present) to the shared settings
file and a [SaraFem] section (if present) onto Config in place, field
by field — a field the import doesn't mention is left as it was, rather
than reset to default, and unknown sections/fields (another program's,
or a newer SaraFem version's) are silently skipped: the "should stack
fine with others if installed or not... one file works anywhere on the
same version no matter what you have installed" compatibility the
feature asked for. Raises toml.TomlDecodeError on an unreadable file —
callers show that to the user rather than silently doing nothing."""
Data = toml.loads(SrcPath.read_text())
SharedData = Data.get("SaraSuite")
if isinstance(SharedData, dict):
KnownSharedFields = {F for F in SharedSettings.__dataclass_fields__}
Filtered = {K: V for K, V in SharedData.items() if K in KnownSharedFields}
Merged = asdict(_OwnSettings(Config))
Merged.update(Filtered)
SharedSettings(**Merged).Save()
OwnData = Data.get("SaraFem")
if isinstance(OwnData, dict):
KnownFields = {F for F in AppConfig.__dataclass_fields__}
Filtered = {K: V for K, V in OwnData.items() if K in KnownFields}
SaraTagData = Filtered.pop("SaraTag", None)
if isinstance(SaraTagData, dict):
KnownSaraTagFields = {F for F in SaraTagIntegrationConfig.__dataclass_fields__}
SaraTagFiltered = {K: V for K, V in SaraTagData.items() if K in KnownSaraTagFields}
for Key, Value in SaraTagFiltered.items():
setattr(Config.SaraTag, Key, Value)
SaraSortdData = Filtered.pop("SaraSortd", None)
if isinstance(SaraSortdData, dict):
KnownSaraSortdFields = {F for F in SaraSortdIntegrationConfig.__dataclass_fields__}
SaraSortdFiltered = {K: V for K, V in SaraSortdData.items() if K in KnownSaraSortdFields}
for Key, Value in SaraSortdFiltered.items():
setattr(Config.SaraSortd, Key, Value)
for Key, Value in Filtered.items():
setattr(Config, Key, Value)
Config.Save()
def ApplyAppearance(Settings: SharedSettings) -> None:
"""Theme colours and font. Must run before anything reads Palette.X or
calls BuildTheme(). The window title bar is applied by Main."""