A Ghostty-backed terminal panel for BB. It attaches to a terminal session owned by the current BB thread, keeps the stock BB terminal untouched, and adds file transfer and terminal-specific font controls.
This repository is an early public preview. The plugin ID is
wterm-terminal-preview, so it can coexist with BB's bundled
wterm-terminal while the integration is evaluated.
The latest tag is v0.4.2, and it matches main: mouse-wheel travel is
quantized before SGR forwarding, so a precision trackpad does not scroll a
Herdr pane one notch per tiny browser event. Clickable OSC 8 web/file links
and plain-text link detection are unchanged, as are bounded Kitty Graphics
and the optional full-page Herdr/Wterm launchers.
- Ghostty terminal emulation through
@wterm/ghostty0.5.0 and WebAssembly. - Dark first paint while WASM and the Nerd Font load; leftover TUI colors are not kept on scrollback rows.
- Composer shortcut: a terminal button in the chat footer. If a Wterm tab is already open in this thread, it reveals that panel. If none is open, it creates a new session. On BB hosts that support
experimental_primarySurface, chat collapses so the terminal fills the thread; use Exit Full Screen to return. Packaged BB 0.40.0 does not include that host API, so the same button opens the side panel or session picker instead. - Optional full-page shortcuts in BB's left sidebar. Herdr launches
herdrwith its ram icon; Wterm launches a standalone shell. Each shortcut creates and owns an independent PTY. - Bundled Symbols Nerd Font Mono fallback for Powerline, Starship, and Nerd Font prompt icons; no local font installation is required.
- Every Wterm terminal tab starts an independent thread-scoped terminal. + / a new tab always creates a new PTY. It never silently reopens the previous session from localStorage. Attach to an existing session only from the picker, by clicking that row.
- The session picker distinguishes loading, a failed list (with Retry), and an empty list. Dead linked sessions show as unavailable/exited, not as running.
- Select and restart existing thread-scoped BB terminal sessions when needed.
- Keyboard, resize, wheel, click, and button-drag mouse input for terminal UIs.
- Persistent font size controls from 10px to 24px.
- Native, character-level text selection contained inside the terminal and copied on selection. The copy event is scoped to the terminal, so surrounding BB message rows are never included.
- OSC 8 hyperlinks and plain-text link detection: see Terminal links.
- Kitty Graphics direct PNG/RGB/RGBA output with bounded image storage and upstream placement/scrollback/resize handling.
- OSC 52 clipboard writes from TUIs such as Herdr, plus cell-based copy-on-drag while mouse tracking is enabled. Copy uses a synchronous clipboard write during the pointer gesture so it still works when async clipboard permission is missing.
- File upload by button or drag-and-drop, plus image upload from the clipboard.
- Files are written on the terminal host and their quoted path is inserted at the prompt using bracketed paste.
- Compatibility with BB hosts that expose the legacy terminal WebSocket.
Every clickable thing in the terminal goes through one handler, fed by two sources.
OSC 8 hyperlinks. When a program emits them, the anchor is rendered as
given and clicked as given. Most TUIs only do that when they recognize the
terminal, and a BB terminal session does not identify itself — pi-tui, for
example, stays silent when TERM_PROGRAM is unset, which is the usual case
here.
Plain-text links. Output that arrives as plain text is scanned after each
render, and tokens that are plainly links become the same a.term-link anchors:
https://…/http://…URLs;- absolute paths such as
/var/log/app.logorfile:///tmp/notes.md; - workspace-relative paths such as
docs/README.md; path:lineandpath:line:columnlocations, which open at that position.
The scan runs per row on the rows that changed, inside a 4 ms per-frame budget, so a long scrollback is not held up. It stays deliberately narrow — a false link is worse than a missed one:
- a token must contain a separator and end in something that looks like a file
extension, so
3/4.5,and/orandv1.2.3are left alone; - glob patterns (
**/src/*.ts) are ignored; - a home shorthand (
~/notes.md) is ignored, because it cannot be resolved without the home directory and a literal~/notes.mdwould open the wrong file; - a link that a row wrap cut in two is stitched back together: rows that continue the same line are planned as one text, and each visible fragment becomes an anchor for the whole link. A row that filled its last column continues into the next one; while output is still streaming, a link whose last fragment sits on the final painted row is left alone until the next row settles it.
| Link | Destination |
|---|---|
http(s)://… |
BB's browser preference (navigate.openUrl), with window.open as the fallback. Only http: and https: are ever handed over, re-checked at the sink. |
| Path inside the thread's workspace | BB's file-opener pipeline as a workspace file — the same identity rendered Markdown uses, so per-extension opener preferences apply. |
| Path outside the workspace, or a terminal with no thread workspace | BB's file-opener pipeline as a host file, on the session's host. |
A relative path is resolved against the directory the terminal was opened in
(the panel asks the server for the thread workspace through the
threadFileSource RPC). Segments that would climb out of the workspace fall
back to the host route, and a relative path on a terminal without a workspace is
not opened at all.
BB renders the first applicable file opener for an extension unless one is pinned, so a third-party opener does not win on its own. To open terminal links in the Files plugin, pin it under Settings → Files for the extensions you care about (Automatic restores the built-in choice). BB's own preview and file-manager openers are the defaults for most extensions.
Terminal output is untrusted: any program can print a path or emit OSC 8. Both routes re-validate what they open, so a link can only ask BB to display a page or a file.
- BB
0.35.1or newer with plugin SDK^0.4.1and host file writes. - Single-tab presentation requires a BB build that supports
experimental_claimedTerminalId. Older hosts still work, but also show the same session in a native terminal tab. git,npm, andbbavailable onPATH.- Network access to GitHub and the npm registry during installation.
Install the pinned release:
bb plugin install 'git:github.com/Diffuzmetall/bb-wterm-terminal-plugin@v0.4.2' --yes
bb plugin source wterm-terminal-previewTo run the current main instead of a release tag, install @main.
Open a BB thread and choose Wterm terminal from the new-tab menu. Each activation, including + for another tab, creates a new terminal session. The picker in an existing tab can still attach to a running session.
To add full-page launchers to BB's left sidebar, open this plugin's settings page and enable either or both options under Configuration:
- Show Herdr in the left sidebar — starts
herdrin a full-page Wterm terminal. - Show Wterm in the left sidebar — starts a standalone shell in a full-page Wterm terminal.
Both options are off by default, so installing the plugin does not add sidebar items unless you ask it to. Closing or leaving either page closes only the PTY created by that page; existing Herdr workspaces and agents keep running inside the Herdr runtime.
The chat composer also has a terminal button. If a Wterm tab is already open,
it reveals that panel; otherwise it creates a new session. On BB hosts that
support experimental_primarySurface, it collapses chat so the terminal fills
the pane (Exit Full Screen returns to chat). Packaged BB 0.40.0 ignores that
flag and opens the side panel or picker instead; reloading the plugin cannot add
full-screen Chat ↔ CLI to that app.
Pinned release tags do not advance through bb plugin update. Replace an
installed release explicitly:
bb plugin remove wterm-terminal-preview
bb plugin install 'git:github.com/Diffuzmetall/bb-wterm-terminal-plugin@v0.4.2' --yes
bb plugin source wterm-terminal-previewTo install from the latest commit on main instead of a release tag:
bb plugin install 'git:github.com/Diffuzmetall/bb-wterm-terminal-plugin@main' --yes
bb plugin source wterm-terminal-previewOnce the plugin is installed from main, update it with:
bb plugin update wterm-terminal-preview --yesRelease tags are recommended because a pinned source is reproducible. Updating
a pinned tag, or switching between a tag and main, requires the
remove-and-install sequence above.
- Use the
-and+toolbar buttons to change the terminal font size. The setting is remembered in the browser. - In a normal shell, drag across characters to select text. The completed selection is copied even if the drag ends outside the grid, without copying surrounding BB rows.
- When a TUI such as Herdr has enabled mouse tracking, click and drag are sent to the TUI. Dragging across cells copies the selected cell text immediately; it does not copy whole rendered rows.
Hold
Shiftwhile dragging to use browser-native selection instead. - OSC 52 clipboard writes from the TUI (Herdr copy-on-select, Vim
y, and similar) are applied to the system clipboard during the next terminal click if the browser blocked the original write. Clipboard queries are ignored.
The plugin uses libghostty through @wterm/ghostty as its VT and Unicode
core. @wterm/dom paints that state as an HTML terminal grid, so settings from
the native Ghostty application do not control this embedded terminal. The
bundled Nerd Font fallback supplies prompt icons consistently across machines.
Seeing zsh is normal: it is the shell process running inside the Ghostty-backed
terminal. A separate neighbouring native zsh tab is different; that means the
BB host does not yet support experimental_claimedTerminalId.
Use Upload file, drop a file over the panel, or paste an image. The plugin
sends the bytes through an authenticated BB plugin route and bb.sdk.files to
the host that owns the selected terminal. It does not implement a second SCP or
SSH client.
Uploads are stored under <terminal cwd>/.bb-wterm-uploads/ with randomized
names and mode 0600. The plugin verifies the returned size and SHA-256 before
inserting the path. Images are limited to 10 MiB and other files to 25 MiB.
OSC 8 links emitted by programs inside Wterm are clickable. HTTP(S) links open
through BB's browser preference; absolute file:// links open in BB Files/file
preview. The target must be reachable through the current BB host/workspace.
Test a web link:
printf '\033]8;;https://example.com\033\\WEB_TEST\033]8;;\033\\\n'Test a file link by replacing the path with an existing host file:
printf '\033]8;;file:///absolute/path/to/README.md\033\\FILE_TEST\033]8;;\033\\\n'Wterm also accepts direct Kitty Graphics PNG/RGB/RGBA sequences. A minimal RGB smoke test that displays a green rectangle is:
printf '\033_Ga=T,f=24,s=1,v=1,c=12,r=6,m=0;AP8A\033\\'Uploading an image does not automatically display it as Kitty Graphics: upload stores the file and inserts its path in the shell. A Kitty-compatible client must send the graphics escape sequence, for example for a small PNG:
printf '\033_Ga=T,f=100;' && base64 -w0 ./image.png && printf '\033\\'Large PNG payloads must be chunked according to the Kitty Graphics protocol.
bb plugin list
bb plugin reload wterm-terminal-preview
bb plugin disable wterm-terminal-preview
bb plugin enable wterm-terminal-preview
bb plugin remove wterm-terminal-previewThe two sidebar toggles live on the plugin's settings page under Configuration. Changes apply to the desktop sidebar immediately.
If the BB host daemon restarts and the selected terminal is no longer available, reopen the Wterm panel and select or restart a terminal session.
- The WASM and upload endpoints require BB's per-plugin HTTP token.
- An upload is accepted only when the terminal belongs to the requested thread.
- File writes use the terminal's host and initial working directory as the BB file boundary.
- Upload names are randomized; existing files are not overwritten.
- The repository contains no credentials and the plugin does not persist BB tokens.
Before installing code from main, review the current commit. Prefer a signed
or otherwise trusted release policy for production deployments.
git clone https://github.com/Diffuzmetall/bb-wterm-terminal-plugin.git
cd bb-wterm-terminal-plugin
npm ci
npm test
npm run buildBefore changing dependencies or the checked-in WASM, run the same fail-fast check
used by npm run build:
npm run wterm:preflightIt compares installed @wterm/{core,dom,ghostty,react} package metadata with
package.json and the root package-lock.json, then compares the repository
ghostty-vt.wasm SHA-256 with the installed @wterm/ghostty copy. A mismatch
stops before bb plugin build, so existing dist/ artifacts remain untouched.
The build writes dist/wterm-build-provenance.json with the verified versions
and WASM SHA-256. For safe dependency recovery, reinstall from the committed
lockfile and rerun the check:
npm ci
npm run wterm:preflightDo not bypass the check with a custom WASM; update the pinned dependency and repository asset together.
The standalone tests protect thread scoping, upload limits and integrity, safe host paths, terminal replay ordering, queued input and resize, and detach behavior. They run against this repository's sources without importing the BB workspace.
The build produces the frontend and server bundles in dist/ and copies
ghostty-vt.wasm and the bundled Nerd Font from the repository. Generated
dependencies and build output are intentionally not committed.
About Contributions: Please don't take this the wrong way, but I do not accept outside contributions for any of my projects. I simply don't have the mental bandwidth to review anything, and it's my name on the thing, so I'm responsible for any problems it causes; thus, the risk-reward is highly asymmetric from my perspective. I'd also have to worry about other "stakeholders," which seems unwise for tools I mostly make for myself for free. Feel free to submit issues, and even PRs if you want to illustrate a proposed fix, but know I won't merge them directly. Instead, I'll have Claude or Codex review submissions via gh and independently decide whether and how to address them. Bug reports in particular are welcome. Sorry if this offends, but I want to avoid wasted time and hurt feelings. I understand this isn't in sync with the prevailing open-source ethos that seeks community contributions, but it's the only way I can move at this velocity and keep my sanity.
This standalone plugin is derived from the Wterm integration originally
developed in get-bb/bb, so it preserves that
repository's MIT copyright notice.
The bundled Ghostty WASM renderer comes from
@wterm/ghostty,
which is distributed under the Apache-2.0 license.
The Herdr sidebar mark follows the official
ram.svg from the Apache-2.0-licensed
Herdr project.
SymbolsNerdFontMono-Regular.woff2 comes from Nerd Fonts v3.5.0 and is
distributed under the included MIT license.