# v3.1.1 settings.py import json from dataclasses import dataclass from pathlib import Path from typing import Final APP_DIR: Final[Path] = ( Path(__file__).resolve().parent.parent ) SETTINGS_FILE: Final[Path] = ( APP_DIR / "settings.json" ) DEFAULT_SETTINGS: Final[dict] = { "active_profile": "default", "profiles": { "default": { "theme": "macos_blue", "window": { "width": 260, "height": 300, "min_width": 220, "min_height": 140, "layout_margin": 4, "layout_spacing": 4, "drag_handle_height": 28, "resize_grip_size": 16, "corner_radius": 16, "startup_stabilize_ms": 80, }, "appearance": { "enable_mica": True, "enable_acrylic": True, "enable_glass_blur": True, "enable_animations": True, "enable_shadow": True, "opacity_active": 0.99, "opacity_inactive": 0.95, "opacity_dragging": 0.98, "focus_animation_ms": 180, }, "editor": { "font_family": [ "Inter", "SF Pro Display", "Helvetica", "Arial", "system-ui", ], "font_size": 15, "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": 8, }, "behavior": { "enable_tray": True, "enable_persistence": True, "enable_window_memory": True, "enable_auto_restore": True, "high_contrast_inactive": False, }, }, "light": { "theme": "light", "window": { "width": 260, "height": 300, "min_width": 220, "min_height": 140, "layout_margin": 4, "layout_spacing": 4, "drag_handle_height": 28, "resize_grip_size": 16, "corner_radius": 16, "startup_stabilize_ms": 80, }, "appearance": { "enable_mica": True, "enable_acrylic": True, "enable_glass_blur": True, "enable_animations": True, "enable_shadow": False, "opacity_active": 0.99, "opacity_inactive": 0.95, "opacity_dragging": 0.98, "focus_animation_ms": 120, }, "editor": { "font_family": [ "Inter", "Segoe UI", "Arial", "system-ui", ], "font_size": 13, "preview_delay_ms": 500, "save_debounce_ms": 400, "max_note_size": 2_000_000, "enable_markdown_preview": True, "enable_clickable_links": True, "enable_antialiasing": True, "enable_spellcheck": False, "padding": 8, }, "behavior": { "enable_tray": True, "enable_persistence": True, "enable_window_memory": True, "enable_auto_restore": True, "high_contrast_inactive": False, }, }, "accessibility": { "theme": "dark", "window": { "width": 320, "height": 380, "min_width": 260, "min_height": 180, "layout_margin": 6, "layout_spacing": 6, "drag_handle_height": 32, "resize_grip_size": 18, "corner_radius": 18, "startup_stabilize_ms": 80, }, "appearance": { "enable_mica": True, "enable_acrylic": True, "enable_glass_blur": True, "enable_animations": False, "enable_shadow": True, "opacity_active": 0.92, "opacity_inactive": 0.98, "opacity_dragging": 1.0, "focus_animation_ms": 0, }, "editor": { "font_family": [ "Inter", "Segoe UI", "Arial", "system-ui", ], "font_size": 15, "preview_delay_ms": 300, "save_debounce_ms": 300, "max_note_size": 2_000_000, "enable_markdown_preview": True, "enable_clickable_links": True, "enable_antialiasing": True, "enable_spellcheck": False, "padding": 6, }, "behavior": { "enable_tray": True, "enable_persistence": True, "enable_window_memory": True, "enable_auto_restore": True, "high_contrast_inactive": True, }, }, }, } @dataclass(slots=True) class SettingsManager: settings: dict @classmethod def load(cls) -> "SettingsManager": if not SETTINGS_FILE.exists(): SETTINGS_FILE.write_text( json.dumps( DEFAULT_SETTINGS, indent=2, ), encoding="utf-8", ) return cls( DEFAULT_SETTINGS ) try: raw = SETTINGS_FILE.read_text( encoding="utf-8", ) data = json.loads(raw) merged = cls.merge_defaults( DEFAULT_SETTINGS, data, ) return cls(merged) except Exception: 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: SETTINGS_FILE.write_text( json.dumps( self.settings, indent=2, ), encoding="utf-8", ) @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 set_active_profile( self, profile_name: str, ) -> None: if ( profile_name not in self.profiles ): return self.settings[ "active_profile" ] = profile_name self.save() def reload(self) -> None: loaded = ( SettingsManager.load() ) self.settings = ( loaded.settings ) def reset(self) -> None: self.settings = ( DEFAULT_SETTINGS ) self.save() def create_profile( self, name: str, data: dict, ) -> None: self.settings[ "profiles" ][name] = data self.save() def delete_profile( self, name: str, ) -> None: if name == "default": return if ( name not in self.settings[ "profiles" ] ): return del self.settings[ "profiles" ][name] if ( self.active_profile_name == name ): self.settings[ "active_profile" ] = "default" self.save()