"""Shared configuration constants for the application.""" from __future__ import annotations import contextlib from pathlib import Path from typing import Any, Callable try: # Python 3.11 and above import tomllib # type: ignore[attr-defined] except ModuleNotFoundError: # pragma: no cover - fallback for <3.11 with contextlib.suppress(ModuleNotFoundError): import tomli as tomllib # type: ignore[assignment] if "tomllib" not in globals(): tomllib = None # type: ignore[assignment] PREVIEW_MAX_SIZE = (900, 660) BASE_DIR = Path(__file__).resolve().parents[2] IMAGES_DIR = BASE_DIR / "images" CONFIG_FILE = BASE_DIR / "config.toml" LANG_DIR = BASE_DIR / "app" / "lang" _DEFAULTS_BASE = { "hue_min": 250.0, "hue_max": 310.0, "sat_min": 15.0, "val_min": 15.0, "val_max": 100.0, "alpha": 120, } SUPPORTED_IMAGE_EXTENSIONS = (".webp", ".png", ".jpg", ".jpeg", ".bmp") LANGUAGE_DEFAULT = "en" _DEFAULT_TYPES: dict[str, Callable[[Any], Any]] = { "hue_min": float, "hue_max": float, "sat_min": float, "val_min": float, "val_max": float, "alpha": int, } def _load_config_data() -> dict[str, Any]: """Read the optional config file once and return its parsed data.""" if tomllib is None or not CONFIG_FILE.exists(): return {} decode_error = getattr(tomllib, "TOMLDecodeError", ValueError) # type: ignore[attr-defined] try: with CONFIG_FILE.open("rb") as handle: data = tomllib.load(handle) except (OSError, AttributeError, decode_error, TypeError): # type: ignore[arg-type] return {} if not isinstance(data, dict): return {} return data def _extract_default_overrides(data: dict[str, Any]) -> dict[str, Any]: settings = data.get("defaults") if not isinstance(settings, dict): return {} overrides: dict[str, Any] = {} for key, caster in _DEFAULT_TYPES.items(): if key not in settings: continue try: overrides[key] = caster(settings[key]) except (TypeError, ValueError): continue return overrides def _available_languages() -> set[str]: languages = {path.stem.lower() for path in LANG_DIR.glob("*.toml")} if not languages: languages.add(LANGUAGE_DEFAULT) return languages def _extract_language(data: dict[str, Any]) -> str: value = data.get("language") supported = _available_languages() if isinstance(value, str): normalised = value.strip().lower() if normalised in supported: return normalised if LANGUAGE_DEFAULT in supported: return LANGUAGE_DEFAULT return sorted(supported)[0] _CONFIG_DATA = _load_config_data() DEFAULTS = {**_DEFAULTS_BASE, **_extract_default_overrides(_CONFIG_DATA)} LANGUAGE = _extract_language(_CONFIG_DATA)