Spaces:
Paused
Paused
Download Browser_Agent/browser/accessibility.py from Luca448/APP-Backend: direct link, hf CLI and curl.
- Browser
- Download file 4.53 kB
-
https://huggingface.co/spaces/Luca448/APP-Backend/resolve/main/Browser_Agent/browser/accessibility.py
- Command line
-
hf download hf://spaces/Luca448/APP-Backend/Browser_Agent/browser/accessibility.py
-
curl -L -o accessibility.py https://huggingface.co/spaces/Luca448/APP-Backend/resolve/main/Browser_Agent/browser/accessibility.py
4.53 kB
| # browser/accessibility.py | |
| import json | |
| from playwright.async_api import Page | |
| _SKIP_PROPERTIES = { | |
| "description", "value", "keyshortcuts", "roledescription", | |
| "valuetext", "disabled", "expanded", "focused", "modal", | |
| "multiline", "multiselectable", "readonly", "required", "selected" | |
| } | |
| def compress_ax_node(node: dict) -> dict | None: | |
| if "ignored" in node and node["ignored"]: | |
| return None | |
| compressed = { | |
| "id": node.get("nodeId", ""), | |
| "role": node.get("role", ""), | |
| "name": node.get("name", ""), | |
| } | |
| if "children" in node: | |
| children = [] | |
| for child in node["children"]: | |
| comp_child = compress_ax_node(child) | |
| if comp_child: | |
| children.append(comp_child) | |
| if children: | |
| compressed["children"] = children | |
| if node.get("role") in ("none", "generic", "StaticText") and not node.get("name"): | |
| if "children" not in compressed: | |
| return None | |
| return compressed | |
| async def extract_ax_tree(page: Page) -> str: | |
| """ | |
| Gibt den komprimierten Accessibility Tree (ohne rohes HTML) zurück. | |
| Schützt vor Prompt Injections und spart Token. | |
| """ | |
| client = await page.context.new_cdp_session(page) | |
| response = await client.send("Accessibility.getFullAXTree") | |
| nodes = response.get("nodes", []) | |
| if not nodes: | |
| return "{}" | |
| root_node = nodes[0] | |
| # Wir iterieren durch die flache Liste und bauen den Baum zusammen | |
| # Für diese vereinfachte Implementierung geben wir vorerst die gefilterte Liste zurück | |
| # Eine echte Rekursion auf der Basis der 'childIds' wäre noch robuster. | |
| simplified_nodes = [] | |
| for n in nodes: | |
| if n.get("role") not in ("generic", "StaticText") or n.get("name"): | |
| if not n.get("ignored"): | |
| simplified_nodes.append({ | |
| "id": n.get("nodeId"), | |
| "role": n.get("role"), | |
| "name": n.get("name", {}).get("value", "") | |
| }) | |
| return json.dumps(simplified_nodes, ensure_ascii=False) | |
| async def extract_dom_text(page: Page) -> str: | |
| """ | |
| Token-sparende DOM Extraktion (inspiriert vom Antigravity Mobile Browser). | |
| Filtert SCRIPT, STYLE und generiert eine textuelle Repräsentation. | |
| """ | |
| js = r''' | |
| (function(){ | |
| const selector = [ | |
| "a", "button", "input", "textarea", "select", "iframe", | |
| "[role=button]", "[role=checkbox]", "[role=link]", | |
| "[contenteditable=true]", "[tabindex]" | |
| ].join(","); | |
| const candidates = Array.from(document.querySelectorAll(selector)); | |
| const visible = candidates.filter(el => { | |
| const style = getComputedStyle(el); | |
| const rect = el.getBoundingClientRect(); | |
| return style.display !== "none" && style.visibility !== "hidden" && | |
| rect.width > 0 && rect.height > 0; | |
| }).slice(0, 80); | |
| window.__gravityAxNextId = window.__gravityAxNextId || 0; | |
| const elements = visible.map((el) => { | |
| if (!el.getAttribute("data-ax-node-id")) { | |
| el.setAttribute( | |
| "data-ax-node-id", | |
| "ax-" + window.__gravityAxNextId++ | |
| ); | |
| } | |
| const id = el.getAttribute("data-ax-node-id"); | |
| const name = ( | |
| el.getAttribute("aria-label") || el.getAttribute("title") || | |
| el.placeholder || el.name || el.value || el.textContent || "" | |
| ).replace(/\s+/g, " ").trim().substring(0, 120); | |
| const item = { | |
| id, | |
| tag: el.tagName.toLowerCase(), | |
| role: el.getAttribute("role") || "", | |
| name | |
| }; | |
| if (el.type) item.type = el.type; | |
| if (el.checked !== undefined) item.checked = Boolean(el.checked); | |
| if (el.disabled !== undefined) item.disabled = Boolean(el.disabled); | |
| if (el.href) item.href = String(el.href).substring(0, 180); | |
| return item; | |
| }); | |
| const pageText = (document.body.innerText || "") | |
| .replace(/\s+/g, " ").trim().substring(0, 2500); | |
| return JSON.stringify({ | |
| url: location.href, | |
| title: document.title, | |
| elements, | |
| text: pageText | |
| }); | |
| })() | |
| ''' | |
| try: | |
| text = await page.evaluate(js) | |
| return text if text else "" | |
| except Exception: | |
| return "" | |