Newer
Older
navi-1 / clients / terminal / tui / settings.py
"""Persistent TUI settings stored in ~/.navi_code/tui.json."""

from __future__ import annotations

import json
from dataclasses import asdict, dataclass, field
from pathlib import Path
from typing import Any

from clients.terminal.config import settings


@dataclass
class TuiSettings:
    """User-facing TUI configuration persisted across restarts."""

    theme: str = "gnexus-dark"
    mouse: bool = True
    scroll_speed: int = 1
    diff_style: str = "unified"
    keybinds: dict[str, str] = field(default_factory=dict)

    def to_dict(self) -> dict[str, Any]:
        return asdict(self)

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> "TuiSettings":
        field_defaults = {f.name: f.default for f in cls.__dataclass_fields__.values()}
        merged = dict(field_defaults)
        merged.update(data)
        keybinds = merged.get("keybinds", {})
        if keybinds is None or not isinstance(keybinds, dict):
            keybinds = {}
        return cls(
            theme=merged.get("theme", "gnexus-dark"),
            mouse=merged.get("mouse", True),
            scroll_speed=merged.get("scroll_speed", 1),
            diff_style=merged.get("diff_style", "unified"),
            keybinds=dict(keybinds),
        )

    @property
    def settings_dir(self) -> Path:
        return settings.state_dir

    @property
    def settings_file(self) -> Path:
        return self.settings_dir / "tui.json"

    def load(self) -> "TuiSettings":
        if not self.settings_file.exists():
            return self._save_and_return()
        try:
            with self.settings_file.open("r", encoding="utf-8") as f:
                data = json.load(f)
        except (json.JSONDecodeError, OSError):
            return self._save_and_return()
        loaded = self.from_dict(data)
        loaded._ensure_dir()
        return loaded

    def save(self) -> None:
        self._ensure_dir()
        with self.settings_file.open("w", encoding="utf-8") as f:
            json.dump(self.to_dict(), f, indent=2, ensure_ascii=False)

    def _ensure_dir(self) -> None:
        self.settings_dir.mkdir(parents=True, exist_ok=True)

    def _save_and_return(self) -> "TuiSettings":
        self.save()
        return self


# Global lazy-loaded instance.
_tui_settings: TuiSettings | None = None


def get_tui_settings() -> TuiSettings:
    """Return the loaded TUI settings, caching them for the process lifetime."""
    global _tui_settings
    if _tui_settings is None:
        _tui_settings = TuiSettings().load()
    return _tui_settings


def reload_tui_settings() -> TuiSettings:
    """Force reload from disk and return fresh settings."""
    global _tui_settings
    _tui_settings = TuiSettings().load()
    return _tui_settings