""" modules/clipboard_intelligence.py - Phase 8 clipboard intelligence Detects useful content types from clipboard history/current clipboard: - URLs - code snippets - email addresses - phone numbers - postal-ish addresses (best-effort heuristic) """ from __future__ import annotations import json import re from dataclasses import dataclass from pathlib import Path from modules.clipboard_control import _read_text, HIST_PATH URL_RE = re.compile(r"https?://[^\s]+", re.I) EMAIL_RE = re.compile(r"\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b", re.I) PHONE_RE = re.compile(r"(?:(?:\+?\d{1,3}[- ]?)?(?:\(?\d{3}\)?[- ]?)?\d{3}[- ]?\d{4,})") CODE_HINTS = ("def ", "class ", "import ", "from ", "{", "}", "=>", "function ", "SELECT ", "CREATE ", "console.log") @dataclass(frozen=True, slots=True) class ClipInsight: kind: str preview: str def _load_history(limit: int = 20) -> list[str]: if not HIST_PATH.exists(): return [] try: data = json.loads(HIST_PATH.read_text(encoding="utf-8")) if not isinstance(data, list): return [] return [str(item.get("text") or "") for item in data[-limit:]] except Exception: return [] def _detect_one(text: str) -> list[ClipInsight]: t = (text or "").strip() if not t: return [] out: list[ClipInsight] = [] urls = URL_RE.findall(t) if urls: out.extend(ClipInsight("url", u[:120]) for u in urls[:3]) mails = EMAIL_RE.findall(t) if mails: out.extend(ClipInsight("email", m[:120]) for m in mails[:3]) phones = PHONE_RE.findall(t) if phones: out.extend(ClipInsight("phone", p[:40]) for p in phones[:3]) if any(h in t for h in CODE_HINTS): out.append(ClipInsight("code", t[:120].replace("\n", " "))) # Rough address heuristic: number + street-ish word + comma/city-like structure if re.search(r"\b\d{1,5}\s+[A-Za-z0-9 .'-]{3,}\b(?:road|rd|street|st|avenue|ave|lane|ln|nagar|society|colony)\b", t, re.I): out.append(ClipInsight("address", t[:140].replace("\n", " "))) return out def inspect_current() -> list[ClipInsight]: return _detect_one(_read_text() or "") def inspect_history(limit: int = 20) -> list[ClipInsight]: seen = set() out: list[ClipInsight] = [] for text in reversed(_load_history(limit)): for insight in _detect_one(text): key = (insight.kind, insight.preview) if key in seen: continue seen.add(key) out.append(insight) return out def summary() -> str: items = inspect_current() or inspect_history(20) if not items: return "Clipboard intelligence: nothing structured detected." kinds = {} for item in items: kinds[item.kind] = kinds.get(item.kind, 0) + 1 parts = [f"{k} x{v}" for k, v in sorted(kinds.items())] preview = items[0].preview return f"Clipboard intelligence: {', '.join(parts)}. Latest match: {preview}"