collin/browser-terminal-extension
RenderedSource
CLAUDE.md
A tmux sidebar for Chrome and Firefox. Two halves that ship separately:
extension/is the browser extension (named terminal). Plain files, no bundler: the browser loads the.js/.css/.htmlon disk as they are.daemon/istermbridge, a Rust CLI that serves a WebSocket on127.0.0.1:7681, spawns a pty, and runs tmux inside it. tmux, not the daemon, is the persistence layer.
See README.md for protocol, pairing, TLS, and the systemd socket activation
story. This file is the build and edit loop.
Build
./build.sh # extension -> dist/chrome and dist/firefox
./run.sh # daemon: cargo build --release, then `termbridge serve`
build.sh copies files into dist/. The browser loads dist/chrome (or
dist/firefox), never extension/. So an edit under extension/ has no effect
until ./build.sh runs and the sidebar (or the extension) is reloaded. This is
the most common reason a change "didn't work".
build.sh copies an explicit list of files. A new file under extension/ or
extension/lib/ will not reach dist/ until it is added to that list.
extension/mock.html is deliberately excluded: it renders the sidebar header in
a plain tab for layout work, outside the extension.
.githooks/post-commit runs build.sh after any commit that touched
extension/, so a committed change is at least in dist/. Enable it once per
clone (hooks live outside the repo, so this is what points git at the tracked
copy):
git config core.hooksPath .githooks
It does not reload the sidebar, and it does nothing for an uncommitted edit —
./build.sh by hand is still the loop while you are working.
Checks
npm test # node --test over extension/lib/*.test.js
npm run check # tsc over both jsconfig projects, noEmit
cd daemon && cargo test
npm run check is type checking only, never a build step. The extension files
are classic scripts sharing one global scope (sidebar.js reaches Themes,
Sanitize, tbPickElement with no imports), so nothing under extension/ may
introduce import/export syntax. There are two tsconfig projects because
there are two global scopes: jsconfig.json (sidebar document) and
jsconfig.sw.json (service worker), both extending jsconfig.base.json.
Daemon tests use /bin/sh rather than tmux where they can, and skip themselves
when tmux is missing; the tmux ones use a private socket so they never touch
your real sessions.
Daemon edit loop
If the daemon is installed as a user service (termbridge install), a rebuilt
binary is picked up on the next activation, but only once the running one exits:
cd daemon && cargo build --release
termbridge reload
Re-run termbridge install instead when the change affects what the unit
encodes (port, idle timeout, pinned session, binary path).
Conventions
- Both manifests (
manifest.chrome.json,manifest.firefox.json) are hand maintained. A permission or file change usually has to land in both. extension/lib/theme.jsis the single source of truth for both palettes: theuiblock becomes CSS custom properties, thextermblock goes toterm.options.theme. Do not hardcode colors insidebar.css; use the variables.sidebar.csscarries long comments explaining why a rule exists (the tab silhouette, the separators, the seam the active tab covers). Keep that style when editing it, and read the surrounding comment before changing a value.extension/vendor/is vendored xterm.js. Do not edit it.