master
John Lauer Update 40 file(s) 70e1922 2mo ago
"""settings.py — kicad_get_settings / kicad_set_settings: the bridge's
user-visible preferences, persisted per box.

Born 2026-08-17 (John): "place adom favicon as overlay icons into all your
kicad windows taskbar icons ... but this should be a Setting in your settings
window in your dashboard." These verbs are the settings BACKEND; the dashboard
(kicad launch page, modeled on fusion-dashboard) renders them.

Settings live in %USERPROFILE%/.adom/kicad-bridge-settings.json.

Current settings:
  overlayBadges (bool, default true) — stamp the Adom favicon as a taskbar
    overlay on every bridge-created KiCad window, so the user can see at a
    glance which windows Adom is driving.
"""
import json
import os
from pathlib import Path

_DEFAULTS = {
    "overlayBadges": True,
    # How the taskbar shows "Adom is driving KiCad" (John, 2026-08-22, adopted
    # from the fusion bridge): "progress" paints a progress bar on the KiCad
    # button from the measured verb timings and suppresses the OS orange flash;
    # "flash" leaves Windows' attention flash alone and paints no bar; "both";
    # "none". Default progress: a flash nags the user to click it away, while a
    # bar ends by itself exactly when Adom stops driving.
    "activityIndicator": "progress",
}


def _path() -> Path:
    base = os.environ.get("USERPROFILE") or str(Path.home())
    d = Path(base) / ".adom"
    d.mkdir(parents=True, exist_ok=True)
    return d / "kicad-bridge-settings.json"


def get_settings() -> dict:
    out = dict(_DEFAULTS)
    try:
        out.update(json.loads(_path().read_text(encoding="utf-8")))
    except Exception:
        pass
    return out


def handle_get_settings(kicad_info: dict, args: dict) -> dict:
    s = get_settings()
    return {
        "success": True,
        "settings": s,
        "defaults": dict(_DEFAULTS),
        "path": str(_path()),
        "_hint": ("kicad_set_settings {\"overlayBadges\": false} to change; changes apply "
                  "immediately (overlay badges are stamped/cleared on live windows)."),
    }


_CHOICES = {"activityIndicator": ("progress", "flash", "both", "none")}


def _coerce(key, raw):
    """Type the value by its DEFAULT's type. The first cut bool()'d everything,
    so activityIndicator:"flash" persisted as True and the indicator silently
    ran its fallback branch (caught 2026-08-23 rebuilding the settings modal)."""
    if key in _CHOICES:
        v = str(raw).strip().lower()
        if v not in _CHOICES[key]:
            raise ValueError(f"{key} must be one of {', '.join(_CHOICES[key])}")
        return v
    if isinstance(_DEFAULTS[key], bool):
        if isinstance(raw, str):
            return raw.strip().lower() in ("1", "true", "on", "yes")
        return bool(raw)
    return raw


def handle_set_settings(kicad_info: dict, args: dict) -> dict:
    s = get_settings()
    changed = {}
    if args.get("restoreDefaults"):
        for k, v in _DEFAULTS.items():
            if s.get(k) != v:
                changed[k] = v
            s[k] = v
    for k in _DEFAULTS:
        if k in args:
            try:
                v = _coerce(k, args[k])
            except ValueError as e:
                return {"success": False, "error": str(e)}
            if s.get(k) != v:
                changed[k] = v
            s[k] = v
    if not changed:
        return {"success": True, "settings": s, "changed": {},
                "output": "no settings changed",
                "_hint": f"Settable keys: {', '.join(_DEFAULTS)}."}
    try:
        _path().write_text(json.dumps(s, indent=2), encoding="utf-8")
    except Exception as e:
        return {"success": False, "error": f"could not persist settings: {e}"}
    applied = {}
    if "overlayBadges" in changed:
        try:
            from handlers.win_taskbar import rebadge_all
            applied["rebadgedWindows"] = rebadge_all(changed["overlayBadges"])
        except Exception:
            pass
    return {"success": True, "settings": s, "changed": changed, **applied,
            "output": f"settings updated: {changed}"}