npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@relictombs/opentui-browser

v0.2.0

Published

A fast Electron browser surface for OpenTUI using the Kitty graphics protocol

Readme

@relictombs/opentui-browser

Embed a real browser inside OpenTUI. Electron runs as a hidden offscreen sidecar—there is no visible Electron window—and the selected page is rendered directly into a Kitty-capable terminal.

This project is inspired by zenbu-labs/terminal-browser and its work on making a real browser feel at home inside a terminal interface.

What it provides

  • A focusable BrowserRenderable with keyboard, paste, pointer, wheel, hover, and cursor feedback.
  • Responsive page sizing that follows the OpenTUI surface and terminal resolution.
  • One Electron runtime with one or more long-lived pages.
  • Exact loopback DevTools endpoints and target IDs so an agent and the user can share the same page.
  • Latest-frame scheduling and 1 fps background-page idling to keep rendering work bounded.
  • Effect-native public APIs, typed failures, and scoped services.

There is one rendering path and no system-Chrome or screenshot fallback:

hidden Electron BrowserWindow -> authenticated binary RGBA socket -> in-memory Kitty transmit -> terminal image placement

Requirements

  • Node.js 24 or newer with npm. Bun is not required by consumers and is used only by repository contributors.
  • @opentui/core 0.5.1 (supported range: >=0.5.1 <0.6.0).
  • A local terminal with Kitty graphics support. Ghostty is the primary tested terminal.
  • OpenTUI's "alternate-screen" mode and a direct local renderer.

The public BrowserRenderable path requires Electron and the renderer on one machine. Terminal multiplexers such as tmux or screen and remote terminal attachment are outside this direct image transport's supported baseline. Node applications that create an OpenTUI renderer must start Node with --experimental-ffi.

This package's public BrowserRenderable quick start is the local library path: Electron and the terminal run on the same machine, with no FFmpeg or network transport. For the complete OpenCode setup, including a browser running on a remote server, use npx --yes @relictombs/[email protected] and choose browser or both surfaces. The private remote adapter is deliberately not a second backend inside @relictombs/opentui-browser.

Install

npm install @relictombs/[email protected] @opentui/core

Contributor checkouts use Bun for workspace dependencies and scripts.

Kitty transport, Presentation coordination, and terminal readiness are private bundled implementation packages. They do not need to be installed or published separately.

Electron is a package dependency. If the package manager did not run Electron's install script, the first Managed Browser Runtime launch runs Electron's installer asynchronously in a bounded worker and validates the installed binary before continuing. That first launch needs either a cached binary or network access; later launches use the installed executable directly.

Quick start

import { createCliRenderer } from "@opentui/core"
import { Deferred, Effect } from "effect"
import { BrowserRenderable } from "@relictombs/opentui-browser"

const program = Effect.gen(function* () {
  const quit = yield* Deferred.make<void>()
  const renderer = yield* Effect.tryPromise(() =>
    createCliRenderer({
      screenMode: "alternate-screen",
      exitOnCtrlC: true,
      onDestroy: () => Effect.runFork(Deferred.succeed(quit, undefined)),
    }),
  )
  const browser = new BrowserRenderable(renderer, {
    url: "https://example.com",
    width: "100%",
    height: "100%",
    border: true,
  })
  renderer.root.add(browser)

  yield* Effect.acquireUseRelease(
    Effect.void,
    () =>
      browser.start().pipe(Effect.andThen(Effect.sync(() => browser.focus())), Effect.andThen(Deferred.await(quit))),
    () => browser.close().pipe(Effect.ignore, Effect.ensuring(Effect.sync(() => renderer.destroy()))),
  )
})

await Effect.runPromise(program)

BrowserRenderable exposes goto(), reload(), goBack(), goForward(), stopLoading(), settle(), url, loading, pointerStyle, surfaceMetrics, started, closed, page, and runtime.

Run the included browser with terminal-native address and navigation controls:

bun run example

The example opens example.com. Set OPENTUI_BROWSER_URL to change its initial page. Set OPENTUI_BROWSER_PROFILE_DIR to use a dedicated persistent Electron profile.

Configuration

The most commonly used BrowserRenderable options are:

| Option | Purpose | | ------------------------------------------------------------ | -------------------------------------------------------------------- | | url | Initial URL. Values without a scheme are treated as HTTPS. | | page | Present an existing Shared Browser Page without owning it. | | runtime | Create and own a page inside an existing browser runtime. | | launch | Configure the Electron runtime owned by this Browser Surface. | | maxFps | Maximum terminal presentation rate. Defaults to 60. | | everyNthFrame | Ask Electron to submit every Nth active paint frame. | | maxViewportWidth, maxViewportHeight, maxViewportPixels | Bound the pixel cost of terminal-sized pages. | | showLoadingIndicator | Toggle the built-in loading label. | | onNavigationStateChange | Synchronize an address field, buttons, or a spinner with the page. | | onPointerStyleChange | Observe the page cursor when the host coordinates pointer ownership. |

Common launch options include:

const browser = new BrowserRenderable(renderer, {
  launch: {
    userDataDirectory: "/absolute/path/to/dedicated-profile",
    frameRate: 60,
    readyTimeoutMs: 10_000,
    shutdownTimeoutMs: 2_000,
  },
})

page and runtime are mutually exclusive, and neither can be combined with launch.

Runtime and ownership

ElectronBrowser owns one hidden Electron sidecar, its profile, control connection, binary media connection, DevTools server, browser storage, and pages:

import { ElectronBrowser } from "@relictombs/opentui-browser"
import { Effect } from "effect"

const program = Effect.gen(function* () {
  const runtime = yield* ElectronBrowser.launch({
    userDataDirectory: "/absolute/path/to/dedicated-profile",
  })
  const page = yield* runtime.newPage({ url: "https://example.com" })
  yield* Effect.logInfo("browser page ready", page.url)
}).pipe(Effect.scoped)

await Effect.runPromise(program)

Ownership is explicit:

| Browser Surface input | What the surface closes | | ---------------------- | ---------------------------------------- | | No source, or launch | Its runtime, page, and Presentation | | runtime | The page it creates and its Presentation | | page | Only its Presentation |

Closing a surface never closes a supplied page or runtime. Electron uses a private temporary profile unless userDataDirectory is supplied; owned temporary profiles are deleted during cleanup, while caller-owned profiles are never removed.

DevTools and agents

Every ElectronPage exposes the exact attachment for its live webContents:

const inspectTarget = Effect.gen(function* () {
  const runtime = yield* ElectronBrowser.launch()
  const page = yield* runtime.newPage({ url: "https://example.com" })

  yield* Effect.logInfo("DevTools attachment", {
    page: page.devToolsTarget,
    browser: runtime.webSocketEndpoint,
  })
}).pipe(Effect.scoped)

await Effect.runPromise(inspectTarget)

Use page.devToolsTarget.endpoint as the browser HTTP URL for a CDP client such as Chrome DevTools MCP, then select the listed page whose target ID equals page.devToolsTarget.targetId. runtime.webSocketEndpoint is the browser-level WebSocket endpoint.

The terminal owns presentation and human input while the DevTools client inspects and operates the same page. DevTools does not carry presentation frames, and this package does not embed the interactive Chrome DevTools UI as another terminal pane.

For a complete OpenCode integration with automatic browser-control and Chrome DevTools MCP registration, see @relictombs/opencode-browser.

Effect composition

Scoped services are available from either the root package or the curated @relictombs/opentui-browser/effect entrypoint:

import { Effect } from "effect"
import { BrowserSurface, Electron } from "@relictombs/opentui-browser/effect"

const program = Effect.gen(function* () {
  const electron = yield* Electron.Service
  const surfaces = yield* BrowserSurface.Service
  const page = yield* electron.newPage({ url: "https://example.com" })
  const handle = yield* surfaces.open(renderer, {
    page: page.page,
    width: "100%",
    height: "100%",
  })

  yield* handle.goto("https://effect.website")
  const state = yield* handle.snapshot()
  yield* Effect.logInfo("browser ready", state)
  handle.renderable.focus()
}).pipe(Effect.provide(BrowserSurface.layer), Effect.provide(Electron.layer()), Effect.scoped)

await Effect.runPromise(program)

Electron.layer() scopes the Managed Browser Runtime and every page opened through its Effect handle. BrowserSurface.layer scopes renderer attachment, Presentation ownership, and shutdown. Effect handles expose typed navigation, input, settling, and snapshot operations. Root browser, page, and renderable operations are Effects too; applications run them at their runtime boundary.

BrowserSurface.layer also provides the shared @relictombs/kitty/effect service, so its default graphics path uses schema-decoded protocol operations and scoped placement cleanup. Synchronous OpenTUI callbacks admit work into the same Effect coordinator. Advanced hosts can provide their own Kitty service to BrowserSurface.layerFromKitty.

Performance model

Only a visible page with an active Presentation paints at the configured rate. Pages without a Presentation remain alive but idle at 1 fps. The Browser Surface defaults to a 60 fps presentation ceiling; hosts can lower maxFps, launch.frameRate, viewport area, or use everyNthFrame to trade smoothness for lower CPU and memory traffic.

Producer-driven work stays bounded:

  • the media sender retains one frame awaiting acknowledgement and only the latest pending frame;
  • a Presentation keeps only the latest useful frame;
  • Kitty serializes terminal writes and reconciles them to the latest desired image;
  • pointer moves, wheel input, viewport changes, and placement changes use latest-state reconciliation.

The local Browser path uses raw Kitty direct data because generic Kitty graphics detection does not prove that a terminal safely supports compressed direct data. Large frames yield between bounded terminal writes so input and other host work can run between output batches.

At 60 fps, tightly packed 960×600 RGBA capture has an upper-bound raw rate of about 132 MiB/s; 1920×1080 is about 475 MiB/s. Remote transport applies H.264 after capture instead, and actual work depends on page paints and terminal size.

Limitations and security

  • Popup windows, dialogs, browser permission requests, webviews, and drag-and-drop navigation are disabled.
  • The DevTools server is loopback-only, but any local process that attaches can inspect page cookies and storage.
  • Offscreen pages use Electron sandboxing and context isolation with Node integration disabled.
  • Raw frames never enter the filesystem. A separate one-time-token-authenticated loopback socket has strict bounded framing and is closed with the managed runtime.
  • Keep @relictombs/opentui-browser updated so its pinned Electron runtime receives package updates.

Development

The repository uses Bun for dependency management and scripts while keeping shared runtime modules usable from Bun and Node.js. Run the commands below from packages/opentui/browser in a repository checkout.

| Command | Purpose | | ----------------------- | --------------------------------------------------------- | | bun run build | Build the package and Electron sidecar into dist/. | | bun run typecheck | Run TypeScript without emitting files. | | bun test | Run the test suite. | | bun run test:electron | Build and run the live Electron smoke test. | | bun run check | Run formatting, types, tests, and build. | | bun run test:packed | Pack, install, and verify the published artifact surface. | | bun run bench | Run deterministic hot-path benchmarks. | | bun run example | Build and run the interactive browser example. |

Contributor-level domain language, ownership, and module boundaries live in CONTEXT.md.

Release

Run bun run check, bun run test:packed, and bun run test:electron before tagging. Publishing is tag-driven through .github/workflows/publish.yml: push main, then push the matching @relictombs/opentui-browser@<version> tag. GitHub repeats check and test:packed before publishing through npm trusted publishing.