Remove docs-site, add PostHog analytics to web (#66)

- Delete docs-site/ (superseded by web/app/docs)
- Add posthog-js with Vercel reverse proxy at /cdata to bypass adblockers
- Track pageviews (SPA-aware), download clicks (hero/navbar/mobile_drawer),
  and GitHub link clicks (hero/navbar/mobile_drawer/footer)
This commit is contained in:
Lawrence Chen 2026-02-18 04:00:04 -08:00 committed by GitHub
parent fdd8029c00
commit cb86322e98
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
41 changed files with 190 additions and 8535 deletions

35
docs-site/.gitignore vendored
View file

@ -1,35 +0,0 @@
# dependencies
node_modules
.pnpm-store
# next.js
.next/
out/
# production
build
dist
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# local env files
.env*.local
.env
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
# fumadocs
.source/

View file

@ -1,58 +0,0 @@
# cmux Documentation
Documentation website for [cmux](https://github.com/manaflow-ai/cmux), built with [Fumadocs](https://fumadocs.vercel.app) and Next.js.
## Development
```bash
# Install dependencies
npm install
# Start dev server
npm run dev
```
Open [http://localhost:3000](http://localhost:3000) to view the docs.
## Deployment
This site is deployed to Vercel. Push to main to trigger a deployment.
### Manual Deploy
```bash
npm run build
npx vercel --prod
```
## Structure
```
docs-site/
├── app/ # Next.js app router
│ ├── docs/ # Documentation pages
│ └── page.tsx # Landing page
├── content/
│ └── docs/ # MDX documentation files
└── lib/
└── source.ts # Fumadocs source configuration
```
## Adding Documentation
1. Create a new `.mdx` file in `content/docs/`
2. Add frontmatter with title and description
3. Add the page to `content/docs/meta.json`
Example:
```mdx
---
title: My Page
description: Description of my page
---
# My Page
Content here...
```

View file

@ -1,46 +0,0 @@
import { source } from '@/lib/source';
import {
DocsPage,
DocsBody,
DocsDescription,
DocsTitle,
} from 'fumadocs-ui/page';
import { notFound } from 'next/navigation';
import defaultMdxComponents from 'fumadocs-ui/mdx';
export default async function Page(props: {
params: Promise<{ slug?: string[] }>;
}) {
const params = await props.params;
const page = source.getPage(params.slug);
if (!page) notFound();
const MDX = page.data.body;
return (
<DocsPage toc={page.data.toc} full={page.data.full}>
<DocsTitle>{page.data.title}</DocsTitle>
<DocsDescription>{page.data.description}</DocsDescription>
<DocsBody>
<MDX components={{ ...defaultMdxComponents }} />
</DocsBody>
</DocsPage>
);
}
export async function generateStaticParams() {
return source.generateParams();
}
export async function generateMetadata(props: {
params: Promise<{ slug?: string[] }>;
}) {
const params = await props.params;
const page = source.getPage(params.slug);
if (!page) notFound();
return {
title: page.data.title,
description: page.data.description,
};
}

View file

@ -1,20 +0,0 @@
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
import type { ReactNode } from 'react';
import { source } from '@/lib/source';
export default function Layout({ children }: { children: ReactNode }) {
return (
<DocsLayout
tree={source.pageTree}
nav={{
title: 'cmux',
url: '/',
}}
sidebar={{
defaultOpenLevel: 1,
}}
>
{children}
</DocsLayout>
);
}

View file

@ -1,11 +0,0 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
:root {
--fd-background-card: theme(colors.zinc.50);
}
.dark {
--fd-background-card: theme(colors.zinc.900);
}

View file

@ -1,23 +0,0 @@
import './global.css';
import { RootProvider } from 'fumadocs-ui/provider';
import { Inter } from 'next/font/google';
import type { ReactNode } from 'react';
const inter = Inter({
subsets: ['latin'],
});
export const metadata = {
title: 'cmux Documentation',
description: 'Documentation for cmux - A Ghostty-based terminal for AI agents',
};
export default function Layout({ children }: { children: ReactNode }) {
return (
<html lang="en" className={inter.className} suppressHydrationWarning>
<body className="flex flex-col min-h-screen">
<RootProvider>{children}</RootProvider>
</body>
</html>
);
}

View file

@ -1,327 +0,0 @@
---
title: Changelog
description: Release notes and version history for cmux
---
All notable changes to cmux are documented here.
## [0.38.1] - 2026-02-18
### Fixed
- Right-click and menubar lag in production builds (rebuilt with macOS 26.2 SDK)
## [0.38.0] - 2026-02-18
### Added
- Double-clicking the sidebar title-bar area now zooms/maximizes the window
### Fixed
- Browser omnibar `Cmd+L` now reliably refreshes/selects-all and supports immediate typing without stale inline text
- Omnibar inline completion no longer replaces typed prefixes with mismatched suggestion text
## [0.37.0] - 2026-02-17
### Added
- "+" button on the tab bar for quickly creating new terminal or browser tabs
## [0.36.0] - 2026-02-17
### Fixed
- App hang when omnibar safety timeout failed to fire (blocked main thread)
- Tab drag/drop not working when multiple workspaces exist
- Clicking in browser WebView not focusing the browser tab
## [0.35.0] - 2026-02-17
### Fixed
- App hang when clicking browser omnibar (NSTextView tracking loop spinning forever)
- White flash when creating new browser panels
- Tab drag/drop broken when dragging over WebView panes
- Stale drag timeout cancelling new drags of the same tab
- 88% idle CPU from infinite makeFirstResponder loop
- Terminal keys (arrows, Ctrl+N/P) swallowed after opening browser
- Cmd+N swallowed by browser omnibar navigation
- Split focus stolen by re-entrant becomeFirstResponder during reparenting
## [0.34.0] - 2026-02-16
### Fixed
- Browser not loading localhost URLs correctly
## [0.33.0] - 2026-02-16
### Fixed
- Menubar and general UI lag in production builds
- Sidebar tabs getting extra left padding when update pill is visible
- Memory leak when middle-clicking to close tabs
## [0.32.0] - 2026-02-16
### Added
- Sidebar metadata: git branch, listening ports, log entries, progress bars, and status pills
### Fixed
- localhost and 127.0.0.1 URLs not resolving correctly in the browser panel
### Changed
- `browser open` now targets the caller's workspace by default via CMUX_WORKSPACE_ID
## [0.31.0] - 2026-02-15
### Added
- Arrow key navigation in browser omnibar suggestions
- Browser zoom shortcuts (Cmd+/-, Cmd+0 to reset)
- "Install Update and Relaunch" menu item when an update is available
### Changed
- Open browser shortcut remapped from Cmd+Shift+B to Cmd+Shift+L
- Flash focused panel shortcut remapped from Cmd+Shift+L to Cmd+Shift+H
- Update pill now shows only in the sidebar footer
### Fixed
- Omnibar inline completion showing partial domain (e.g. "news." instead of "news.ycombinator.com")
## [0.30.0] - 2026-02-15
### Fixed
- Update pill not appearing when sidebar is visible in Release builds
## [0.29.0] - 2026-02-15
### Added
- Cmd+click on links in the browser opens them in a new tab
- Right-click context menu shows "Open Link in New Tab" instead of "Open in New Window"
- Third-party licenses bundled in app with Licenses button in About window
- Update availability pill now visible in Release builds
### Changed
- Cmd+[/] now triggers browser back/forward when a browser panel is focused (no-op on terminal)
- Reload configuration shortcut changed to Cmd+Shift+,
- Improved browser omnibar suggestions and focus behavior
## [0.28.2] - 2026-02-14
### Fixed
- Sparkle updates from `0.27.0` could fail to detect newer releases because release build numbers were behind the latest published appcast build number
- Release GitHub Action failed on repeat runs when `SUPublicEDKey` / `SUFeedURL` already existed in `Info.plist`
## [0.28.1] - 2026-02-14
### Fixed
- Release build failure caused by debug-only helper symbols referenced in non-debug code paths
## [0.28.0] - 2026-02-14
### Added
- Optional nightly update channel in Settings (`Receive Nightly Builds`)
- Automated nightly build and publish workflow for `main` when new commits are available
### Changed
- Settings and About windows now use the updated transparent titlebar styling and aligned controls
- Repository license changed to GNU AGPLv3
### Fixed
- Terminal panes freezing after repeated split churn
- Finder service directory resolution now normalizes paths consistently
## [0.27.0] - 2026-02-11
### Fixed
- Muted traffic lights and toolbar items on macOS 14 (Sonoma) caused by `clipsToBounds` default change
- Toolbar buttons (sidebar, notifications, new tab) disappearing after toggling sidebar with Cmd+B
- Update check pill not appearing in titlebar on macOS 14 (Sonoma)
## [0.26.0] - 2026-02-11
### Fixed
- Muted traffic lights and toolbar items in focused window caused by background blur in themeFrame
- Sidebar showing two different textures near the titlebar on older macOS versions
## [0.25.0] - 2026-02-11
### Fixed
- Blank terminal on macOS 26 (Tahoe) — two additional code paths were still clearing the window background, bypassing the initial fix
- Blank terminal on macOS 15 caused by background blur view covering terminal content
## [0.24.0] - 2026-02-09
### Changed
- Update bundle identifier to `com.cmuxterm.app` for consistency
## [0.23.0] - 2026-02-09
### Changed
- Rename app to cmux — new app name, socket paths, Homebrew tap, and CLI binary name (bundle ID remains `com.cmuxterm.app` for Sparkle update continuity)
- Sidebar now shows tab status as text instead of colored dots, with instant git HEAD change detection
### Fixed
- CLI `set-status` command not properly quoting values or routing `--tab` flag
## [0.22.0] - 2026-02-09
### Fixed
- Xcode and system environment variables (e.g. DYLD, LANGUAGE) leaking into terminal sessions
## [0.21.0] - 2026-02-09
### Fixed
- Zsh autosuggestions not working with shared history across terminal panes
## [0.17.3] - 2025-02-05
### Fixed
- Auto-update not working (Sparkle EdDSA signing was silently failing due to SUPublicEDKey missing from Info.plist)
## [0.17.1] - 2025-02-05
### Fixed
- Auto-update not working (Sparkle public key was missing from release builds)
## [0.17.0] - 2025-02-05
### Fixed
- Traffic lights (close/minimize/zoom) not showing on macOS 13-15
- Titlebar content overlapping traffic lights and toolbar buttons when sidebar is hidden
## [0.16.0] - 2025-02-04
### Added
- Sidebar blur effect with withinWindow blending for a polished look
- `--panel` flag for `new-split` command to control split pane placement
## [0.15.0] - 2025-01-30
### Fixed
- Typing lag caused by redundant render loop
## [0.14.0] - 2025-01-30
### Added
- Setup script for initializing submodules and building dependencies
- Contributing guide for new contributors
### Fixed
- Terminal focus when scrolling with mouse/trackpad
### Changed
- Reload scripts are more robust with better error handling
## [0.13.0] - 2025-01-29
### Added
- Customizable keyboard shortcuts via Settings
### Fixed
- Find panel focus and search alignment with Ghostty behavior
### Changed
- Sentry environment now distinguishes between production and dev builds
## [0.12.0] - 2025-01-29
### Fixed
- Handle display scale changes when moving between monitors
### Changed
- Fix SwiftPM cache handling for release builds
## [0.11.0] - 2025-01-29
### Added
- Notifications documentation for AI agent integrations
### Changed
- App and tooling updates
## [0.10.0] - 2025-01-29
### Added
- Sentry SDK for crash reporting
- Documentation site with Fumadocs
- Homebrew installation support (`brew install --cask cmux`)
- Auto-update Homebrew cask on release
### Fixed
- High CPU usage from notification system
- Release workflow SwiftPM cache issues
### Changed
- New tabs now insert after current tab and inherit working directory
## [0.9.0] - 2025-01-29
### Changed
- Normalized window controls appearance
- Added confirmation panel when closing windows with active processes
## [0.8.0] - 2025-01-29
### Fixed
- Socket key input handling
- OSC 777 notification sequence support
### Changed
- Customized About window
- Restricted titlebar accessories for cleaner appearance
## [0.7.0] - 2025-01-29
### Fixed
- Environment variable and terminfo packaging issues
- XDG defaults handling
## [0.6.0] - 2025-01-28
### Fixed
- Terminfo packaging for proper terminal compatibility
## [0.5.0] - 2025-01-28
### Added
- Sparkle updater cache handling
- Ghostty fork documentation
## [0.4.0] - 2025-01-28
### Added
- cmux CLI with socket control modes
- NSPopover-based notifications
### Fixed
- Notarization and codesigning for embedded CLI
- Release workflow reliability
### Changed
- Refined titlebar controls and variants
- Clear notifications on window close
## [0.3.0] - 2025-01-28
### Added
- Debug scrollback tab with smooth scroll wheel
- Mock update feed UI tests
- Dev build branding and reload scripts
### Fixed
- Notification focus handling and indicators
- Tab focus for key input
- Update UI error details and pill visibility
### Changed
- Renamed app to cmux
- Improved CI UI test stability
## [0.1.0] - 2025-01-28
### Added
- Sparkle auto-update flow
- Titlebar update UI indicator
## [0.0.x] - 2025-01-28
Initial releases with core terminal functionality:
- GPU-accelerated terminal rendering via Ghostty
- Tab management with native macOS UI
- Split pane support
- Keyboard shortcuts
- Socket API for automation

View file

@ -1,83 +0,0 @@
---
title: Claude Code Integration
description: Automatic notifications and status tracking for Claude Code sessions
---
# Claude Code Integration
cmux automatically integrates with [Claude Code](https://docs.anthropic.com/en/docs/claude-code) to show session status and notifications in the sidebar — no manual configuration needed.
## How It Works
When you run `claude` (or `clawd`) inside a cmux terminal, cmux automatically:
1. **Injects a session ID** so each Claude invocation is tracked independently
2. **Registers hooks** for `SessionStart`, `Stop`, and `Notification` events
3. **Routes notifications** to the correct workspace and surface in the sidebar
This works by placing a thin wrapper script ahead of the real `claude` binary in your PATH. The wrapper adds `--session-id` and `--settings` flags, then `exec`s the real Claude — no extra process, no config files modified.
<Callout type="info">
The wrapper only activates inside cmux terminals (when `CMUX_SURFACE_ID` is set). Running `claude` in any other terminal works normally without hooks.
</Callout>
## What You See
### Session Status
When a Claude session is active, the sidebar shows a status indicator:
- **Running** — Claude is working
- **Needs input** — Claude is waiting for your approval or response
### Notifications
When Claude finishes or needs attention, a notification appears in the sidebar with context:
- **Permission** — Claude needs approval for a tool use
- **Error** — Something went wrong
- **Completed** — Session finished, with a summary of Claude's last response
## Disabling
You can turn off the integration in **Settings > Automation > Claude Code Integration**. When disabled, `claude` runs without cmux hooks — no session tracking, no sidebar status, no notifications.
New terminals pick up the setting immediately; existing terminals keep their current state.
<Callout type="info">
You can also set `CMUX_CLAUDE_HOOKS_DISABLED=1` in your shell environment to disable per-terminal.
</Callout>
## Custom Hooks
The automatic integration covers `SessionStart`, `Stop`, and `Notification` events. If you want to add hooks for other Claude Code events (like `PostToolUse` or `PreToolUse`), add them to your `~/.claude/settings.json` as usual — cmux's hooks are merged additively and won't conflict.
### Example: Notify on Task Agent Completion
```json
{
"hooks": {
"PostToolUse": [
{
"matcher": "Task",
"hooks": [
{
"type": "command",
"command": "cmux notify --title 'Claude Code' --subtitle 'Agent Finished' --body 'Task agent completed'",
"timeout": 5
}
]
}
]
}
}
```
## Technical Details
- The wrapper lives at `Resources/bin/claude` inside the app bundle
- It is prepended to PATH via cmux's shell integration (runs after `.zshrc`/`.bashrc`)
- Each invocation gets a fresh UUID session ID
- The `cmux claude-hook` CLI subcommand handles routing hook payloads to the correct workspace
- Session mappings are stored at `~/.cmuxterm/claude-hook-sessions.json` (auto-pruned after 7 days)
- The `Stop` hook reads the Claude session transcript (via `transcript_path`) to include the last assistant message in the completion notification

View file

@ -1,222 +0,0 @@
---
title: CLI Reference
description: Command-line interface for cmux
---
# CLI Reference
cmux includes a command-line tool for controlling the terminal from scripts and other tools.
## Installation
The CLI is bundled with cmux. Inside cmux terminals, it's available automatically. For external use:
```bash
# Create symlink to CLI
sudo ln -sf "/Applications/cmux.app/Contents/MacOS/cmux" /usr/local/bin/cmux
```
## Global Options
| Option | Description |
|--------|-------------|
| `--socket PATH` | Use a custom socket path |
| `--json` | Output in JSON format |
| `--tab ID` | Target a specific tab |
| `--panel ID` | Target a specific panel |
## Environment Variables
| Variable | Description |
|----------|-------------|
| `CMUX_SOCKET_PATH` | Default socket path |
| `CMUX_TAB_ID` | Default tab ID |
| `CMUX_PANEL_ID` | Default panel ID |
## Commands
### Tab Management
#### list-tabs
List all open tabs.
```bash
cmux list-tabs
cmux list-tabs --json
```
#### new-tab
Create a new tab.
```bash
cmux new-tab
```
#### select-tab
Switch to a specific tab.
```bash
cmux select-tab --tab <tab-id>
```
#### current-tab
Get the current tab info.
```bash
cmux current-tab
cmux current-tab --json
```
#### close-tab
Close a tab.
```bash
cmux close-tab --tab <tab-id>
```
### Split Management
#### new-split
Create a new split pane.
```bash
cmux new-split right
cmux new-split down
cmux new-split left
cmux new-split up
```
#### list-panels
List panels in the current tab.
```bash
cmux list-panels
cmux list-panels --json
```
#### focus-panel
Focus a specific panel.
```bash
cmux focus-panel --panel <panel-id>
```
### Input Commands
#### send
Send text to the terminal.
```bash
cmux send "echo hello"
cmux send "ls -la\n" # Include newline to execute
```
#### send-key
Send a key press.
```bash
cmux send-key enter
cmux send-key tab
cmux send-key escape
```
#### send-panel
Send text to a specific panel.
```bash
cmux send-panel --panel <panel-id> "command"
```
#### send-key-panel
Send a key press to a specific panel.
```bash
cmux send-key-panel --panel <panel-id> enter
```
### Notifications
#### notify
Send a notification.
```bash
cmux notify --title "Title" --body "Message body"
cmux notify --title "Title" --subtitle "Subtitle" --body "Body"
```
#### list-notifications
List all notifications.
```bash
cmux list-notifications
cmux list-notifications --json
```
#### clear-notifications
Clear all notifications.
```bash
cmux clear-notifications
```
### Utility
#### ping
Check if cmux is running and responsive.
```bash
cmux ping
```
## Examples
### Build Script with Notification
```bash
#!/bin/bash
npm run build
if [ $? -eq 0 ]; then
cmux notify --title "✓ Build Success" --body "Ready to deploy"
else
cmux notify --title "✗ Build Failed" --body "Check the logs"
fi
```
### Create Tab and Run Command
```bash
#!/bin/bash
# Create a new tab
result=$(cmux new-tab --json)
tab_id=$(echo "$result" | jq -r '.id')
# Select the new tab
cmux select-tab --tab "$tab_id"
# Send a command
cmux send "npm run dev\n"
```
### Monitor Multiple Tabs
```bash
#!/bin/bash
# List all tabs and their directories
cmux list-tabs --json | jq -r '.tabs[] | "\(.title): \(.directory)"'
```

View file

@ -1,93 +0,0 @@
---
title: Concepts
description: Understanding cmux's window, workspace, pane, and surface hierarchy
---
# Concepts
cmux organizes your terminals in a four-level hierarchy. Understanding these levels helps when using the socket API, CLI, and keyboard shortcuts.
## Hierarchy
```
Window
└── Workspace (sidebar entry)
└── Pane (split region)
└── Surface (tab within pane)
└── Panel (terminal or browser content)
```
### Window
A macOS window. You can open multiple windows with **⌘⇧N**. Each window has its own sidebar with independent workspaces.
### Workspace
A sidebar entry. Each workspace contains one or more split panes. Workspaces are what you see listed in the left sidebar.
In the UI and keyboard shortcuts, workspaces are often called "tabs" since they behave like tabs in the sidebar. The socket API and environment variables use the term "workspace".
| Context | Term Used |
|---------|-----------|
| Sidebar UI | Tab |
| Keyboard shortcuts | Workspace or tab |
| Socket API | `workspace` |
| Environment variable | `CMUX_WORKSPACE_ID` |
**Shortcuts:** **⌘N** (new), **⌘1**-**⌘9** (jump), **⌘⇧W** (close), **⌘⌃[** / **⌘⌃]** (prev/next)
### Pane
A split region within a workspace. Created by splitting with **⌘D** (right) or **⌘⇧D** (down). Navigate between panes with **⌘⌥** + arrow keys.
Each pane can hold multiple surfaces (tabs within the pane).
### Surface
A tab within a pane. Each pane has its own tab bar and can hold multiple surfaces. Created with **⌘T**, navigated with **⌘⇧[** / **⌘⇧]** or **⌃1**-**⌃9**.
Surfaces are the individual terminal or browser sessions you interact with. Each surface has its own `CMUX_SURFACE_ID` environment variable.
### Panel
The content inside a surface. Currently two types:
- **Terminal** - A Ghostty terminal session
- **Browser** - An embedded web view
Panel is mostly an internal concept. In the socket API and CLI, you interact with surfaces rather than panels directly.
## Visual Example
```
┌──────────────────────────────────────────────────────┐
│ ┌──────────┐ ┌─────────────────────────────────────┐ │
│ │ Sidebar │ │ Workspace "dev" │ │
│ │ │ │ │ │
│ │ │ │ ┌───────────────┬─────────────────┐ │ │
│ │ > dev │ │ │ Pane 1 │ Pane 2 │ │ │
│ │ server │ │ │ [S1] [S2] │ [S1] │ │ │
│ │ logs │ │ │ │ │ │ │
│ │ │ │ │ Terminal │ Terminal │ │ │
│ │ │ │ │ │ │ │ │
│ │ │ │ └───────────────┴─────────────────┘ │ │
│ └──────────┘ └─────────────────────────────────────┘ │
└──────────────────────────────────────────────────────┘
```
In this example:
- The **window** contains a sidebar with three workspaces (dev, server, logs)
- **Workspace "dev"** is selected, showing two **panes** side by side
- **Pane 1** has two **surfaces** ([S1] and [S2] in the tab bar), with S1 active
- **Pane 2** has one surface
- Each surface contains a **panel** (a terminal in this case)
## Summary Table
| Level | What It Is | Created By | Identified By |
|-------|-----------|------------|---------------|
| Window | macOS window | **⌘⇧N** | -- |
| Workspace | Sidebar entry | **⌘N** | `CMUX_WORKSPACE_ID` |
| Pane | Split region | **⌘D** / **⌘⇧D** | Pane ID (socket API) |
| Surface | Tab within pane | **⌘T** | `CMUX_SURFACE_ID` |
| Panel | Terminal or browser | Automatic | Panel ID (internal) |

View file

@ -1,128 +0,0 @@
---
title: Configuration
description: Configure cmux appearance and behavior
---
# Configuration
cmux reads configuration from Ghostty config files, giving you familiar options if you're coming from Ghostty.
## Config File Locations
cmux looks for configuration in these locations (in order):
1. `~/.config/ghostty/config`
2. `~/Library/Application Support/com.mitchellh.ghostty/config`
Create the config file if it doesn't exist:
```bash
mkdir -p ~/.config/ghostty
touch ~/.config/ghostty/config
```
## Appearance Options
### Font
```ini
font-family = JetBrains Mono
font-size = 14
```
### Colors
```ini
# Theme (or use individual colors below)
theme = Dracula
# Custom colors
background = #1e1e2e
foreground = #cdd6f4
cursor-color = #f5e0dc
cursor-text = #1e1e2e
selection-background = #585b70
selection-foreground = #cdd6f4
```
### Split Panes
```ini
# Opacity for unfocused splits (0.0 to 1.0)
unfocused-split-opacity = 0.7
# Fill color for unfocused splits
unfocused-split-fill = #1e1e2e
# Divider color between splits
split-divider-color = #45475a
```
## Behavior Options
### Scrollback
```ini
# Number of lines to keep in scrollback buffer
scrollback-limit = 10000
```
### Working Directory
```ini
# Default directory for new terminals
working-directory = ~/Projects
```
## App Settings
In-app settings are available via **cmux → Settings** (⌘,):
### Theme Mode
Choose between:
- **System** - Follow macOS appearance
- **Light** - Always light mode
- **Dark** - Always dark mode
### Automation Mode
Control socket access level:
- **Off** - No socket control (most secure)
- **Notifications only** - Only allow notification commands
- **Full control** - Allow all socket commands
<Callout type="warn">
On shared machines, consider using "Notifications only" mode to prevent other processes from controlling your terminals.
</Callout>
### Update Channel
- **Stable** (default) - Production releases only
- **Nightly builds** - Opt in to nightly main-branch builds
Nightly builds can include incomplete features or regressions; use them if you want the latest changes quickly.
## Example Config
Here's a complete example configuration:
```ini
# Font
font-family = SF Mono
font-size = 13
# Colors
theme = One Dark
# Scrollback
scrollback-limit = 50000
# Splits
unfocused-split-opacity = 0.85
split-divider-color = #3e4451
# Working directory
working-directory = ~/code
```

View file

@ -1,179 +0,0 @@
---
title: Environment Variables
description: Environment variables for configuring cmux
---
# Environment Variables
cmux uses environment variables for configuration and integration.
## Socket Control
### CMUX_SOCKET_PATH
Override the default socket path.
```bash
export CMUX_SOCKET_PATH=/custom/path/cmux.sock
```
Default: `/tmp/cmux.sock` (release) or `/tmp/cmux-debug.sock` (debug)
### CMUX_SOCKET_ENABLE
Enable or disable the control socket.
```bash
export CMUX_SOCKET_ENABLE=1 # Enable
export CMUX_SOCKET_ENABLE=0 # Disable
```
Values: `1`, `0`, `true`, `false`, `yes`, `no`
### CMUX_SOCKET_MODE
Override the socket access mode.
```bash
export CMUX_SOCKET_MODE=cmuxOnly # cmux processes only (default)
export CMUX_SOCKET_MODE=allowAll # Allow any local process (no ancestry check)
export CMUX_SOCKET_MODE=off # Disabled
```
## CLI Context
### CMUX_TAB_ID
Default tab ID for CLI commands.
```bash
export CMUX_TAB_ID=abc123
cmux send "hello" # Sends to tab abc123
```
cmux automatically sets this in each terminal session.
### CMUX_PANEL_ID
Default panel ID for CLI commands.
```bash
export CMUX_PANEL_ID=xyz789
cmux send-panel "hello" # Sends to panel xyz789
```
cmux automatically sets this for each split pane.
## Terminal Environment
cmux sets these variables in terminal sessions:
### TERM
Terminal type for compatibility.
```bash
TERM=xterm-ghostty
```
### TERM_PROGRAM
Identifies the terminal application.
```bash
TERM_PROGRAM=ghostty
```
<Callout type="info">
cmux sets this to `ghostty` since it's built on Ghostty. Use the socket check to distinguish from regular Ghostty.
</Callout>
### GHOSTTY_RESOURCES_DIR
Path to Ghostty resources.
```bash
GHOSTTY_RESOURCES_DIR=/path/to/resources
```
## Testing & Debug
### CMUX_UI_TEST_SHOW_SETTINGS
Show the settings window on app launch (for UI testing).
```bash
export CMUX_UI_TEST_SHOW_SETTINGS=1
```
### CMUX_UI_TEST_TRIGGER_UPDATE_CHECK
Trigger an update check on app launch (for testing).
```bash
export CMUX_UI_TEST_TRIGGER_UPDATE_CHECK=1
```
### CMUX_COMMIT
Override the commit hash displayed in the About window.
```bash
export CMUX_COMMIT=abc1234
```
## Detection Script
Check if running inside cmux:
```bash
#!/bin/bash
is_cmux() {
# Check for cmux socket
if [ -S "${CMUX_SOCKET_PATH:-/tmp/cmux.sock}" ]; then
return 0
fi
return 1
}
if is_cmux; then
echo "Running in cmux"
echo "Tab ID: ${CMUX_TAB_ID:-unknown}"
echo "Panel ID: ${CMUX_PANEL_ID:-unknown}"
else
echo "Not in cmux"
fi
```
## Shell Configuration
Add to your `~/.bashrc` or `~/.zshrc`:
```bash
# cmux integration
if [ -S "${CMUX_SOCKET_PATH:-/tmp/cmux.sock}" ]; then
# We're in cmux
# Function to notify on long commands
notify_done() {
"$@"
local exit_code=$?
cmux notify --title "Command Complete" --body "$1"
return $exit_code
}
# Alias for builds
alias build='notify_done npm run build'
fi
```
## Precedence
Environment variables override app settings:
1. Environment variable (if set)
2. App settings (Settings window)
3. Default value
For example, if `CMUX_SOCKET_MODE=cmuxOnly` is set, it overrides the app's Automation Mode setting.

View file

@ -1,60 +0,0 @@
---
title: Introduction
description: cmux - A lightweight native macOS terminal with vertical tabs and notifications for AI coding agents
---
# cmux
cmux is a lightweight, native macOS terminal built on Ghostty for managing multiple AI coding agents. It features vertical tabs, a notification panel, and a socket-based control API.
## Why cmux?
- **Native macOS app** - Built with Swift and AppKit, not Electron. Fast startup, low memory usage, and native look and feel.
- **Lightweight** - Small binary, minimal resource footprint. No bundled browser engine.
- **GPU-accelerated** - Powered by libghostty for buttery smooth rendering.
When running AI coding agents like Claude Code, Codex, or similar tools, you need to:
- **Manage multiple terminal sessions** - Each agent runs in its own terminal
- **Know when agents need attention** - Agents waiting for input should notify you
- **Automate terminal control** - Programmatically create tabs, send input, and more
cmux solves these with:
- **Vertical sidebar tabs** - All terminals visible at a glance
- **OSC 99/777 notifications** - Desktop alerts when agents need you
- **Unix socket API** - Full programmatic control for automation
## Quick Start
```bash
brew tap manaflow-ai/cmux
brew install --cask cmux
```
Or [download the DMG](https://github.com/manaflow-ai/cmux/releases/latest/download/cmux-macos.dmg) directly.
## Key Features
<Cards>
<Card title="Concepts" href="/concepts">
Understanding windows, workspaces, panes, and surfaces
</Card>
<Card title="Vertical Tabs" href="/tabs">
All workspaces in a resizable sidebar with keyboard navigation
</Card>
<Card title="Notifications" href="/notifications">
Desktop alerts via OSC 99/777 sequences when agents need attention
</Card>
<Card title="Socket API" href="/socket-api">
Unix socket for programmatic control - create tabs, send input, and more
</Card>
<Card title="Claude Code Hooks" href="/claude-code-hooks">
Send notifications from Claude Code when tasks complete
</Card>
</Cards>
## Requirements
- macOS 13.0 or later
- Apple Silicon or Intel Mac

View file

@ -1,70 +0,0 @@
---
title: Installation
description: How to install cmux on macOS
---
# Installation
## Homebrew (Recommended)
Install via Homebrew:
```bash
brew tap manaflow-ai/cmux
brew install --cask cmux
```
To update later:
```bash
brew upgrade --cask cmux
```
## Manual Download
Download the latest release from GitHub:
<a href="https://github.com/manaflow-ai/cmux/releases/latest/download/cmux-macos.dmg">
<img src="/macos-badge.png" alt="Download cmux for macOS" width="180" />
</a>
Then:
1. Open the downloaded `.dmg` file
2. Drag **cmux** to your **Applications** folder
3. Open cmux from Applications
<Callout type="info">
On first launch, macOS may ask you to confirm opening an app from an identified developer. Click **Open** to proceed.
</Callout>
## Verify Installation
Open cmux and you should see:
- A terminal window with a vertical tab sidebar on the left
- One initial tab already open
- The Ghostty-powered terminal ready for input
## CLI Tool
cmux includes a command-line tool for automation. It's bundled inside the app and works automatically when you run commands inside cmux.
To use the CLI from outside cmux, you can create a symlink:
```bash
sudo ln -sf "/Applications/cmux.app/Contents/MacOS/cmux" /usr/local/bin/cmux
```
Then you can run commands like:
```bash
cmux list-tabs
cmux notify --title "Build Complete" --body "Your build finished successfully"
```
## Updates
cmux checks for updates automatically using Sparkle. When an update is available, you'll see an update pill in the titlebar. Click it to install the latest version.
You can also check for updates manually via **cmux → Check for Updates** in the menu bar.

View file

@ -1,100 +0,0 @@
---
title: Keyboard Shortcuts
description: Complete list of cmux keyboard shortcuts
---
# Keyboard Shortcuts
## Workspace (Sidebar Tab) Management
| Shortcut | Action |
|----------|--------|
| **⌘N** | New workspace |
| **⌘1** - **⌘9** | Jump to workspace 1-9 |
| **⌘⌃]** | Next workspace |
| **⌘⌃[** | Previous workspace |
| **⌘⇧W** | Close workspace |
| **⌘B** | Toggle sidebar |
## Surface (Tab Within Pane) Management
| Shortcut | Action |
|----------|--------|
| **⌘T** | New surface |
| **⌘⇧]** | Next surface |
| **⌘⇧[** | Previous surface |
| **⌃1** - **⌃9** | Jump to surface 1-9 |
| **⌃Tab** | Next surface |
| **⌃⇧Tab** | Previous surface |
| **⌘W** | Close surface (or workspace if last) |
## Split Panes
| Shortcut | Action |
|----------|--------|
| **⌘D** | Split right |
| **⌘⇧D** | Split down |
| **⌘⌥←** | Focus left pane |
| **⌘⌥→** | Focus right pane |
| **⌘⌥↑** | Focus pane above |
| **⌘⌥↓** | Focus pane below |
| **⌘⇧H** | Flash focused panel |
## Notifications
| Shortcut | Action |
|----------|--------|
| **⌘⇧U** | Jump to latest unread notification |
| **⌘I** | Show notifications panel |
## Browser
| Shortcut | Action |
|----------|--------|
| **⌘⇧L** | Open browser in split |
| **⌘L** | Focus browser address bar |
| **⌘[** | Back |
| **⌘]** | Forward |
| **⌘R** | Reload page |
| **⌘⌥I** | Open Developer Tools |
## Window
| Shortcut | Action |
|----------|--------|
| **⌘⇧N** | New window |
| **⌘,** | Settings |
| **⌘⇧,** | Reload configuration |
| **⌘Q** | Quit |
| **⌘M** | Minimize |
| **⌘H** | Hide |
## Find
| Shortcut | Action |
|----------|--------|
| **⌘F** | Find |
| **⌘G** / **⌘⇧G** | Find next / previous |
| **⌘⇧F** | Hide find bar |
| **⌘E** | Use selection for find |
## Terminal
| Shortcut | Action |
|----------|--------|
| **⌘K** | Clear scrollback |
| **⌘C** | Copy (when text selected) |
| **⌘V** | Paste |
| **⌘+** | Increase font size |
| **⌘-** | Decrease font size |
| **⌘0** | Reset font size |
## Symbol Key
| Symbol | Key |
|--------|-----|
| ⌘ | Command |
| ⌥ | Option/Alt |
| ⌃ | Control |
| ⇧ | Shift |
| ← → ↑ ↓ | Arrow keys |

View file

@ -1,23 +0,0 @@
{
"title": "Documentation",
"pages": [
"index",
"---Getting Started---",
"installation",
"configuration",
"concepts",
"---Features---",
"tabs",
"notifications",
"splits",
"keyboard-shortcuts",
"---Automation---",
"socket-api",
"cli",
"claude-code-hooks",
"---Reference---",
"osc-sequences",
"environment-variables",
"changelog"
]
}

View file

@ -1,127 +0,0 @@
---
title: Notifications
description: Desktop notifications in cmux for AI agents
---
# Notifications
cmux supports desktop notifications, allowing AI agents and scripts to alert you when they need attention.
## How It Works
1. A process in the terminal sends an OSC escape sequence
2. cmux parses the notification title and body
3. If the terminal isn't focused, a desktop notification appears
4. The tab shows an unread badge
## Sending Notifications
### Using the CLI
The simplest way to send a notification:
```bash
cmux notify --title "Task Complete" --body "Your build finished"
```
With a subtitle:
```bash
cmux notify --title "Claude Code" --subtitle "Waiting" --body "Agent needs input"
```
### Using OSC Sequences
You can send notifications directly using escape sequences:
#### OSC 99 (Kitty Protocol)
```bash
# Basic notification
printf '\e]99;i=1;e=1;d=0:Hello World\e\\'
# With title and body
printf '\e]99;i=1;e=1;d=0;p=title:My Title\e\\'
printf '\e]99;i=1;e=1;d=0;p=body:Message body here\e\\'
```
#### OSC 777 (RXVT Protocol)
```bash
printf '\e]777;notify;My Title;Message body here\a'
```
See [OSC Sequences](/docs/osc-sequences) for full format documentation.
## Notification Behavior
### Suppression
Notifications are suppressed (no desktop alert) when:
- The cmux window is focused
- The specific tab sending the notification is active
- The notification panel is open
This prevents duplicate alerts when you're already looking at the terminal.
### Notification Panel
Press **⌘I** to open the notification panel, showing all notifications:
- Click a notification to jump to that tab
- Notifications are marked as read when viewed
- Clear all with the clear button
### Quick Jump
Press **⌘⇧U** to jump directly to the tab with the most recent unread notification.
## Notification Lifecycle
1. **Received** - Notification appears in panel, desktop alert fires (if not suppressed)
2. **Unread** - Badge shown on tab
3. **Read** - Cleared when you view that tab
4. **Cleared** - Removed from panel
## Integration Examples
### Notify After Long Command
```bash
# Add to your shell config
notify-after() {
"$@"
local exit_code=$?
if [ $exit_code -eq 0 ]; then
cmux notify --title "✓ Command Complete" --body "$1"
else
cmux notify --title "✗ Command Failed" --body "$1 (exit $exit_code)"
fi
return $exit_code
}
# Usage
notify-after npm run build
```
### Notify When Build Finishes
```bash
npm run build && cmux notify --title "Build Success" --body "Ready to deploy"
```
### Watch Script Completion
```bash
#!/bin/bash
# long-running-task.sh
do_work() {
# ... your task
sleep 10
}
do_work
cmux notify --title "Task Complete" --body "long-running-task.sh finished"
```

View file

@ -1,204 +0,0 @@
---
title: OSC Sequences
description: OSC escape sequence reference for cmux notifications
---
# OSC Sequences
cmux supports two OSC (Operating System Command) escape sequence standards for sending notifications from terminal programs.
## OSC 99 (Kitty Protocol)
The Kitty notification protocol provides rich notification support with multiple parameters.
### Format
```
ESC ] 99 ; <params> ; <payload> ESC \
```
Or using BEL terminator:
```
ESC ] 99 ; <params> ; <payload> BEL
```
### Parameters
Parameters are semicolon-separated key=value pairs:
| Parameter | Description | Values |
|-----------|-------------|--------|
| `i` | Notification ID | Any string |
| `e` | Event type | `1` = new notification |
| `d` | Done flag | `0` = more data coming, `1` = complete |
| `p` | Payload type | `title`, `body`, `subtitle` |
### Payload
After the final semicolon, the payload contains the notification content. If `p=` is specified, the payload is interpreted as that type.
### Examples
**Simple notification:**
```bash
printf '\e]99;i=1;e=1;d=0:Hello World\e\\'
```
**Notification with title and body:**
```bash
# Set title
printf '\e]99;i=1;e=1;d=0;p=title:Build Complete\e\\'
# Set body
printf '\e]99;i=1;e=1;d=1;p=body:All tests passed\e\\'
```
**With subtitle:**
```bash
printf '\e]99;i=1;e=1;d=0;p=title:Claude Code\e\\'
printf '\e]99;i=1;e=1;d=0;p=subtitle:Session abc123\e\\'
printf '\e]99;i=1;e=1;d=1;p=body:Waiting for input\e\\'
```
### Shell Function
```bash
notify_osc99() {
local title="$1"
local body="$2"
local id="${3:-1}"
printf '\e]99;i=%s;e=1;d=0;p=title:%s\e\\' "$id" "$title"
printf '\e]99;i=%s;e=1;d=1;p=body:%s\e\\' "$id" "$body"
}
# Usage
notify_osc99 "Build Done" "Your project compiled successfully"
```
## OSC 777 (RXVT Protocol)
The RXVT/urxvt protocol is simpler, with a fixed format for title and body.
### Format
```
ESC ] 777 ; notify ; <title> ; <body> BEL
```
Or with ST terminator:
```
ESC ] 777 ; notify ; <title> ; <body> ESC \
```
### Examples
**Basic notification:**
```bash
printf '\e]777;notify;Hello;World\a'
```
**Build completion:**
```bash
printf '\e]777;notify;Build Complete;All 42 tests passed\a'
```
**With ST terminator:**
```bash
printf '\e]777;notify;Task Done;Ready for review\e\\'
```
### Shell Function
```bash
notify_osc777() {
local title="$1"
local body="$2"
printf '\e]777;notify;%s;%s\a' "$title" "$body"
}
# Usage
notify_osc777 "Download Complete" "File saved to ~/Downloads"
```
## Comparison
| Feature | OSC 99 | OSC 777 |
|---------|--------|---------|
| Title | ✓ | ✓ |
| Body | ✓ | ✓ |
| Subtitle | ✓ | ✗ |
| Notification ID | ✓ | ✗ |
| Complexity | Higher | Lower |
| Compatibility | Kitty, cmux | RXVT, cmux |
## Recommendations
- **Use OSC 777** for simple title + body notifications
- **Use OSC 99** when you need subtitles or notification IDs
- **Use the CLI** (`cmux notify`) for the easiest integration
## Testing
Test notifications in your terminal:
```bash
# OSC 777 (simpler)
printf '\e]777;notify;Test;This is a test notification\a'
# OSC 99 (with subtitle)
printf '\e]99;i=test;e=1;d=0;p=title:Test Notification\e\\'
printf '\e]99;i=test;e=1;d=0;p=subtitle:Subtitle here\e\\'
printf '\e]99;i=test;e=1;d=1;p=body:This is the body text\e\\'
```
## Integration with Other Tools
### tmux
If using tmux inside cmux, enable passthrough:
```bash
# In .tmux.conf
set -g allow-passthrough on
```
Then wrap sequences:
```bash
printf '\ePtmux;\e\e]777;notify;Title;Body\a\e\\'
```
### SSH
Notifications work over SSH if your SSH client supports OSC passthrough. Most modern terminals do.
### Python
```python
import sys
def notify(title: str, body: str):
"""Send OSC 777 notification."""
sys.stdout.write(f'\x1b]777;notify;{title};{body}\x07')
sys.stdout.flush()
notify("Script Complete", "Processing finished")
```
### Node.js
```javascript
function notify(title, body) {
process.stdout.write(`\x1b]777;notify;${title};${body}\x07`);
}
notify('Build Done', 'webpack finished');
```

View file

@ -1,263 +0,0 @@
---
title: Socket API
description: Unix socket API for programmatic control of cmux
---
# Socket API
cmux exposes a Unix socket for programmatic control, enabling automation and integration with tools like Claude Code.
## Socket Location
| Build | Socket Path |
|-------|-------------|
| Release | `/tmp/cmux.sock` |
| Debug | `/tmp/cmux-debug.sock` |
You can override the path with the `CMUX_SOCKET_PATH` environment variable.
## Access Modes
cmux has three access modes, configurable in Settings:
| Mode | Description |
|------|-------------|
| **Off** | Socket disabled |
| **Notifications only** | Only notification commands allowed |
| **Full control** | All commands enabled |
<Callout type="warn">
On shared machines, use "Notifications only" mode to prevent other users from controlling your terminals.
</Callout>
## Protocol
Commands are sent as newline-terminated JSON:
```json
{"command": "command-name", "arg1": "value1"}
```
Responses are newline-terminated JSON:
```json
{"success": true, "data": {...}}
{"success": false, "error": "Error message"}
```
## Commands Reference
### Tab Management
#### list-tabs
List all open tabs.
```json
{"command": "list-tabs"}
```
Response:
```json
{
"success": true,
"tabs": [
{"id": "uuid", "title": "Tab 1", "directory": "/Users/..."}
]
}
```
#### new-tab
Create a new tab.
```json
{"command": "new-tab"}
```
#### select-tab
Switch to a specific tab.
```json
{"command": "select-tab", "id": "tab-uuid"}
```
#### current-tab
Get the currently active tab.
```json
{"command": "current-tab"}
```
#### close-tab
Close a specific tab.
```json
{"command": "close-tab", "id": "tab-uuid"}
```
### Split Management
#### new-split
Create a new split pane.
```json
{"command": "new-split", "direction": "right"}
```
Direction options: `left`, `right`, `up`, `down`
#### list-panels
List all panes in the current tab.
```json
{"command": "list-panels"}
```
#### focus-panel
Focus a specific pane.
```json
{"command": "focus-panel", "id": "panel-uuid"}
```
### Input Control
#### send
Send text input to a terminal.
```json
{"command": "send", "text": "echo hello\n"}
```
#### send-key
Send a key press.
```json
{"command": "send-key", "key": "enter"}
```
Key options: `enter`, `tab`, `escape`, `backspace`, `delete`, `up`, `down`, `left`, `right`
#### send-panel
Send text to a specific panel.
```json
{"command": "send-panel", "id": "panel-uuid", "text": "ls -la\n"}
```
#### send-key-panel
Send a key press to a specific panel.
```json
{"command": "send-key-panel", "id": "panel-uuid", "key": "enter"}
```
### Notifications
#### notify
Send a notification.
```json
{
"command": "notify",
"title": "Build Complete",
"subtitle": "Project X",
"body": "Build finished successfully"
}
```
#### list-notifications
List all notifications.
```json
{"command": "list-notifications"}
```
#### clear-notifications
Clear all notifications.
```json
{"command": "clear-notifications"}
```
### App Control
#### ping
Check if the socket is responsive.
```json
{"command": "ping"}
```
Response:
```json
{"success": true, "pong": true}
```
#### set-app-focus
Simulate app focus/unfocus (for testing).
```json
{"command": "set-app-focus", "focused": true}
```
## Example: Python Client
```python
import socket
import json
def send_command(cmd):
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.connect('/tmp/cmux.sock')
sock.send(json.dumps(cmd).encode() + b'\n')
response = sock.recv(4096).decode()
sock.close()
return json.loads(response)
# List tabs
tabs = send_command({"command": "list-tabs"})
print(tabs)
# Send notification
send_command({
"command": "notify",
"title": "Hello",
"body": "From Python!"
})
```
## Example: Shell Script
```bash
#!/bin/bash
# Function to send commands
cmux_cmd() {
echo "$1" | nc -U /tmp/cmux.sock
}
# List tabs
cmux_cmd '{"command": "list-tabs"}'
# Send notification
cmux_cmd '{"command": "notify", "title": "Done", "body": "Task complete"}'
```

View file

@ -1,111 +0,0 @@
---
title: Split Panes
description: Working with split panes in cmux
---
# Split Panes
cmux supports splitting terminal panes within a tab, allowing you to view multiple terminals side by side.
## Creating Splits
### Keyboard Shortcuts
| Shortcut | Action |
|----------|--------|
| **⌘D** | Split right |
| **⌘⇧D** | Split down |
### Using the CLI
```bash
# Split in a specific direction
cmux new-split right
cmux new-split down
cmux new-split left
cmux new-split up
```
## Navigating Splits
### Keyboard
Use **⌘⌥** + arrow keys to move between splits:
| Shortcut | Action |
|----------|--------|
| **⌘⌥←** | Focus left pane |
| **⌘⌥→** | Focus right pane |
| **⌘⌥↑** | Focus pane above |
| **⌘⌥↓** | Focus pane below |
### CLI
```bash
# List all panes in current tab
cmux list-panels
# Focus a specific pane
cmux focus-panel --panel <panel-id>
```
## Closing Splits
- **⌘W** - Close the focused pane
- If only one pane remains, **⌘W** closes the tab
## Visual Styling
Unfocused panes are visually distinguished to help you identify the active pane.
### Configuration
In your Ghostty config:
```ini
# Dim unfocused panes (0.0 = fully dimmed, 1.0 = no dimming)
unfocused-split-opacity = 0.7
# Background fill for unfocused panes
unfocused-split-fill = #1e1e2e
# Color of the divider between panes
split-divider-color = #45475a
```
## Split Layout
cmux uses a binary split tree structure:
- Each split creates two child panes
- Nested splits create a tree of panes
- Each pane has a unique `panel-id` for CLI access
### Example Layout
```
┌────────────────────────────────┐
│ Tab 1 │
├──────────────┬─────────────────┤
│ │ Pane 2 │
│ Pane 1 ├─────────────────┤
│ │ Pane 3 │
└──────────────┴─────────────────┘
```
This layout is created by:
1. Split right → Pane 1 | Pane 2
2. Focus Pane 2
3. Split down → Pane 2 | Pane 3
## Sending Input to Specific Panes
With the CLI, you can send input to specific panes:
```bash
# Send text to a specific pane
cmux send-panel --panel <panel-id> "echo hello"
# Send a keypress to a specific pane
cmux send-key-panel --panel <panel-id> enter
```

View file

@ -1,72 +0,0 @@
---
title: Vertical Tabs
description: Managing terminal tabs in cmux
---
# Vertical Tabs
cmux displays all your terminal tabs in a vertical sidebar on the left side of the window, making it easy to see and switch between multiple terminals at a glance.
## Sidebar Features
- **Always visible** - All tabs shown at once, no hidden tabs
- **Resizable** - Drag the sidebar edge to resize (140-360pt)
- **Current directory** - Each tab shows its working directory
- **Notification badges** - Unread notification indicators
- **Keyboard navigation** - Quick switching without mouse
## Tab Operations
### Create New Tab
- **⌘T** or **⌘N** - Create a new tab
### Close Tab
- **⌘⇧W** - Close the current tab
- **⌘W** - Close the current pane (closes tab if only one pane)
### Switch Tabs
| Shortcut | Action |
|----------|--------|
| **⌘1** - **⌘9** | Jump to tab 1-9 |
| **⌘⌃]** | Next tab |
| **⌘⌃[** | Previous tab |
### Jump to Notifications
- **⌘⇧U** - Jump to the tab with the latest unread notification
- **⌘I** - Show the notifications panel
## Tab Titles
Tab titles are determined by:
1. **Terminal title** - Set via OSC escape sequence
2. **Current directory** - Shown if no title is set
3. **Tab number** - Fallback display
You can set a custom tab title from within the terminal:
```bash
# Set tab title
echo -e "\033]0;My Custom Title\007"
# Or using printf
printf "\033]0;Build Server\007"
```
## Toggle Sidebar
- **⌘B** - Toggle the sidebar visibility
When the sidebar is hidden, you can still use keyboard shortcuts to navigate between tabs.
## Tab Indicators
Tabs display visual indicators:
- **Blue dot** - Unread notification in this tab
- **Bold title** - Currently selected tab
- **Directory path** - Current working directory for the tab

View file

@ -1,8 +0,0 @@
import { docs, meta } from '@/.source';
import { createMDXSource } from 'fumadocs-mdx';
import { loader } from 'fumadocs-core/source';
export const source = loader({
baseUrl: '/',
source: createMDXSource(docs, meta),
});

View file

@ -1,10 +0,0 @@
import { createMDX } from 'fumadocs-mdx/next';
const withMDX = createMDX();
/** @type {import('next').NextConfig} */
const config = {
reactStrictMode: true,
};
export default withMDX(config);

File diff suppressed because it is too large Load diff

View file

@ -1,29 +0,0 @@
{
"name": "cmux-docs",
"version": "0.1.0",
"license": "AGPL-3.0-or-later",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"fumadocs-core": "^14.0.0",
"fumadocs-mdx": "^11.0.0",
"fumadocs-ui": "^14.0.0",
"next": "^15.0.0",
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
"devDependencies": {
"@types/node": "^22.0.0",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"autoprefixer": "^10.4.20",
"postcss": "^8.4.49",
"tailwindcss": "^3.4.16",
"typescript": "^5.7.0"
}
}

View file

@ -1,9 +0,0 @@
/** @type {import('postcss-load-config').Config} */
const config = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};
export default config;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

View file

@ -1,7 +0,0 @@
import { defineConfig, defineDocs } from 'fumadocs-mdx/config';
export const { docs, meta } = defineDocs({
dir: 'content/docs',
});
export default defineConfig();

View file

@ -1,33 +0,0 @@
import { createPreset } from 'fumadocs-ui/tailwind-plugin';
import type { Config } from 'tailwindcss';
const config: Config = {
content: [
'./components/**/*.{ts,tsx}',
'./app/**/*.{ts,tsx}',
'./content/**/*.{md,mdx}',
'./node_modules/fumadocs-ui/dist/**/*.js',
],
presets: [createPreset()],
theme: {
extend: {
colors: {
primary: {
50: '#f0f9ff',
100: '#e0f2fe',
200: '#bae6fd',
300: '#7dd3fc',
400: '#38bdf8',
500: '#0ea5e9',
600: '#0284c7',
700: '#0369a1',
800: '#075985',
900: '#0c4a6e',
950: '#082f49',
},
},
},
},
};
export default config;

View file

@ -1,40 +0,0 @@
{
"compilerOptions": {
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": [
"./*"
]
},
"target": "ES2017"
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts"
],
"exclude": [
"node_modules"
]
}

View file

@ -1,4 +0,0 @@
{
"$schema": "https://openapi.vercel.sh/vercel.json",
"framework": "nextjs"
}

View file

@ -1,8 +1,19 @@
export function DownloadButton({ size = "default" }: { size?: "default" | "sm" }) {
"use client";
import posthog from "posthog-js";
export function DownloadButton({
size = "default",
location = "hero",
}: {
size?: "default" | "sm";
location?: string;
}) {
const isSmall = size === "sm";
return (
<a
href="https://github.com/manaflow-ai/cmux/releases/latest/download/cmux-macos.dmg"
onClick={() => posthog.capture("cmuxterm_download_clicked", { location })}
className={`inline-flex items-center whitespace-nowrap rounded-full font-medium bg-foreground hover:opacity-85 transition-opacity ${
isSmall ? "gap-2 px-4 py-1.5 text-xs" : "gap-2.5 px-5 py-2.5 text-[15px]"
}`}

View file

@ -0,0 +1,20 @@
"use client";
import posthog from "posthog-js";
export function GitHubButton() {
return (
<a
href="https://github.com/manaflow-ai/cmux"
target="_blank"
rel="noopener noreferrer"
onClick={() => posthog.capture("cmuxterm_github_clicked", { location: "hero" })}
className="inline-flex items-center whitespace-nowrap gap-2 rounded-full border border-border px-5 py-2.5 text-[15px] font-medium text-foreground hover:bg-code-bg transition-colors"
>
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" />
</svg>
View on GitHub
</a>
);
}

View file

@ -1,4 +1,7 @@
"use client";
import Link from "next/link";
import posthog from "posthog-js";
export function NavLinks() {
return (
@ -31,6 +34,7 @@ export function NavLinks() {
href="https://github.com/manaflow-ai/cmux"
target="_blank"
rel="noopener noreferrer"
onClick={() => posthog.capture("cmuxterm_github_clicked", { location: "navbar" })}
className="hover:text-foreground transition-colors"
>
GitHub
@ -43,7 +47,15 @@ export function SiteFooter() {
return (
<footer className="py-8 flex justify-center">
<div className="flex flex-wrap justify-center items-center gap-4 text-sm text-muted px-6">
<a href="https://github.com/manaflow-ai/cmux" target="_blank" rel="noopener noreferrer" className="hover:text-foreground transition-colors">GitHub</a>
<a
href="https://github.com/manaflow-ai/cmux"
target="_blank"
rel="noopener noreferrer"
onClick={() => posthog.capture("cmuxterm_github_clicked", { location: "footer" })}
className="hover:text-foreground transition-colors"
>
GitHub
</a>
<a href="https://twitter.com/manaflowai" target="_blank" rel="noopener noreferrer" className="hover:text-foreground transition-colors">Twitter</a>
<a href="https://discord.gg/QRxkhZgY" target="_blank" rel="noopener noreferrer" className="hover:text-foreground transition-colors">Discord</a>
<Link href="/privacy-policy" className="hover:text-foreground transition-colors">Privacy</Link>

View file

@ -1,6 +1,7 @@
"use client";
import Link from "next/link";
import posthog from "posthog-js";
import { NavLinks } from "./nav-links";
import { DownloadButton } from "./download-button";
import { ThemeToggle } from "../theme";
@ -57,7 +58,7 @@ export function SiteHeader({
{/* Right: Download + theme + mobile */}
<div className="flex flex-1 items-center justify-end gap-1 min-w-0">
<div className="hidden md:block">
<DownloadButton size="sm" />
<DownloadButton size="sm" location="navbar" />
</div>
<ThemeToggle />
<MobileDrawerToggle
@ -136,12 +137,13 @@ export function SiteHeader({
href="https://github.com/manaflow-ai/cmux"
target="_blank"
rel="noopener noreferrer"
onClick={() => posthog.capture("cmuxterm_github_clicked", { location: "mobile_drawer" })}
className="hover:text-foreground transition-colors py-1"
>
GitHub
</a>
<div className="pt-2">
<DownloadButton size="sm" />
<DownloadButton size="sm" location="mobile_drawer" />
</div>
</div>
</nav>

View file

@ -1,6 +1,7 @@
import Balancer from "react-wrap-balancer";
import { TypingTagline } from "./typing";
import { DownloadButton } from "./components/download-button";
import { GitHubButton } from "./components/github-button";
import { SiteHeader } from "./components/site-header";
export default function Home() {
@ -35,18 +36,8 @@ export default function Home() {
{/* Download */}
<div className="flex flex-wrap items-center gap-3" data-dev="download" style={{ marginTop: 21, marginBottom: 33 }}>
<DownloadButton />
<a
href="https://github.com/manaflow-ai/cmux"
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center whitespace-nowrap gap-2 rounded-full border border-border px-5 py-2.5 text-[15px] font-medium text-foreground hover:bg-code-bg transition-colors"
>
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" />
</svg>
View on GitHub
</a>
<DownloadButton location="hero" />
<GitHubButton />
</div>
{/* Features */}

43
web/app/posthog.tsx Normal file
View file

@ -0,0 +1,43 @@
"use client";
import posthog from "posthog-js";
import { PostHogProvider as PHProvider } from "posthog-js/react";
import { usePathname, useSearchParams } from "next/navigation";
import { useEffect, Suspense } from "react";
if (typeof window !== "undefined") {
posthog.init("phc_opOVu7oFzR9wD3I6ZahFGOV2h3mqGpl5EHyQvmHciDP", {
api_host: "/cmuxterm",
ui_host: "https://us.posthog.com",
person_profiles: "identified_only",
capture_pageview: false,
capture_pageleave: true,
});
}
function PageviewTracker() {
const pathname = usePathname();
const searchParams = useSearchParams();
useEffect(() => {
if (pathname && posthog) {
let url = window.origin + pathname;
const search = searchParams.toString();
if (search) url += "?" + search;
posthog.capture("$pageview", { $current_url: url });
}
}, [pathname, searchParams]);
return null;
}
export function PostHogProvider({ children }: { children: React.ReactNode }) {
return (
<PHProvider client={posthog}>
<Suspense fallback={null}>
<PageviewTracker />
</Suspense>
{children}
</PHProvider>
);
}

View file

@ -1,11 +1,14 @@
"use client";
import { ThemeProvider } from "next-themes";
import { PostHogProvider } from "./posthog";
export function Providers({ children }: { children: React.ReactNode }) {
return (
<ThemeProvider attribute="class" defaultTheme="dark" disableTransitionOnChange>
{children}
<PostHogProvider>
{children}
</PostHogProvider>
</ThemeProvider>
);
}

View file

@ -7,6 +7,7 @@
"dependencies": {
"next": "16.1.6",
"next-themes": "^0.4.6",
"posthog-js": "^1.350.0",
"react": "19.2.3",
"react-dom": "19.2.3",
"react-wrap-balancer": "^1.1.1",
@ -185,6 +186,52 @@
"@nolyfill/is-core-module": ["@nolyfill/is-core-module@1.0.39", "", {}, "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA=="],
"@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="],
"@opentelemetry/api-logs": ["@opentelemetry/api-logs@0.208.0", "", { "dependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-CjruKY9V6NMssL/T1kAFgzosF1v9o6oeN+aX5JB/C/xPNtmgIJqcXHG7fA82Ou1zCpWGl4lROQUKwUNE1pMCyg=="],
"@opentelemetry/core": ["@opentelemetry/core@2.2.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-FuabnnUm8LflnieVxs6eP7Z383hgQU4W1e3KJS6aOG3RxWxcHyBxH8fDMHNgu/gFx/M2jvTOW/4/PHhLz6bjWw=="],
"@opentelemetry/exporter-logs-otlp-http": ["@opentelemetry/exporter-logs-otlp-http@0.208.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.208.0", "@opentelemetry/core": "2.2.0", "@opentelemetry/otlp-exporter-base": "0.208.0", "@opentelemetry/otlp-transformer": "0.208.0", "@opentelemetry/sdk-logs": "0.208.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-jOv40Bs9jy9bZVLo/i8FwUiuCvbjWDI+ZW13wimJm4LjnlwJxGgB+N/VWOZUTpM+ah/awXeQqKdNlpLf2EjvYg=="],
"@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.208.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/otlp-transformer": "0.208.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-gMd39gIfVb2OgxldxUtOwGJYSH8P1kVFFlJLuut32L6KgUC4gl1dMhn+YC2mGn0bDOiQYSk/uHOdSjuKp58vvA=="],
"@opentelemetry/otlp-transformer": ["@opentelemetry/otlp-transformer@0.208.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.208.0", "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/sdk-logs": "0.208.0", "@opentelemetry/sdk-metrics": "2.2.0", "@opentelemetry/sdk-trace-base": "2.2.0", "protobufjs": "^7.3.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-DCFPY8C6lAQHUNkzcNT9R+qYExvsk6C5Bto2pbNxgicpcSWbe2WHShLxkOxIdNcBiYPdVHv/e7vH7K6TI+C+fQ=="],
"@opentelemetry/resources": ["@opentelemetry/resources@2.5.1", "", { "dependencies": { "@opentelemetry/core": "2.5.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-BViBCdE/GuXRlp9k7nS1w6wJvY5fnFX5XvuEtWsTAOQFIO89Eru7lGW3WbfbxtCuZ/GbrJfAziXG0w0dpxL7eQ=="],
"@opentelemetry/sdk-logs": ["@opentelemetry/sdk-logs@0.208.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.208.0", "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.4.0 <1.10.0" } }, "sha512-QlAyL1jRpOeaqx7/leG1vJMp84g0xKP6gJmfELBpnI4O/9xPX+Hu5m1POk9Kl+veNkyth5t19hRlN6tNY1sjbA=="],
"@opentelemetry/sdk-metrics": ["@opentelemetry/sdk-metrics@2.2.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.9.0 <1.10.0" } }, "sha512-G5KYP6+VJMZzpGipQw7Giif48h6SGQ2PFKEYCybeXJsOCB4fp8azqMAAzE5lnnHK3ZVwYQrgmFbsUJO/zOnwGw=="],
"@opentelemetry/sdk-trace-base": ["@opentelemetry/sdk-trace-base@2.2.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-xWQgL0Bmctsalg6PaXExmzdedSp3gyKV8mQBwK/j9VGdCDu2fmXIb2gAehBKbkXCpJ4HPkgv3QfoJWRT4dHWbw=="],
"@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.39.0", "", {}, "sha512-R5R9tb2AXs2IRLNKLBJDynhkfmx7mX0vi8NkhZb3gUkPWHn6HXk5J8iQ/dql0U3ApfWym4kXXmBDRGO+oeOfjg=="],
"@posthog/core": ["@posthog/core@1.23.0", "", { "dependencies": { "cross-spawn": "^7.0.6" } }, "sha512-WXYL4+trl27iV8/Y+ESADOYDB7jBhbEj6q3AEQdn+9ygYG06Q3rZSdWk4ZVn8FdrD3mlq8fEqkUgRCekzp2W4g=="],
"@posthog/types": ["@posthog/types@1.350.0", "", {}, "sha512-Z8s3xc70RByHDT9u/xB1lLYHFNmEgY7nveqY8hXRPK39+vKrhosrQQOjnURLKAdyi9fRgoLc0D2yL/qRBKTxvQ=="],
"@protobufjs/aspromise": ["@protobufjs/aspromise@1.1.2", "", {}, "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="],
"@protobufjs/base64": ["@protobufjs/base64@1.1.2", "", {}, "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="],
"@protobufjs/codegen": ["@protobufjs/codegen@2.0.4", "", {}, "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="],
"@protobufjs/eventemitter": ["@protobufjs/eventemitter@1.1.0", "", {}, "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q=="],
"@protobufjs/fetch": ["@protobufjs/fetch@1.1.0", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.1", "@protobufjs/inquire": "^1.1.0" } }, "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ=="],
"@protobufjs/float": ["@protobufjs/float@1.0.2", "", {}, "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="],
"@protobufjs/inquire": ["@protobufjs/inquire@1.1.0", "", {}, "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q=="],
"@protobufjs/path": ["@protobufjs/path@1.1.2", "", {}, "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA=="],
"@protobufjs/pool": ["@protobufjs/pool@1.1.0", "", {}, "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="],
"@protobufjs/utf8": ["@protobufjs/utf8@1.1.0", "", {}, "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="],
"@rtsao/scc": ["@rtsao/scc@1.1.0", "", {}, "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g=="],
"@shikijs/core": ["@shikijs/core@3.22.0", "", { "dependencies": { "@shikijs/types": "3.22.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-iAlTtSDDbJiRpvgL5ugKEATDtHdUVkqgHDm/gbD2ZS9c88mx7G1zSYjjOxp5Qa0eaW0MAQosFRmJSk354PRoQA=="],
@ -251,6 +298,8 @@
"@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="],
"@types/trusted-types": ["@types/trusted-types@2.0.7", "", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="],
"@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.55.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.55.0", "@typescript-eslint/type-utils": "8.55.0", "@typescript-eslint/utils": "8.55.0", "@typescript-eslint/visitor-keys": "8.55.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.55.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-1y/MVSz0NglV1ijHC8OT49mPJ4qhPYjiK08YUQVbIOyu+5k862LKUHFkpKHWu//zmr7hDR2rhwUm6gnCGNmGBQ=="],
@ -391,6 +440,8 @@
"convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
"core-js": ["core-js@3.48.0", "", {}, "sha512-zpEHTy1fjTMZCKLHUZoVeylt9XrzaIN2rbPXEt0k+q7JE5CkCZdo6bNq55bn24a69CH7ErAVLKijxJja4fw+UQ=="],
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
"csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
@ -419,6 +470,8 @@
"doctrine": ["doctrine@2.1.0", "", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw=="],
"dompurify": ["dompurify@3.3.1", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q=="],
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
"electron-to-chromium": ["electron-to-chromium@1.5.286", "", {}, "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A=="],
@ -491,6 +544,8 @@
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
"fflate": ["fflate@0.4.8", "", {}, "sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA=="],
"file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="],
"fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
@ -675,6 +730,8 @@
"lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="],
"long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="],
"loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="],
"lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
@ -761,14 +818,22 @@
"postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
"posthog-js": ["posthog-js@1.350.0", "", { "dependencies": { "@opentelemetry/api": "^1.9.0", "@opentelemetry/api-logs": "^0.208.0", "@opentelemetry/exporter-logs-otlp-http": "^0.208.0", "@opentelemetry/resources": "^2.2.0", "@opentelemetry/sdk-logs": "^0.208.0", "@posthog/core": "1.23.0", "@posthog/types": "1.350.0", "core-js": "^3.38.1", "dompurify": "^3.3.1", "fflate": "^0.4.8", "preact": "^10.28.2", "query-selector-shadow-dom": "^1.0.1", "web-vitals": "^5.1.0" } }, "sha512-Ab+dyQdlKUTrfUZ12+fvcBo75S4jw/3o2gMleDga21B1v9c15yybiX4S3JrX66uh5L1DYG1H8sxtd4BXIIodjQ=="],
"preact": ["preact@10.28.3", "", {}, "sha512-tCmoRkPQLpBeWzpmbhryairGnhW9tKV6c6gr/w+RhoRoKEJwsjzipwp//1oCpGPOchvSLaAPlpcJi9MwMmoPyA=="],
"prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
"prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="],
"property-information": ["property-information@7.1.0", "", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="],
"protobufjs": ["protobufjs@7.5.4", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg=="],
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
"query-selector-shadow-dom": ["query-selector-shadow-dom@1.0.1", "", {}, "sha512-lT5yCqEBgfoMYpf3F2xQRK7zEr1rhIIZuceDK6+xRkJQ4NMbHTwXqk4NkwDwQMNqXgG9r9fyHnzwNVs6zV5KRw=="],
"queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
"react": ["react@19.2.3", "", {}, "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA=="],
@ -917,6 +982,8 @@
"vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="],
"web-vitals": ["web-vitals@5.1.0", "", {}, "sha512-ArI3kx5jI0atlTtmV0fWU3fjpLmq/nD3Zr1iFFlJLaqa5wLBkUSzINwBPySCX/8jRyjlmy1Volw1kz1g9XE4Jg=="],
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
"which-boxed-primitive": ["which-boxed-primitive@1.1.1", "", { "dependencies": { "is-bigint": "^1.1.0", "is-boolean-object": "^1.2.1", "is-number-object": "^1.1.1", "is-string": "^1.1.1", "is-symbol": "^1.1.1" } }, "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA=="],
@ -945,6 +1012,16 @@
"@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="],
"@opentelemetry/otlp-transformer/@opentelemetry/resources": ["@opentelemetry/resources@2.2.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A=="],
"@opentelemetry/resources/@opentelemetry/core": ["@opentelemetry/core@2.5.1", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-Dwlc+3HAZqpgTYq0MUyZABjFkcrKTePwuiFVLjahGD8cx3enqihmpAmdgNFO1R4m/sIe5afjJrA25Prqy4NXlA=="],
"@opentelemetry/sdk-logs/@opentelemetry/resources": ["@opentelemetry/resources@2.2.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A=="],
"@opentelemetry/sdk-metrics/@opentelemetry/resources": ["@opentelemetry/resources@2.2.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A=="],
"@opentelemetry/sdk-trace-base/@opentelemetry/resources": ["@opentelemetry/resources@2.2.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.8.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.8.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg=="],

View file

@ -1,7 +1,19 @@
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
/* config options here */
skipTrailingSlashRedirect: true,
async rewrites() {
return [
{
source: "/cmuxterm/static/:path*",
destination: "https://us-assets.i.posthog.com/static/:path*",
},
{
source: "/cmuxterm/:path*",
destination: "https://us.i.posthog.com/:path*",
},
];
},
};
export default nextConfig;

View file

@ -12,6 +12,7 @@
"dependencies": {
"next": "16.1.6",
"next-themes": "^0.4.6",
"posthog-js": "^1.350.0",
"react": "19.2.3",
"react-dom": "19.2.3",
"react-wrap-balancer": "^1.1.1",