"""Data controls tab: model-improvement switch (read-only otherwise). The switch label is pinned from live recon; resolution prefers it and falls back to single-switch, then keyword match. Sets use a trusted click: synthetic clicks are proven no-ops here (2026-10-06). Import/Delete rows are inventoried, never touched. """ import time from approvals import cdp_evaluate from hatch_menu import controls, dialog from hatch_menu.mouse import real_click TAB = "Data controls" SWITCH_LABEL = "Help improve our AI models" _KEYWORDS = ("improv", "train", "model", "data", "usage") JS_AI_RECT_TMPL = """((label) => { const d = document.querySelector('[role="dialog"]'); if (!d) return null; const rowOf = (s) => { let el = s.parentElement, depth = 0; while (el && el !== d && depth < 6) { const t = (el.innerText || '').trim(); if (t && t.length < 250) return t.toLowerCase(); el = el.parentElement; depth += 1; } return ''; }; const hits = Array.from(d.querySelectorAll('[role="switch"]')) .filter(s => rowOf(s).includes(label.toLowerCase()) || (s.getAttribute('aria-label') || '').toLowerCase() .includes(label.toLowerCase())); if (hits.length !== 1) return null; const r = hits[0].getBoundingClientRect(); return {x: r.x + r.width / 2, y: r.y + r.height / 2}; })('%s')""" def _eval(ws, js, timeout=8.0): try: return cdp_evaluate(ws, js, timeout=timeout) except Exception: return None def _resolve(ws): """The improvement switch dict, or None when not resolvable.""" if not dialog.goto_tab(ws, TAB): return None switches = controls.list_switches(ws) for s in switches: blob = ((s.get("label") or "") + " " + (s.get("aria") or "")).lower() if SWITCH_LABEL.lower() in blob: return s if len(switches) == 1: return switches[0] for kw in _KEYWORDS: for s in switches: blob = ((s.get("label") or "") + " " + (s.get("aria") or "")).lower() if kw in blob: return s return None def ai_improvement(ws): """Improvement-switch state: True/False, None when unreadable.""" sw = _resolve(ws) return None if sw is None else bool(sw.get("checked")) def set_ai_improvement(ws, on): """Set via one trusted click; synthetic clicks are no-ops. Bool.""" sw = _resolve(ws) if sw is None: return False if bool(sw.get("checked")) == bool(on): return True rect = _eval(ws, JS_AI_RECT_TMPL % SWITCH_LABEL) if not rect or "x" not in rect: return False try: real_click(ws, rect["x"], rect["y"]) except Exception: return False for _ in range(8): time.sleep(2.0) if ai_improvement(ws) is bool(on): return True return False def describe(ws): """Inventory: switch state + row names (import/delete read-only).""" state = ai_improvement(ws) return {"ai_improvement": ("on" if state else "off") if state is not None else None, "rows": dialog.describe_rows(ws, TAB)}