# v4.0.0 settings.py import json import sys from dataclasses import dataclass from pathlib import Path from typing import Final def resolve_app_dir() -> Path: if getattr(sys, "frozen", False): return Path( sys.executable ).resolve().parent return ( Path(__file__) .resolve() .parent .parent ) APP_DIR: Final[Path] = ( resolve_app_dir() ) APP_DIR.mkdir( parents=True, exist_ok=True, ) SETTINGS_FILE: Final[Path] = ( APP_DIR / "settings.json" ) STATE_FILE: Final[Path] = ( APP_DIR / "active.json" ) MARKDOWN_FILE: Final[Path] = ( APP_DIR / "note.md" ) DEFAULT_SETTINGS: Final[dict] = { "active_profile": "default", "profiles": { "default": { "theme": "dark", "window": { "width": 250, "height": 260, "min_width": 200, "min_height": 100, "layout_margin": 0, "layout_spacing": 0, "drag_handle_height": 0, "resize_grip_size": 16, "corner_radius": 8, "startup_stabilize_ms": 80, }, "appearance": { "enable_mica": True, "enable_acrylic": True, "enable_glass_blur": True, "enable_animations": True, "enable_shadow": False, "opacity_active": 0.97, "opacity_inactive": 0.95, "opacity_dragging": 0.89, "focus_animation_ms": 180, }, "editor": { "font_family": [ "Inter", "SF Pro Display", "Helvetica", "Arial", "system-ui", ], "font_size": 13, "preview_delay_ms": 700, "save_debounce_ms": 500, "max_note_size": 2_000_000, "enable_markdown_preview": True, "enable_clickable_links": True, "enable_antialiasing": True, "enable_spellcheck": False, "padding": 0, }, "behavior": { "enable_tray": True, "enable_persistence": True, "enable_window_memory": True, "enable_auto_restore": True, "high_contrast_inactive": False, }, }, }, } def atomic_write( path: Path, content: str, ) -> None: path.parent.mkdir( parents=True, exist_ok=True, ) temp = path.with_suffix( f"{path.suffix}.tmp" ) temp.write_text( content, encoding="utf-8", ) temp.replace(path) @dataclass(slots=True) class SettingsManager: settings: dict @classmethod def load(cls) -> "SettingsManager": SETTINGS_FILE.parent.mkdir( parents=True, exist_ok=True, ) if not SETTINGS_FILE.exists(): atomic_write( SETTINGS_FILE, json.dumps( DEFAULT_SETTINGS, indent=2, ), ) try: raw = SETTINGS_FILE.read_text( encoding="utf-8", ) data = json.loads(raw) merged = cls.merge_defaults( DEFAULT_SETTINGS, data, ) manager = cls(merged) manager.save() return manager except Exception: atomic_write( SETTINGS_FILE, json.dumps( DEFAULT_SETTINGS, indent=2, ), ) return cls( DEFAULT_SETTINGS ) @staticmethod def merge_defaults( defaults: dict, current: dict, ) -> dict: result = {} for key, value in defaults.items(): result[key] = value for key, value in current.items(): if ( key in result and isinstance( result[key], dict, ) and isinstance( value, dict, ) ): result[key] = ( SettingsManager.merge_defaults( result[key], value, ) ) else: result[key] = value return result def save(self) -> None: atomic_write( SETTINGS_FILE, json.dumps( self.settings, indent=2, ), ) @property def active_profile_name( self, ) -> str: return self.settings.get( "active_profile", "default", ) @property def profiles(self) -> dict: return self.settings.get( "profiles", {}, ) @property def profile(self) -> dict: active = ( self.active_profile_name ) profiles = self.profiles if active not in profiles: return profiles[ "default" ] return profiles[active] def reload(self) -> None: loaded = ( SettingsManager.load() ) self.settings = ( loaded.settings ) def reset(self) -> None: self.settings = ( DEFAULT_SETTINGS ) self.save()