@thelifeisyours/friv (0.1.7)
Installation
@thelifeisyours:registry=https://git.tliy.no/api/packages/thelifeisyours/npm/npm install @thelifeisyours/friv@0.1.7"@thelifeisyours/friv": "0.1.7"About this package
friv
A canvas debugging view for React dev servers. Maps every page of a running app as a live, interactive window on a pannable canvas, arranged as a tree by the links between them, and outlines the windows that actually changed when you edit a file.
The windows are real pages, not screenshots. Hover, click, right-click, type and scroll inside them.
Try it
Two terminals, from the repo root:
pnpm example # demo app on :5173
pnpm build && pnpm friv http://localhost:5173 --root examples/vite-react
Then open http://127.0.0.1:4477/__friv/.
Edit examples/vite-react/src/App.tsx and watch the affected windows outline in
orange. Change the text under /products and only that window lights up; change
the shared button label and all of them do.
Clicking a link inside a window does not navigate that window. It moves you to the window for that route, opening and mapping one first if the route was not already on the canvas. The same applies to the tree on the left.
There is a Next.js example too:
pnpm --filter example-next-app dev # Next app on :3000
pnpm friv http://localhost:3000 --root examples/next-app --port 4478
Letting an agent drive the canvas
The package ships an MCP server, friv-mcp, so a coding agent can start the
canvas and point it at whatever it is working on. .mcp.json registers it for
this repo; elsewhere use the friv-mcp binary, with FRIV_URL set if the
server does not listen on the default port.
| Tool | Purpose |
|---|---|
friv_start |
Launch friv against a dev server on an unusual port |
friv_stop |
Stop a server it started |
friv_status |
What is being mapped, how many routes, whether anyone is watching |
friv_list_routes |
List mapped pages with titles |
friv_focus |
Move the canvas to a route, mapping it first if unknown |
friv_set_follow |
Make the canvas track whichever page a change affects |
friv_open_canvas |
Open the canvas in the user's browser |
friv_rescan |
Re-crawl after adding or removing pages |
Focusing a window does nothing visible if no browser has the canvas open, so
the tools report that rather than claiming success: with no server reachable
they fail with the command to start one, and with no viewer they say so and
point at friv_open_canvas.
Follow mode moves the view on its own. It is exact when the changed file is a route file, since the route comes from the path. Otherwise it relies on pages reporting their own DOM changes, so it moves only when exactly one page reacts, and only among the windows currently live — a page evicted to a screenshot has no document to report with.
Use it on your own project
friv publishes as a single package with no workspace dependencies. It lives in
the Gitea package registry, so map the scope in the consuming project's
.npmrc:
@thelifeisyours:registry=https://git.tliy.no/api/packages/thelifeisyours/npm/
The credential goes in your user ~/.npmrc, never the project one — pnpm
refuses to expand environment variables in registry credentials that come from
a committed file, so that a checked-in .npmrc cannot leak a token to an
attacker-controlled registry:
npm config set //git.tliy.no/api/packages/thelifeisyours/npm/:_authToken <token>
Then:
pnpm add -D @thelifeisyours/friv
pnpm friv http://localhost:5173
--root defaults to the current directory, and a Next.js dev server is
detected automatically. The package ships the canvas prebuilt, so there is no
build step in the consuming project.
To let an agent drive it, register the bundled MCP server:
claude mcp add friv -- npx --package @thelifeisyours/friv friv-mcp
Name the package explicitly. A bare npx friv-mcp only finds the binary inside
a project that already depends on friv; anywhere else npx goes to the public
registry looking for a package called friv-mcp, which is not this one.
Nothing else is needed. Start your Vite or Next dev server, then ask Claude to
show you a page — the tools find the dev server on the usual ports and bring
the canvas up themselves. friv_start exists for pointing at a dev server on
an unusual port.
Publishing
Tagging v* runs .gitea/workflows/publish.yml, which builds, verifies the
tarball installs into a scratch project, and publishes using the
REGISTRY_TOKEN secret. By hand:
pnpm build
pnpm --filter @thelifeisyours/friv publish --no-git-checks
How it works
friv serves its own UI under /__friv and reverse-proxies everything else to
your dev server, so framed pages keep their own absolute URLs and stay
same-origin with the canvas. Frame-blocking headers are stripped and a small
bridge script is injected to report titles, navigation and DOM changes.
Routes are discovered twice: seeded from the filesystem (Next App and Pages
routers, React Router path: literals) and confirmed by a Playwright crawl that
records the real links between pages. The graph, window positions and hidden
flags persist in an embedded Postgres (PGlite) under
node_modules/.cache/friv.
Change attribution works two ways. When a changed file is itself a route file
(app/products/page.tsx), the route is derived from its path and only that
window is outlined. For everything else — shared components, stylesheets,
anything a client-side router owns — no import graph can say which route is
affected, so each framed page reports its own DOM mutations and the canvas
correlates them against file-change events.
Playwright drives your installed Chrome or Edge through playwright-core, so
installing friv does not download a browser.
Layout
| Package | Role |
|---|---|
packages/server |
The published friv package: CLI, MCP server, proxy, crawler, storage |
packages/ui |
The canvas, built into the server package |
Status
Verified against both examples: route discovery, live interactive windows, change outlining, link interception, hide/show, the screenshot tier for idle windows, dragging, layout orientation and persistence.
The scripts/ directory holds the checks used for that, each run against a
live server: vite-check, next-check, link-check, drag-check,
frozen-check, evict-check, layout-check and smoke.
Dependencies
Dependencies
| ID | Version |
|---|---|
| @electric-sql/pglite | ^0.5.8 |
| @fastify/http-proxy | ^11.3.0 |
| @fastify/static | ^8.2.0 |
| @modelcontextprotocol/sdk | ^1.20.2 |
| chokidar | ^4.0.3 |
| citty | ^0.1.6 |
| drizzle-orm | ^0.45.3 |
| fastify | ^5.6.1 |
| picocolors | ^1.1.1 |
| playwright-core | ^1.63.0 |
| ws | ^8.18.3 |
| zod | ^3.25.76 |
Development Dependencies
| ID | Version |
|---|---|
| @types/node | ^24.13.3 |
| @types/ws | ^8.18.1 |
| typescript | ~6.0.2 |