No description
  • TypeScript 45.9%
  • Go 44.3%
  • CSS 9.2%
  • Dockerfile 0.4%
  • HTML 0.2%
Find a file
2026-06-02 10:52:00 -06:00
client feat: Cmd+W/Ctrl+W closes editor tab instead of browser tab 2026-06-02 09:59:43 -06:00
server fix: SSE reloads open tabs on external changes, server shuts down cleanly 2026-06-02 09:09:31 -06:00
.gitignore chore: add package-lock.json, ignore tsbuildinfo 2026-06-01 19:18:08 -06:00
AGENTS.md fix: SSE reloads open tabs on external changes, server shuts down cleanly 2026-06-02 09:09:31 -06:00
Dockerfile migrate to bun, clean up node/npm artifacts 2026-06-01 23:05:03 -06:00
mise.toml feat: Cmd+W/Ctrl+W closes editor tab instead of browser tab 2026-06-02 09:59:43 -06:00
README.md docs: update readme to point to real docker image 2026-06-02 10:52:00 -06:00

Pipit

A lightweight web-based file browser and code editor. Browse and edit files through a familiar split-pane IDE interface — file tree on the left, editor on the right.

Go backend, React frontend. Monaco editor with syntax highlighting. Live-reloads when files change on disk.

Quick Start

docker pull git.w33ble.com/w33ble/pipit:latest
docker run -p 8080:8080 -v /path/to/your/project:/workspace git.w33ble.com/w33ble/pipit:latest

Open http://localhost:8080.

Development

Prerequisites: Go 1.23+, Bun

# Terminal 1 — backend
cd server
go run .

# Terminal 2 — frontend (with hot reload)
cd client
bun install
bun run dev

The Vite dev server proxies /api requests to the Go backend at http://localhost:8080, so open http://localhost:5173.

Configuration

All settings via environment variables with a PIPIT_ prefix. Everything has a default.

Variable Default Description
PIPIT_PORT 8080 Server listen port
PIPIT_ROOT_DIR . (current directory) Root directory to serve files from
PIPIT_BEARER_TOKEN (empty) If set, requires Authorization: Bearer <token> on all API requests
PIPIT_CORS_ORIGIN * Allowed CORS origin

Auth

When PIPIT_BEARER_TOKEN is set, the UI shows a login screen. Enter the token to connect. The token is stored in localStorage and persists across tab closes.

When PIPIT_BEARER_TOKEN is empty (default), no authentication is required.

API

All paths are relative to PIPIT_ROOT_DIR. Path traversal (../) is rejected.

Method Endpoint Description
GET /api/health Health check
GET /api/tree?path=/ List directory contents
GET /api/file?path=/foo.ts Read file contents
PUT /api/file?path=/foo.ts Save file (body = raw content)
POST /api/file?path=/new.ts Create empty file
DELETE /api/file?path=/foo.ts Delete file
POST /api/dir?path=/newdir Create directory
DELETE /api/dir?path=/olddir Delete directory (must be empty)
POST /api/rename Rename file/directory (JSON: {"oldPath","newPath"})
GET /api/events SSE stream of file change events

SSE Events

The /api/events endpoint streams real-time file changes. Events are JSON:

{"type":"created","path":"src/new.ts"}
{"type":"modified","path":"src/app.tsx"}
{"type":"deleted","path":"src/old.ts"}

Tech Stack

Backend: Go standard library (net/http 1.22+ routing) + fsnotify for file watching

Frontend:

  • React 19 + TypeScript
  • TanStack Router (typed context)
  • TanStack Query v5 (server state)
  • Monaco Editor (@monaco-editor/react)
  • react-resizable-panels (split pane layout)

Everything: Tailwind-free, no CSS framework. Dark theme using CSS custom properties.

Project Structure

server/          Go backend
  main.go        Entry point, middleware stack, routes
  internal/
    config/      Env var parsing (PIPIT_ prefix)
    handler/     REST + SSE endpoints
    middleware/  CORS, auth, logging, path safety
    watcher/     fsnotify with debounce dedup

client/          React frontend (Vite)
  src/
    components/  FileTree, EditorTabs, MonacoEditor, Toolbar, LoginGate, Toast
    hooks/       useFileTree, useFileContent, useFileOps, useOpenTabs, useSSE
    lib/         API client, language detection

Testing

# Backend
cd server && go test ./... -v -count=1

# Frontend
cd client && bun run test