# WebVision

**Structured, ref-addressable views of the live DOM** for automation, debugging, and tooling.

WebVision walks the browser document, builds a compact **VX tree** (semantic nodes with stable refs), and can **render** it as text, **highlight** regions on the page, and **resolve** refs back to real DOM nodes. It is designed for scenarios where you need a consistent, inspectable snapshot of “what’s on screen” without shipping a full browser automation stack.

---

## Why use it?

- **Stable refs** — Each meaningful node gets a ref you can use to talk about “that button” or “that heading” across snapshots and tools.
- **Readable dumps** — Turn the tree into a line-oriented string (tags, ids, classes, key attrs, text) for LLMs, logs, or diffing.
- **Visual debugging** — Draw overlays on elements that correspond to VX nodes.
- **Shadow DOM** — Open shadow roots are flattened into the tree by default (see [Shadow DOM](#shadow-dom)).
- **Ships for browser and Node-oriented bundles** — ESM page bundle, IIFE global, optional Tampermonkey userscript for the **page** console.

---

## Install

```bash
npm install @ubio/webvision
```

Build artifacts (`out/page`, `build/*`) are included in the published package. Run `npm run compile` after cloning the repo if you develop from source.

---

## Quick start (ESM)

```javascript
import { captureSnapshot } from '@ubio/webvision';

const tree = await captureSnapshot();
console.log(tree.render({ renderRefs: true, renderTagNames: true }));
```

`captureSnapshot()` parses `document`, stores the latest tree and ref→DOM map on `globalThis`, and returns a **`VxTreeView`**.

---

## Core API

| Export | Role |
|--------|------|
| `captureSnapshot(options?)` | Parse the page; returns `VxTreeView`; updates last snapshot. |
| `getSnapshot()` | Return the last `VxTreeView` (throws if none). |
| `resolveDomNode(ref)` | Map a ref string to a DOM `Node` or `null`. |
| `renderVxNode(node, options?)` | Render a single `VxNode` subtree as text. |

### `VxTreeView`

| Method / property | Description |
|-------------------|-------------|
| `render(options?)` | String dump of the frame’s tree (see `VxRenderOptions` in source). |
| `nodeCount` | Number of ref’d nodes in the map. |
| `highlight(options?)` | Overlay borders for refs (needs snapshot first). |
| `findNode(ref)` | Get the `VxNode` for a ref. |

### Example: snapshot, render, highlight

```javascript
import { captureSnapshot, resolveDomNode } from '@ubio/webvision';

const tree = await captureSnapshot();

console.log(tree.render({
  renderRefs: true,
  renderTagNames: true,
  renderIds: true,
}));

tree.highlight({ clearOverlay: true });

const el = resolveDomNode('0abc'); // example ref from render output
```

### Parser options (`VxTreeOptions`)

Passed to `captureSnapshot({ ... })`:

| Option | Default | Meaning |
|--------|---------|---------|
| `flattenShadowDom` | `true` | Include **open** shadow roots after light-DOM children; set `false` for light DOM only. |
| `viewportOnly` | `false` | Drop nodes outside the viewport. |
| `probeViewport` | `false` | Extra viewport probing (see `probe.ts`). |
| `skipImages` | `false` | Omit `img` nodes when omitting. |
| `opaqueOverlays` | `false` | Try to flatten opaque overlays for parsing. |
| `unnestDivs` | `false` | Aggressive pruning of bare div/spans. |
| `frameId` / `iframeRef` | — | Multi-frame scenarios. |

---

## Shadow DOM

Open shadow trees are walked **after** each host’s light DOM children so the rendered tree matches a **flattened** structural view. **Closed** shadow roots cannot be accessed from script.

To restore the previous behavior (ignore shadow trees):

```javascript
await captureSnapshot({ flattenShadowDom: false });
```

---

## Package exports

| Import path | Output | Use case |
|-------------|--------|----------|
| `@ubio/webvision` | `out/page` (TypeScript build) | Types + ESM in TS projects. |
| `@ubio/webvision/page` | `build/page.mjs` | Single ESM bundle of the page module. |
| `@ubio/webvision/global` | `build/global.js` | IIFE; exposes `globalThis.WebVision` in the browser. |

Generate bundles from source:

```bash
npm run compile:page    # build/page.mjs
npm run compile:global    # build/global.js + source map
```

---

## Browser console: Tampermonkey

For **`window.WebVision`** in the **page** DevTools console (not the extension isolated world), use the generated userscript.

1. **Build** (from repo root):

   ```bash
   npm run compile:userscript
   ```

   Produces `build/global.js` and `build/webvision.user.js`.

2. **Default workflow (hot reload)**  
   - Terminal A: `npm run serve:build` — serves `build/` at `http://127.0.0.1:3847`.  
   - Terminal B: `npm run dev:global` — rebuilds `global.js` on TS changes.  
   - Install **`build/webvision.user.js`** in Tampermonkey (Dashboard → install).  
   - Reload the tab; the script fetches `global.js` with a cache-busting query and injects it into the page.

3. **Offline / no server** — embed the bundle when generating:

   ```bash
   WEBVISION_INLINE=1 npm run compile:userscript
   ```

   Reinstall the userscript after each rebuild (large file).

4. **Custom URL** when building:

   ```bash
   WEBVISION_INJECT_URL=https://your.cdn/webvision/global.js npm run compile:userscript
   ```

Requires `@grant GM.xmlHttpRequest` and matching `// @connect` for the host (generated for you for `http`/`https` URLs).

---

## Development

| Script | Purpose |
|--------|---------|
| `npm run compile` | Clean, `tsc`, bundle `page.mjs` + `global.js` + `webvision.user.js`. |
| `npm run dev` | Parallel `tsc -w` and esbuild watch for `page.mjs`. |
| `npm run dev:global` | Watch rebuild `build/global.js`. |
| `npm run dev:userscript` | One-shot userscript build, then `dev:global` watch. |
| `npm run lint` | ESLint. |

---

## License

ISC
