<!-- GENERATED by scripts/build-llms.mjs from llms/agent-tools.md — do not edit this file. -->

# `lr-browser-frame`

- **Import** `import '@aceshooting/lyra-ui/components/lr-browser-frame.js';` (stable tag alias; registers the tag)
- **Class** `LyraBrowserFrame`, also available unregistered from `@aceshooting/lyra-ui/components/agent-tools/browser-frame/browser-frame.class.js`
- **Family** `components/agent-tools/` — see `llms/index.md` for its siblings
- **Status** `stable` since `4.0.0` — see the maturity and deprecation policy in `llms/shared.md`
- **Release history** [CHANGELOG.md](../../CHANGELOG.md); family-wide breaking-change summaries: [llms-full.txt](../../llms-full.txt)
- **Deprecations** none
- **Optional peers** none
- **Themeable via** 11 parts, 7 custom properties — see this component's own `@csspart`/`@cssprop` list below
- **Library-wide behavior** (events, form association, `locale`/`strings`, tokens, TS types): `llms/shared.md`

---

## `lr-browser-frame`

Presentational "agent computer" viewport: a screenshot/frame stream (or slotted live media), a
read-only URL display, action-ping overlays, and take-over/stop affordances. No automation transport
(no CDP/WebRTC/WebSocket) and no input relay — take-over is an event; the host swaps in its own
interactive element (e.g. an iframe). No replay scrubber (compose `lr-sequence-playback` with
`itemCount` set to the screenshot count, then drive `frameSrc` from `currentIndex` whenever
`lr-sequence-step` emits `detail: { currentIndex }`); no console/network drawers (compose
`lr-terminal`/`lr-json-viewer`); no
pan/zoom of the frame content (slot the image/video inside a `lr-zoomable-frame` instead, though
the pings overlay assumes the unzoomed content box in that composition).

**Properties:** `frameSrc: string = ''` (attribute `frame-src`) — image/MJPEG stream URL rendered as
an `<img>` (safe-URL-gated via `safeMediaSrc`); ignored once the default slot has content. `url:
string = ''` — address shown read-only in the toolbar (`dir="ltr"`, truncating, full value in
`title`). `phase: LyraStreamPhase = 'idle'` (reflected; `'idle' | 'connecting' | 'streaming' |
'stalled'`). `controller:
'agent' | 'user' = 'agent'` (reflected) — who is driving; switches the take-over button's label.
`pings: BrowserPing[] = []` (attribute: false, each `{ id, x, y, kind: 'click' | 'type' | 'scroll' |
'move' }` — `x`/`y` are percent (0–100) of the frame's `object-fit: contain` content box,
letterboxing-aware). Empty/blank ping ids and later duplicates are omitted before overlay rendering. `controls:
boolean = true` — render the built-in take-over/stop buttons.

**Slots:** default — host-owned live element (e.g. `<video>` or an interactive `<iframe>`), replacing
the `frame-src` image. `actions` — extra toolbar controls.

**Events:** `lr-take-over` — `detail: { controller }`, the _requested_ controller (`'user'` when
"Take over" is pressed, `'agent'` when "Hand back" is). `lr-stop` — stop the agent's browser
session, no detail.

**CSS parts:** `base` (`role="group"`), `toolbar`, `url`, `status` (visible, non-live text),
`controller-badge`, `actions`, `take-over-button`, `stop-button`, `viewport`, `frame` (the
`frame-src` `<img>`, absent once the default slot is populated), `ping` (one action-ping marker,
carries `data-kind`).

After mount, each `phase` transition is appended to the shared polite light-DOM announcement sink.
The phase already shown on initial mount or reconnect establishes a silent baseline, including a
phase write queued while detached.

**Themeable custom properties:** `--lr-browser-frame-aspect-ratio` (default `16 / 9`) — the
viewport's aspect ratio.

```html
<lr-browser-frame phase="streaming" url="https://example.com"></lr-browser-frame>
<script type="module">
  const frame = document.querySelector("lr-browser-frame");
  frame.pings = pings;
  frame.addEventListener("lr-take-over", (e) => setController(e.detail.controller));
  frame.addEventListener("lr-stop", () => stopSession());
</script>
```

**Additional API surface:**

- `--lr-browser-frame-controller-background` — Controller badge background. Default: `var(--lr-color-brand-quiet)`.
- `--lr-browser-frame-controller-color` — Controller badge text color. Default: `var(--lr-color-brand)`.
- `--lr-browser-frame-ping-click-color` — Click-ping border color. Default: `var(--lr-color-brand)`.
- `--lr-browser-frame-ping-type-color` — Type-ping border color. Default: `var(--lr-color-success)`.
- `--lr-browser-frame-ping-scroll-color` — Scroll-ping border color. Default: `var(--lr-color-warning)`.
- `--lr-browser-frame-ping-move-color` — Move-ping border color. Default: `var(--lr-color-text-quiet)`.
