LocalLens

Run the browser app

Start the Bun server and chat with brains in the UI.

The browser app is the CLI's longer-lived counterpart. It keeps brains around, supports several at once, and gives you a chat thread per brain.

Start the server

PORT=4180 bun run dev

You should see:

LocalLens is running at http://localhost:4180

Without PORT, the server listens on 3000. Open http://localhost:4180 in any modern browser.

Walk through the demo

  1. Enter a brain name. Something like Sample brain.

  2. Click Choose folder. The browser file picker opens. Select examples/sample-brain from the repo.

  3. Click Create and index. The UI streams progress while files are read, chunked, and embedded.

  4. Once the brain shows up as indexed in the sidebar, type a question into the chat input. For example:

    Why does LocalLens use QWEN3_1_7B_INST_Q4?

  5. Hit Send. The answer streams in with [1], [2] citations, and the sources list at the bottom links each one back to the file.

What's behind the UI

The browser app is plain HTML, JS, and CSS in src/ui/. It talks to the Bun server in src/server.ts over a small JSON API:

MethodPathBodyReturns
GET/api/health{ ok: true, name: "LocalLens" }
GET/api/brains{ brains: Brain[] }
POST/api/brains/from-filesCreateBrainFromFilesInput{ brain: Brain }
POST/api/brains/:id/chat{ question: string }ChatAnswer
DELETE/api/brains/:id204 No Content

Every route lands in LocalLensApp in src/locallens.ts.

Picking a folder in the browser

The <input type="file" webkitdirectory> API gives the browser the folder's contents but not its absolute path. LocalLens stores the brain with a virtual path like browser://my-folder so you can still tell where it came from.

Stopping the server

Ctrl+C triggers the SIGINT handler in src/server.ts. It calls app.close() and tears QVAC down cleanly:

process.on("SIGINT", async () => {
  await app.close();
  process.exit(0);
});

On this page