Initial commit: Screen Leads app
Screen-capture lead tool: capture agent, Claude vision extractor with pluggable site recipes (LinkedIn), canonical funnel model, SQLite storage, FastAPI backend, dashboard, and setup/run scripts. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
"""Cross-platform helper for reading the front browser tab's URL.
|
||||
|
||||
This is the one genuinely OS-specific piece. Reading *your own* browser's
|
||||
current URL is a passive, local operation (no request to the target site), so
|
||||
it stays within the screen-capture / compliance posture. When it can't return
|
||||
a URL (common on Windows/Linux), the detector falls back to reading the address
|
||||
bar visually from a screenshot via the AI.
|
||||
"""
|
||||
import sys
|
||||
from typing import Optional
|
||||
|
||||
|
||||
def get_active_tab_url() -> Optional[str]:
|
||||
"""Best-effort URL of the frontmost browser tab, or None if unavailable."""
|
||||
try:
|
||||
if sys.platform == "darwin":
|
||||
from . import macos
|
||||
|
||||
return macos.get_active_tab_url()
|
||||
if sys.platform.startswith("linux"):
|
||||
from . import linux
|
||||
|
||||
return linux.get_active_tab_url()
|
||||
if sys.platform in ("win32", "cygwin"):
|
||||
from . import windows
|
||||
|
||||
return windows.get_active_tab_url()
|
||||
except Exception:
|
||||
return None
|
||||
return None
|
||||
@@ -0,0 +1,11 @@
|
||||
"""Linux (X11): the active window *title* is reachable via xdotool, but the URL
|
||||
is not exposed without a browser extension or the accessibility bus. We return
|
||||
None so the detector falls back to reading the address bar visually from the
|
||||
screenshot, which is the portable path. (Wayland exposes neither; the app warns
|
||||
about that separately.)
|
||||
"""
|
||||
from typing import Optional
|
||||
|
||||
|
||||
def get_active_tab_url() -> Optional[str]:
|
||||
return None
|
||||
@@ -0,0 +1,39 @@
|
||||
"""macOS: read the frontmost browser tab URL via AppleScript.
|
||||
|
||||
Reads local browser state only (no network call to the target site). Requires
|
||||
the terminal/app running this to have Automation permission for the browser.
|
||||
"""
|
||||
import subprocess
|
||||
from typing import Optional
|
||||
|
||||
# Browsers that expose `URL of active tab of front window` (Chromium family)
|
||||
# or `URL of front document` (Safari).
|
||||
_CHROMIUM = ["Google Chrome", "Brave Browser", "Microsoft Edge", "Arc", "Vivaldi", "Chromium"]
|
||||
|
||||
|
||||
def _frontmost_app() -> Optional[str]:
|
||||
script = 'tell application "System Events" to get name of first process whose frontmost is true'
|
||||
return _osascript(script)
|
||||
|
||||
|
||||
def _osascript(script: str) -> Optional[str]:
|
||||
try:
|
||||
out = subprocess.run(
|
||||
["osascript", "-e", script],
|
||||
capture_output=True, text=True, timeout=5,
|
||||
)
|
||||
val = out.stdout.strip()
|
||||
return val or None
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def get_active_tab_url() -> Optional[str]:
|
||||
app = _frontmost_app()
|
||||
if not app:
|
||||
return None
|
||||
if app in _CHROMIUM:
|
||||
return _osascript(f'tell application "{app}" to get URL of active tab of front window')
|
||||
if app == "Safari":
|
||||
return _osascript('tell application "Safari" to get URL of front document')
|
||||
return None
|
||||
@@ -0,0 +1,24 @@
|
||||
"""Windows: reliably reading a browser's address-bar URL needs UI Automation
|
||||
(pywinauto/uiautomation). We attempt a lightweight UIA read if available and
|
||||
otherwise return None, letting the detector fall back to visual address-bar
|
||||
reading from the screenshot.
|
||||
"""
|
||||
from typing import Optional
|
||||
|
||||
|
||||
def get_active_tab_url() -> Optional[str]:
|
||||
try:
|
||||
import uiautomation as auto # optional dependency
|
||||
except Exception:
|
||||
return None
|
||||
try:
|
||||
window = auto.GetForegroundControl()
|
||||
# Chromium/Firefox expose the address bar as an Edit control named
|
||||
# "Address and search bar" / "Search or enter address".
|
||||
edit = window.EditControl(searchDepth=12)
|
||||
if edit.Exists(0.3):
|
||||
val = (edit.GetValuePattern().Value or "").strip()
|
||||
return val or None
|
||||
except Exception:
|
||||
return None
|
||||
return None
|
||||
Reference in New Issue
Block a user