Skip to content

πŸ„β€β™‚οΈ Next.js 16 web UI for OpenCode - real-time chat with streaming, SSE sync, and React Server Components

Notifications You must be signed in to change notification settings

joelhooks/opencode-vibe

Repository files navigation

opencode-vibe πŸ„β€β™‚οΈ

                                      _      β”‚       _ _
  ___  _ __   ___ _ __   ___ ___   __| | ___ β”‚__   _(_) |__   ___
 / _ \| '_ \ / _ \ '_ \ / __/ _ \ / _` |/ _ \β”‚\ \ / / | '_ \ / _ \
| (_) | |_) |  __/ | | | (_| (_) | (_| |  __/β”‚ \ V /| | |_) |  __/
 \___/| .__/ \___|_| |_|\___\___/ \__,_|\___β”‚  \_/ |_|_.__/ \___|
      |_|                                   β”‚

Next.js 16 rebuild of the OpenCode web application. Real-time chat UI with streaming message display, SSE sync, and React Server Components.

Warning: This project uses Next.js 16 canary - bleeding edge, expect rough edges. Catppuccin-themed because we're not savages.

Quick Start

Prerequisites

  • Bun v1.3+ (required - we don't use npm/pnpm)
  • OpenCode CLI running locally

1. Install Dependencies

bun install

2. Start OpenCode (Any Mode)

The web UI discovers running OpenCode processes automatically. Use whatever mode you want:

# TUI mode (interactive terminal)
cd /path/to/your/project
opencode

# Or serve mode (headless)
opencode serve

Run as many as you want, in different directories. The web UI finds them all.

3. Start the Web UI

# From the opencode-next root directory
bun dev

This starts the Next.js dev server on port 8423.

4. Open in Browser

Navigate to: http://localhost:8423

You should see the OpenCode web interface with your sessions.


Features

  • Multi-server discovery - Finds all running OpenCode processes (TUIs, serves) automatically via lsof
  • Cross-process messaging - Send from web UI, appears in your TUI. Routes to the server that owns the session
  • Real-time streaming - Messages stream in as the AI generates them
  • SSE sync - All updates pushed via Server-Sent Events, merged from all discovered servers
  • Slash commands - Type / for actions like /fix, /test, /refactor
  • File references - Type @ to fuzzy-search and attach files as context
  • Catppuccin theme - Latte (light) / Mocha (dark) with proper syntax highlighting

Architecture

Zero-config server discovery. The web UI finds all running OpenCode processes automatically.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                        YOUR MACHINE                             β”‚
β”‚                                                                 β”‚
β”‚   Terminal 1          Terminal 2          Terminal 3            β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”            β”‚
β”‚   β”‚opencode β”‚        β”‚opencode β”‚        β”‚opencode β”‚            β”‚
β”‚   β”‚  tui    β”‚        β”‚  tui    β”‚        β”‚ serve   β”‚            β”‚
β”‚   β”‚ :4096   β”‚        β”‚ :5123   β”‚        β”‚ :6421   β”‚            β”‚
β”‚   β”‚ ~/foo   β”‚        β”‚ ~/bar   β”‚        β”‚ ~/baz   β”‚            β”‚
β”‚   β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜        β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜        β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜            β”‚
β”‚        β”‚                  β”‚                  β”‚                  β”‚
β”‚        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                  β”‚
β”‚                           β”‚                                     β”‚
β”‚                    β”Œβ”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”                              β”‚
β”‚                    β”‚    lsof     β”‚  discovers all               β”‚
β”‚                    β”‚   + verify  β”‚  opencode processes          β”‚
β”‚                    β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜                              β”‚
β”‚                           β”‚                                     β”‚
β”‚                           β–Ό                                     β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚   β”‚                 WEB UI (:8423)                           β”‚  β”‚
β”‚   β”‚                                                          β”‚  β”‚
β”‚   β”‚   ~/foo sessions ──┐                                     β”‚  β”‚
β”‚   β”‚   ~/bar sessions ──┼── all projects, one view            β”‚  β”‚
β”‚   β”‚   ~/baz sessions β”€β”€β”˜                                     β”‚  β”‚
β”‚   β”‚                                                          β”‚  β”‚
β”‚   β”‚   send message β†’ routes to server that owns the session  β”‚  β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚                                                                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

How Discovery Works

  1. API route runs lsof to find processes listening on TCP with "bun" or "opencode" in the command
  2. Hits /project endpoint on each candidate to verify it's actually OpenCode
  3. Opens SSE stream to each verified server
  4. Events include directory field β†’ routes to correct project in the store

The cool part: Send a message from the web UI and it appears in your TUI. The web discovers which server owns the session and routes there.


Configuration

Mostly unnecessary - discovery handles it. But if you need overrides:

# apps/web/.env.local

# Fallback URL if discovery finds nothing (default: http://localhost:4056)
NEXT_PUBLIC_OPENCODE_URL=http://localhost:4056

# Force a specific directory (optional)
NEXT_PUBLIC_OPENCODE_DIRECTORY=/path/to/your/project

Development

Project Structure

opencode-next/
β”œβ”€β”€ apps/
β”‚   └── web/                    # Next.js 16 application
β”‚       β”œβ”€β”€ src/
β”‚       β”‚   β”œβ”€β”€ app/            # App Router pages
β”‚       β”‚   β”‚   β”œβ”€β”€ page.tsx    # Session list
β”‚       β”‚   β”‚   └── session/
β”‚       β”‚   β”‚       └── [id]/   # Session detail view
β”‚       β”‚   β”œβ”€β”€ components/     # UI components
β”‚       β”‚   β”‚   β”œβ”€β”€ ai-elements/  # Chat UI components
β”‚       β”‚   β”‚   └── ui/           # Shared UI primitives
β”‚       β”‚   β”œβ”€β”€ core/           # SDK client setup
β”‚       β”‚   β”œβ”€β”€ lib/            # Utilities
β”‚       β”‚   └── react/          # React hooks & providers
β”‚       β”‚       β”œβ”€β”€ provider.tsx      # OpenCodeProvider
β”‚       β”‚       β”œβ”€β”€ store.ts          # Zustand store
β”‚       β”‚       β”œβ”€β”€ use-sse.tsx       # SSE connection hook
β”‚       β”‚       β”œβ”€β”€ use-session.ts    # Session data hook
β”‚       β”‚       β”œβ”€β”€ use-messages.ts   # Messages hook
β”‚       β”‚       └── use-send-message.ts # Send message hook
β”‚       └── package.json
β”œβ”€β”€ docs/
β”‚   β”œβ”€β”€ adr/                    # Architecture Decision Records
β”‚   └── guides/                 # Implementation guides
β”œβ”€β”€ package.json                # Root package.json
└── turbo.json                  # Turborepo config

Code Tour

Start here to understand the codebase:

1. Server Discovery (apps/web/src/app/api/opencode-servers/route.ts)

The magic that finds all running OpenCode processes. Uses lsof to find TCP listeners, hits /project to verify they're OpenCode, returns the list. Called on page load.

2. SSE Connection (apps/web/src/react/use-multi-server-sse.ts)

Opens SSE streams to ALL discovered servers simultaneously. Events include a directory field that routes updates to the correct project in the store. This is how TUI ↔ Web sync works.

3. Zustand Store (apps/web/src/react/store.ts)

Central state management. Directory-scoped (each project has isolated state). Handles SSE events via handleEvent() which dispatches to specific handlers for sessions, messages, parts, etc. Uses Immer for immutable updates.

4. Message Transform (apps/web/src/lib/transform-messages.ts)

Converts OpenCode SDK types β†’ ai-elements UIMessage format. The SDK returns {info, parts} envelopes; this flattens them for rendering. Also handles tool state mapping.

5. Session Page (apps/web/src/app/session/[id]/page.tsx)

Server Component that fetches initial data. Uses limit=20 for fast initial load (pagination). Passes data to client components for hydration.

6. Session Messages (apps/web/src/app/session/[id]/session-messages.tsx)

Client Component that renders the message list. Hydrates Zustand store on first render, then subscribes to real-time updates. Uses memoization to prevent re-renders during streaming.

7. Prompt Input (apps/web/src/components/prompt/PromptInput.tsx)

The input box. Handles slash commands (/), file references (@), and message sending. Autocomplete powered by fuzzy search over commands and files.

8. AI Elements (apps/web/src/components/ai-elements/)

Chat UI components: Message, Tool, Reasoning, Conversation, etc. Adapted from Vercel's ai-elements patterns. Each component handles its own streaming states.

Available Scripts

# Development
bun dev                 # Start Next.js dev server (port 8423 = VIBE)
bun build               # Production build
bun start               # Start production server

# Code Quality
bun run typecheck       # TypeScript check (via turbo, checks all packages)
bun lint                # Run oxlint
bun format              # Format with Biome
bun format:check        # Check formatting

# Testing
bun test                # Run tests
bun test --watch        # Watch mode

React Hooks

The web UI provides several hooks for interacting with OpenCode. All hooks use the Effect-based router for type-safe, composable request handling with built-in timeouts, retries, and error handling.

import {
  useSession, // Get session data
  useMessages, // Get messages for a session
  useSendMessage, // Send a message (uses caller internally)
  useSessionStatus, // Get session status (idle/busy/error)
  useProviders, // List available AI providers (uses caller internally)
  useOpenCode, // Access the caller directly
} from "@/react";

// Example: Display session messages
function SessionView({ sessionId }: { sessionId: string }) {
  const session = useSession(sessionId);
  const messages = useMessages(sessionId);
  const { send, isPending } = useSendMessage(sessionId);
  const status = useSessionStatus(sessionId);

  return (
    <div>
      <h1>{session?.title}</h1>
      <div>Status: {status}</div>
      {messages.map((msg) => (
        <Message key={msg.id} message={msg} />
      ))}
      <input
        onKeyDown={(e) => {
          if (e.key === "Enter") {
            send(e.currentTarget.value);
          }
        }}
        disabled={isPending}
      />
    </div>
  );
}

// Example: Using the caller directly
function CustomComponent() {
  const { caller } = useOpenCode();

  const handleClick = async () => {
    // Type-safe route invocation with built-in timeout
    const session = await caller("session.create", { title: "New Session" });
    console.log(session);
  };

  return <button onClick={handleClick}>Create Session</button>;
}

Troubleshooting

"No servers discovered"

# Check what's actually running
lsof -iTCP -sTCP:LISTEN -P -n 2>/dev/null | grep -E 'bun|opencode'

Should show at least one process. If not, start OpenCode somewhere.

"No sessions showing"

  1. OpenCode needs to be running in a project directory
  2. Check browser console for discovery/SSE errors
  3. Try the discovery endpoint directly: curl http://localhost:8423/api/opencode-servers

"Messages not updating"

Check SSE connections in DevTools β†’ Network β†’ filter by "event". Should see active streams to discovered servers.


Tech Stack

Layer Technology Why
Runtime Bun Fast all-in-one runtime
Framework Next.js 16 React Server Components, App Router
Bundler Turbopack Next-gen bundler
Language TypeScript 5+ Type safety
Linting oxlint Fast Rust-based linter
Formatting Biome Fast formatter
Styling Tailwind CSS Utility-first CSS
State Zustand Lightweight state management
SDK @opencode-ai/sdk OpenCode API client

Documentation


Contributing

  1. Use Bun (not npm/pnpm)
  2. Follow TDD: RED β†’ GREEN β†’ REFACTOR
  3. Run bun format before committing
  4. Check bun lint passes

License

MIT

About

πŸ„β€β™‚οΈ Next.js 16 web UI for OpenCode - real-time chat with streaming, SSE sync, and React Server Components

Topics

Resources

Stars

Watchers

Forks