import tomllib from dataclasses import dataclass, field from pathlib import Path from typing import Dict, Any, Optional, Union from src import paths from src.type_aliases import Metadata @dataclass class WorkspaceProfile: name: str ini_content: str show_windows: Dict[str, bool] panel_states: Metadata def to_dict(self) -> Metadata: return { "ini_content": self.ini_content, "show_windows": self.show_windows, "panel_states": self.panel_states, } @classmethod def from_dict(cls, name: str, data: Metadata) -> "WorkspaceProfile": return cls( name = name, ini_content = data.get("ini_content", ""), show_windows = data.get("show_windows", {}), panel_states = data.get("panel_states", {}), ) class WorkspaceManager: """Manages Workspace profiles across global and project-specific files.""" def __init__(self, project_root: Optional[Union[str, Path]] = None): if project_root: self.project_root = Path(project_root).resolve() else: self.project_root = None def _get_path(self, scope: str) -> Path: if scope == "global": return paths.get_global_workspace_profiles_path() elif scope == "project": if not self.project_root: raise ValueError("Project root is not set, cannot resolve project scope.") return paths.get_project_workspace_profiles_path(self.project_root) else: raise ValueError("Invalid scope, must be 'global' or 'project'") def load_all_profiles(self) -> Dict[str, WorkspaceProfile]: """Merges global and project profiles into a single dictionary.""" profiles = {} global_path = paths.get_global_workspace_profiles_path() global_data = self._load_file(global_path) for name, data in global_data.get("profiles", {}).items(): profiles[name] = WorkspaceProfile.from_dict(name, data) if self.project_root: project_path = paths.get_project_workspace_profiles_path(self.project_root) project_data = self._load_file(project_path) for name, data in project_data.get("profiles", {}).items(): profiles[name] = WorkspaceProfile.from_dict(name, data) return profiles def save_profile(self, profile: WorkspaceProfile, scope: str = "project") -> None: path = self._get_path(scope) data = self._load_file(path) if "profiles" not in data: data["profiles"] = {} data["profiles"][profile.name] = profile.to_dict() self._save_file(path, data) def delete_profile(self, name: str, scope: str = "project") -> None: path = self._get_path(scope) data = self._load_file(path) if "profiles" in data and name in data["profiles"]: del data["profiles"][name] self._save_file(path, data) def _load_file(self, path: Path) -> Dict[str, Any]: if not path.exists(): return {} try: with open(path, "rb") as f: return tomllib.load(f) except Exception: return {} def _save_file(self, path: Path, data: Dict[str, Any]) -> None: path.parent.mkdir(parents=True, exist_ok=True) # tomli_w is loaded on-demand to keep the main-thread import graph lean. import tomli_w with open(path, "wb") as f: tomli_w.dump(data, f)