from __future__ import annotations import json import logging from pathlib import Path from ameba_control_panel.config import app_data_dir logger = logging.getLogger(__name__) _DEFAULTS = { # Font "font_family": "JetBrains Mono", "font_size": 10, "ui_font_size": 10, # Serial & Log "default_baud": 1_500_000, "partial_line_hold_ms": 300, "port_scan_interval_sec": 5, "log_tail_lines": 100_000, "log_archive_max": 500_000, "log_flush_interval_ms": 30, "log_flush_batch_limit": 200, # Flash "default_rdev_path": "", "default_floader_path": "", "default_boot_start": "0x08000000", "default_boot_end": "0x08040000", "default_app_start": "0x08040000", "default_app_end": "0x08440000", "default_nn_start": "0x088A3000", "default_nn_end": "0x08EB2FFF", # Command "cmd_delay_ms": 50, "char_delay_ms": 0, "history_max_entries": 500, } class Settings: def __init__(self) -> None: self._path = app_data_dir() / "settings.json" self._data: dict = {} self._load() def _load(self) -> None: try: self._data = json.loads(self._path.read_text(encoding="utf-8")) except FileNotFoundError: self._data = {} except Exception: logger.warning("Corrupt settings file, using defaults: %s", self._path) self._data = {} def save(self) -> None: try: self._path.parent.mkdir(parents=True, exist_ok=True) tmp = self._path.with_suffix(".json.tmp") tmp.write_text(json.dumps(self._data, indent=2), encoding="utf-8") tmp.replace(self._path) except Exception: logger.exception("Failed to write settings: %s", self._path) def get(self, key: str): return self._data.get(key, _DEFAULTS.get(key)) def set(self, key: str, value) -> None: self._data[key] = value # Convenience properties for frequently accessed settings @property def font_family(self) -> str: return self.get("font_family") @font_family.setter def font_family(self, v: str) -> None: self.set("font_family", v) @property def font_size(self) -> int: return self.get("font_size") @font_size.setter def font_size(self, v: int) -> None: self.set("font_size", v) @property def ui_font_size(self) -> int: return self.get("ui_font_size") @ui_font_size.setter def ui_font_size(self, v: int) -> None: self.set("ui_font_size", v) @property def default_baud(self) -> int: return self.get("default_baud") @default_baud.setter def default_baud(self, v: int) -> None: self.set("default_baud", v) @property def partial_line_hold_ms(self) -> int: return self.get("partial_line_hold_ms") @partial_line_hold_ms.setter def partial_line_hold_ms(self, v: int) -> None: self.set("partial_line_hold_ms", v) @property def port_scan_interval_sec(self) -> int: return self.get("port_scan_interval_sec") @port_scan_interval_sec.setter def port_scan_interval_sec(self, v: int) -> None: self.set("port_scan_interval_sec", v) @property def log_tail_lines(self) -> int: return self.get("log_tail_lines") @log_tail_lines.setter def log_tail_lines(self, v: int) -> None: self.set("log_tail_lines", v) @property def cmd_delay_ms(self) -> int: return self.get("cmd_delay_ms") @cmd_delay_ms.setter def cmd_delay_ms(self, v: int) -> None: self.set("cmd_delay_ms", v) @property def char_delay_ms(self) -> int: return self.get("char_delay_ms") @char_delay_ms.setter def char_delay_ms(self, v: int) -> None: self.set("char_delay_ms", v) @property def history_max_entries(self) -> int: return self.get("history_max_entries") @history_max_entries.setter def history_max_entries(self, v: int) -> None: self.set("history_max_entries", v)