From f6ffeb22ec0f5448531aaa0fdbc588f22045369e Mon Sep 17 00:00:00 2001 From: Claude Agent Date: Thu, 16 Apr 2026 06:15:22 +0000 Subject: [PATCH] fix(calibrate): case-insensitive window title match; show visible titles on miss Co-Authored-By: Claude Opus 4.6 (1M context) --- src/atm/calibrate.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/atm/calibrate.py b/src/atm/calibrate.py index cf35ed9..742c893 100644 --- a/src/atm/calibrate.py +++ b/src/atm/calibrate.py @@ -67,18 +67,24 @@ def write_config(data: dict, out_dir: Path) -> Path: # --------------------------------------------------------------------------- def _capture_window(title_substr: str): - """Screenshot the first window matching *title_substr*. Windows-only (mss+pygetwindow). + """Screenshot the first window whose title CONTAINS *title_substr* (case-insensitive). - Returns a PIL RGB Image. + Windows-only (mss+pygetwindow). Returns a PIL RGB Image. """ import mss # type: ignore[import-untyped] import pygetwindow as gw # type: ignore[import-untyped] from PIL import Image - wins = gw.getWindowsWithTitle(title_substr) - if not wins: - raise RuntimeError(f"No window matching title substring: {title_substr!r}") - win = wins[0] + needle = title_substr.lower() + all_titles = [t for t in gw.getAllTitles() if t] + matching = [t for t in all_titles if needle in t.lower()] + if not matching: + preview = "\n ".join(sorted(set(all_titles))[:40]) + raise RuntimeError( + f"No window title contains {title_substr!r} (case-insensitive).\n" + f"Visible windows (first 40):\n {preview}" + ) + win = gw.getWindowsWithTitle(matching[0])[0] try: win.activate() except Exception: