"""Dump the real DOM of an Elmhurst RdSAP-10-Online page (dev tool). The entry tool is an ASP.NET WebForms app: field IDs are server-generated (``ctl00_ctl00_ContentBody_...``) and many controls postback on change. We can't guess selectors, so this reads them off the live page. Reuses the saved persistent session (no login needed). Run it on the viewer display: DISPLAY=:99 python scripts/hyde/elmhurst_explore.py "" Writes, next to this file: * elmhurst_dom/.png — screenshot (confirms we're authenticated) * elmhurst_dom/.json — every input/select/textarea + the left-nav links It navigates, dumps, and exits (releasing the profile lock for the next step). """ from __future__ import annotations import json import re import sys from pathlib import Path from typing import Any from playwright.sync_api import sync_playwright HERE = Path(__file__).parent SESSION_DIR = HERE / ".elmhurst-session" OUT_DIR = HERE / "elmhurst_dom" # Runs in the page to harvest every form control + the section nav, with the # label text an assessor reads — so we can tie markdown fields to server IDs. _DUMP_JS = r""" () => { const labelFor = (el) => { if (el.id) { const l = document.querySelector(`label[for="${el.id}"]`); if (l) return l.innerText.trim(); } // WebForms often puts the caption in a sibling cell, not a