Browser Session
experimentalA computer/browser-use session made legible: tidy browser frame, the page, the agent's cursor with click ripple and target box, and a step log with take-over.
A page with the agent's cursor
Migrate session API to @acme/auth client #482
Opening the pull request
Kura opens the compare view, types the title and description, clicks “Create pull request” and reads the PR number. The cursor glides, clicks ripple, the log advances and the session settles.
Open a pull request
Create a new pull request by comparing changes across two branches.
4 commits · 6 files changed · +142 −38
- Open the compare view (in progress)github.com/acme/web/compare/main...kura/auth-clientnavigate
- Focus the title field (pending)input#pr-titleclick
- Type the PR title (pending)“Migrate session API to @acme/auth client”type
- Write the description (pending)“Replaces fetchUser() and the legacy /api/session…”type
- Click “Create pull request” (pending)button[type=submit]click
- Read the PR number (pending)#482 · Openextract
Paused for you
A 2FA prompt only the human can answer: amber edge, “Resume agent”.
Two-factor authentication
Enter the 6-digit code from your authenticator app to continue to Acme Cloud.
- Open the preview (done)cloud.acme.dev/web/deploymentsnavigate
- Continue with GitHub (done)button[data-provider="github"]click
- Enter the 2FA code (waiting for you)Needs a code only you havetype
- Check the login page (pending)/loginextract
Under the plan
A narrow stacked session under a TaskList, as in a run sidebar.
- Run pnpm test (done)36.8s
- Open pull request (in progress)via the browser
- Summarize the run (pending)
Migrate session API to @acme/auth client #482
- Click “Create pull request” (done)click
- Read the PR number (in progress)h1extract
Installation
npx shadcn@latest add https://kuro.abstergo.fyi/r/browser-session.jsonCopies the source into components/kura/browser-session with its dependencies (kura-theme, button, kura-format, shimmer-text, status-glyph, use-elapsed, utils). Tokens come from the kura-theme item.
When to use it
Use it for
- Show what a browser-use or computer-use agent is doing on a web page (Operator, Claude computer use, Browserbase, Playwright-driven agents).
- Stream screenshots of a remote browser (`src`) with the agent's next action drawn on top (`cursor`, `highlight`).
- Replay a recorded browser run step by step with the log of navigate / click / type / scroll / extract actions.
- Hand control to the human when the agent hits a login, 2FA or CAPTCHA (`status="paused"` + `onResume`), or let them take over (`onTakeOver`).
Reach for something else when
- Terminal or shell output from the agent — use terminal.
- Code edits made by the agent — use diff-view or file-changes.
- A plan or todo list of the whole run — use task-list.
- A static rendered artifact (HTML preview, document) — use artifact-card.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| url* | string | — | Page URL in the address bar. The host is emphasised, the path muted; https shows a lock. |
| title | string | — | Page title next to the address bar (hidden in narrow containers). |
| children | React.ReactNode | — | Viewport content: a live page, or a mock rendered as markup. |
| src | string | — | A screenshot URL to show instead of children (e.g. the latest frame from a remote browser). |
| alt | string | — | Alt text for `src`. Default “Screenshot of {url}”. |
| viewport | { width: number; height: number } | — | Design size of the page in CSS px (e.g. the agent's 1280×800 screen). Children render at this size and scale to fit the frame, so % coordinates stay exact at any width. Omit to let children flow at the frame's width. |
| aspectRatio | number | 16 / 10 | Viewport aspect ratio when `viewport` is not given. |
| scrollTop | number | 0 | Vertical page scroll in viewport px; eases on change (use for scroll steps). |
| cursor | BrowserPoint | null | — | The agent's cursor { x, y } in % of the viewport. Glides to new positions. |
| cursorLabel | string | — | Name tag on the cursor, e.g. the agent's name. |
| clickKey | string | number | — | Change it to play a click ripple at the cursor. Default: the active step's id when that step is a click. |
| highlight | BrowserHighlight | null | — | Box around the target element: { x, y, width, height } in % plus an optional `label` tag (e.g. `button "Create pull request"`). |
| steps | readonly BrowserStep[] | — | The action log. BrowserStep = { id, kind: "navigate" | "click" | "type" | "scroll" | "extract", label, status: AgentStatus, detail? (mono: URL, selector, typed text) }. Omit to hide the log. |
| activeStep | string | — | Id of the step being executed. Default: the first running step. |
| status | "browsing" | "paused" | "done" | "failed" | "browsing" | browsing = “Agent is browsing” (blue, shimmer); paused = “Paused for you” (amber, the viewport gets an amber edge); done; failed. |
| statusText | React.ReactNode | — | Secondary text after the status, e.g. “Enter the 2FA code, then hand back”. |
| startedAt | Date | number | — | Session start (wall clock) for the elapsed clock; ticks while browsing. |
| loading | boolean | — | Show the loading bar under the address bar. Default: true while the active step is a running navigate. |
| actions | React.ReactNode | — | Extra controls in the status line (e.g. open in new tab, replay). |
| onTakeOver | () => void | — | Shows a “Take over” button while browsing. |
| onResume | () => void | — | Shows a “Resume agent” button while paused. |
| layout | "auto" | "side" | "stacked" | "auto" | Step log beside the browser (side), below it (stacked), or beside only when the container is ≥ 48rem (auto). |
All native attributes of the root element, className and ref pass through.
Anatomy
- BrowserSessionRoot panel; `data-status`, `data-layout`; container-query context; polite status region.
data-slot="browser-session" - HeaderStatus line: glyph, “Agent is browsing” / “Paused for you”, statusText, done/total, elapsed, actions, Take over / Resume.
data-slot="browser-session-header" - ElapsedMono clock from startedAt.
data-slot="browser-session-elapsed" - BrowserColumn with the chrome and the viewport.
data-slot="browser-session-browser" - ChromeTitle (favicon letter + text) and the address bar.
data-slot="browser-session-chrome" - Addressrole="group" “Address”: lock, host, muted path, in mono.
data-slot="browser-session-address" - Loading2px accent sweep under the chrome while a page loads.
data-slot="browser-session-loading" - ViewportFixed-aspect frame (no layout shift); amber inner edge while paused.
data-slot="browser-session-viewport" - PageHolds children/src; scaled to fit when `viewport` is set; translated by scrollTop.
data-slot="browser-session-page" - Overlayaria-hidden layer for the highlight, ripple and cursor.
data-slot="browser-session-overlay" - HighlightAccent box + mono tag around the target; eases between targets.
data-slot="browser-session-highlight" - CursorPointer arrow with an optional accent name tag; glides between points.
data-slot="browser-session-cursor" - RippleAccent ring that expands and fades after the cursor arrives.
data-slot="browser-session-ripple" - LogScrollable step log; keeps the active step in view.
data-slot="browser-session-log" - Step<li> with `data-kind`, `data-status`, `data-active`; aria-current="step" on the active one.
data-slot="browser-session-step"
Accessibility
- The overlay (cursor, ripple, highlight) is decorative and aria-hidden; what the agent is doing is carried by the step log and the status line.
- The step log is an <ol> labelled “Browser actions”; each step speaks its status after the label, and the active one has aria-current="step".
- A polite status region announces the active step's label (when it is a string), “Paused for you” / your statusText, “Session ended” or “Session failed” — never the ticking clock.
- The address bar is a labelled group; the lock icon is labelled “Secure connection”. With `src`, the image gets alt text (default “Screenshot of {url}”).
- Take over / Resume agent are real buttons with visible focus. Paused state is signalled by text and glyph, not only the amber edge.
- Reduced motion: The cursor and highlight jump instead of gliding (the highlight still fades), the click ripple is skipped, page scroll is instant, the loading sweep becomes a static bar and the shimmer stops.
| Keys | Action |
|---|---|
| Tab | Moves to the status-line actions (Take over / Resume agent) and any controls inside the page. |
Notes for agents
- Coordinates are percentages of the viewport. If your agent reports pixels on a W×H screen, pass `viewport={{ width: W, height: H }}` and use x = px / W * 100.
- For remote browsers pass the latest screenshot as `src` and keep `viewport` equal to the screenshot size; draw the *next* action with `cursor` + `highlight`.
- Change `clickKey` (e.g. an action counter) to replay the ripple; repeated clicks on the same step otherwise only ripple once.
- Set `status="paused"` whenever the agent needs the human (login, 2FA, CAPTCHA, payment) and say why in `statusText`.
- Map the agent's actions to the five kinds; put the URL, selector or typed text in `detail` (mono).