|
| 1 | +<p align="center"> |
| 2 | + <img src="public/logo-square.png" alt="OTG Code" width="128" /> |
| 3 | +</p> |
| 4 | + |
| 5 | +<h1 align="center">OTG Code</h1> |
| 6 | +<p align="center"><em>On-The-Go Code — a mobile-first, terminal-focused development environment in the browser</em></p> |
| 7 | + |
| 8 | +<p align="center">Work on your VPS or home machine from anywhere — phone, tablet, or desktop — with zero client-side installation. A mobile-optimised terminal built for coding CLIs, with file explorer, code editor, and localhost proxy — all in a single browser tab, accessible via a single Cloudflare Quick Tunnel.</p> |
| 9 | + |
| 10 | +<p align="center"> |
| 11 | + <img src="https://img.shields.io/badge/version-0.1.0-blue" alt="Version"> |
| 12 | + <img src="https://img.shields.io/badge/status-alpha-orange" alt="Status: alpha"> |
| 13 | + <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green" alt="License"></a> |
| 14 | + <img src="https://img.shields.io/badge/node-%3E%3D20-brightgreen" alt="Node >= 20"> |
| 15 | + <img src="https://img.shields.io/badge/TypeScript-5.9-blue?logo=typescript&logoColor=white" alt="TypeScript"> |
| 16 | + <img src="https://img.shields.io/badge/React_Router-v7-red?logo=reactrouter&logoColor=white" alt="React Router"> |
| 17 | + <img src="https://img.shields.io/badge/PRs-welcome-brightgreen" alt="PRs Welcome"> |
| 18 | +</p> |
| 19 | + |
| 20 | +## Features |
| 21 | + |
| 22 | +<!-- Product demo previews (GIFs) -> use table for consistent GitHub layout --> |
| 23 | +<table align="center"> |
| 24 | + <tr> |
| 25 | + <td align="center" style="padding:8px"> |
| 26 | + <a href="public/videos/demo_explorer.gif" title="Open Explorer GIF"><img src="public/videos/demo_explorer.gif" alt="File explorer" width="220" style="max-width:220px;height:auto;"></a> |
| 27 | + <div style="font-size:13px;color:#555;margin-top:8px;">File explorer</div> |
| 28 | + </td> |
| 29 | + <td align="center" style="padding:8px"> |
| 30 | + <a href="public/videos/demo_terminal.gif" title="Open Terminal GIF"><img src="public/videos/demo_terminal.gif" alt="Terminal interface" width="220" style="max-width:220px;height:auto;"></a> |
| 31 | + <div style="font-size:13px;color:#555;margin-top:8px;">Terminal interface</div> |
| 32 | + </td> |
| 33 | + <td align="center" style="padding:8px"> |
| 34 | + <a href="public/videos/demo_localhost.gif" title="Open Localhost GIF"><img src="public/videos/demo_localhost.gif" alt="Localhost preview" width="220" style="max-width:220px;height:auto;"></a> |
| 35 | + <div style="font-size:13px;color:#555;margin-top:8px;">Localhost preview</div> |
| 36 | + </td> |
| 37 | + </tr> |
| 38 | +</table> |
| 39 | + |
| 40 | + |
| 41 | +Three panels — **Terminal**, **Files**, and **Browser** — responsive 3-column layout on desktop/landscape, single-panel with bottom tab bar on mobile/portrait (orientation-aware breakpoint). All panels stay mounted in the DOM with CSS visibility toggling, preserving terminal state, WebSocket connections, and iframe content across navigation. Everything routes through a single Cloudflare Quick Tunnel. |
| 42 | + |
| 43 | +### Terminal |
| 44 | +- Multi-session tabs (renamable), xterm.js with configurable font size |
| 45 | +- **Quick action tab groups** — color-coded action tabs (blue) and app tabs (green): |
| 46 | + - **cmds** — common shell commands (ls, top, df, free, nvidia-smi, etc.) |
| 47 | + - **cd** — visual directory picker with server-side CWD detection (works inside tmux) |
| 48 | + - **Ctrl+** — sticky modifier combos (Ctrl, Ctrl+Shift, Alt, Alt+Shift) with full A-Z and 0-9 |
| 49 | + - **code** — coding CLI launchers (Claude Code, Codex, OpenCode) with vendor selector, permission presets, and slash commands |
| 50 | + - **git** — quick actions (status, log, diff, add, fetch, pull, push, stash, branch), commit with message input, git config setup (user.name/email) |
| 51 | + - **nano / vim** — file opener with version detection, in-app command buttons, save/exit shortcuts |
| 52 | + - **tmux** — session manager (list, attach, create, kill), in-session window/pane controls, auto extended-keys compatibility |
| 53 | +- **Always-visible nav keys** below input — Enter, Bksp, arrows, Esc, Tab, PgUp/PgDn |
| 54 | +- **Context-aware tabs** — editor mode (nano/vim) disables action tabs; tmux mode hides nano/vim; plain terminal shows everything |
| 55 | +- Auto-reset of tmux/editor state on socket reconnect |
| 56 | +- System info popup with OS, kernel, CPU, memory, GPU details |
| 57 | +- Tool version detection (nano, vim, tmux, claude, codex, opencode) |
| 58 | + |
| 59 | +### File Explorer |
| 60 | +- Multi-session tabs (renamable), breadcrumb + editable path navigation |
| 61 | +- Create, rename, delete files and folders; file info dialog; hidden files toggle |
| 62 | +- Context menu (right-click / long-press), selectable/scrollable file paths |
| 63 | +- Drag-and-drop file upload with progress tracking and per-file cancel |
| 64 | +- Chunked upload for large files (>80 MB) to bypass Cloudflare's 100 MB request limit |
| 65 | +- **Built-in viewers & editors:** |
| 66 | + - **Code Editor** — Monaco with syntax highlighting, configurable font size, word wrap |
| 67 | + - **Markdown Preview** — GitHub-flavored with prose styling |
| 68 | + - **Jupyter Notebook** — cells with type badges, rendered markdown, code, and outputs |
| 69 | + - **HTML Preview** — live rendered in sandboxed iframe |
| 70 | + - **PDF Viewer** — inline rendering with zoom and pinch-to-zoom |
| 71 | + - **Image Viewer** — zoom controls with full pan/scroll at any zoom level, pinch-to-zoom |
| 72 | + - **Video Player** — mp4, webm, ogg, mov, mkv, avi |
| 73 | + - **Audio Player** — mp3, wav, ogg, flac, aac, m4a |
| 74 | + |
| 75 | +### Localhost Preview |
| 76 | +- Preview any localhost port in a new tab via the built-in `/proxy/:port` reverse proxy |
| 77 | +- Multi-tab port list with reachability checks and green/amber status indicators |
| 78 | +- All localhost previews route through the same main tunnel of the app |
| 79 | + |
| 80 | +## Quick Start |
| 81 | + |
| 82 | +```bash |
| 83 | +# Clone and install |
| 84 | +git clone https://github.com/davindicode/otgcode.git |
| 85 | +cd otgcode |
| 86 | +pnpm install |
| 87 | + |
| 88 | +# Create .env |
| 89 | +cp .env.example .env |
| 90 | +# Edit .env to set OTG_PORT, DEFAULT_SHELL, DEFAULT_CWD |
| 91 | + |
| 92 | +# Production with Cloudflare tunnel (all-in-one) |
| 93 | +./start.sh |
| 94 | +``` |
| 95 | + |
| 96 | +The `start.sh` script installs dependencies if needed, builds the app, starts the server, and launches a Cloudflare Quick Tunnel — printing a public URL you can open on any device. On macOS it also clears Gatekeeper quarantine flags on node-pty binaries. If `cloudflared` is not found in your PATH, the script auto-downloads it to `.bin/` (supports macOS, Linux, and Windows on x64/arm64). |
| 97 | + |
| 98 | +<details> |
| 99 | +<summary><strong>DNS tip:</strong> Tunnel URL not resolving? Set your DNS to 1.1.1.1</summary> |
| 100 | + |
| 101 | +The Quick Tunnel URL (e.g. `xxxx.trycloudflare.com`) resolves instantly on Cloudflare DNS but may take time on ISP/router DNS servers, or fail entirely if a stale NXDOMAIN gets cached. Set your DNS to **1.1.1.1** (Cloudflare) for instant resolution. |
| 102 | + |
| 103 | +**macOS:** |
| 104 | +```bash |
| 105 | +networksetup -setdnsservers Wi-Fi 1.1.1.1 1.0.0.1 |
| 106 | +sudo dscacheutil -flushcache && sudo killall -HUP mDNSResponder |
| 107 | +# To revert: networksetup -setdnsservers Wi-Fi empty |
| 108 | +``` |
| 109 | + |
| 110 | +**Linux (systemd-resolved):** |
| 111 | +```bash |
| 112 | +resolvectl dns $(ip route show default | awk '{print $5}') 1.1.1.1 1.0.0.1 |
| 113 | +# Or permanently via /etc/systemd/resolved.conf: |
| 114 | +# [Resolve] |
| 115 | +# DNS=1.1.1.1 1.0.0.1 |
| 116 | +# sudo systemctl restart systemd-resolved |
| 117 | +``` |
| 118 | + |
| 119 | +**Windows (PowerShell as Admin):** |
| 120 | +```powershell |
| 121 | +Get-NetAdapter | Select Name, Status |
| 122 | +Set-DnsClientServerAddress -InterfaceAlias "Wi-Fi" -ServerAddresses 1.1.1.1,1.0.0.1 |
| 123 | +ipconfig /flushdns |
| 124 | +# To revert: Set-DnsClientServerAddress -InterfaceAlias "Wi-Fi" -ResetServerAddresses |
| 125 | +``` |
| 126 | +</details> |
| 127 | + |
| 128 | +## Scripts |
| 129 | + |
| 130 | +| Command | Description | |
| 131 | +|---------|-------------| |
| 132 | +| `pnpm dev` | Dev server with HMR (Socket.IO + proxy on same port) | |
| 133 | +| `pnpm build` | Production build | |
| 134 | +| `pnpm start` | Production server | |
| 135 | +| `pnpm start:tunnel` | Production server + Cloudflare tunnel | |
| 136 | +| `./start.sh` | Install + build + start + tunnel (all-in-one) | |
| 137 | + |
| 138 | +## Architecture |
| 139 | + |
| 140 | +Single Express server on one port handles everything: |
| 141 | + |
| 142 | +``` |
| 143 | +Express (port 7777) |
| 144 | +├── React Router v7 — UI (SSR shell + client-side app) |
| 145 | +├── Socket.IO — real-time terminal I/O via node-pty |
| 146 | +├── REST API — file operations (list, read, write, rename, delete, upload, download, mkdir) |
| 147 | +├── Reverse Proxy — /proxy/:port/* forwards HTTP + WebSocket to localhost services |
| 148 | +└── Cloudflare Tunnel — single optional tunnel via cloudflared (all traffic) |
| 149 | +``` |
| 150 | + |
| 151 | +### Reverse Proxy |
| 152 | + |
| 153 | +The `/proxy/:port` reverse proxy is how localhost previews work — no extra tunnels or processes needed. It handles: |
| 154 | +- **Path rewriting** — strips the `/proxy/:port` prefix before forwarding to `127.0.0.1:<port>` |
| 155 | +- **WebSocket upgrades** — WS connections on `/proxy/:port/...` are forwarded (supports Vite HMR, webpack-dev-server, etc.) |
| 156 | +- **Header stripping** — removes `X-Frame-Options` and CSP headers so content can embed in the preview iframe |
| 157 | +- **Asset path rewriting** — rewrites absolute paths in HTML, CSS, and JS (src, href, url(), ES imports) to include the `/proxy/:port` prefix so assets load correctly through the proxy |
| 158 | +- **Port blocking** — blocks privileged ports (1–1023, except 80/443) and the OTG server port itself |
| 159 | + |
| 160 | +**Dev server notes:** Most dev servers (Vite, webpack, Next.js) work out of the box since both HTTP and WebSocket traffic are proxied. However, dev servers that hardcode their WebSocket URL to `ws://localhost:<port>` in client-side code (bypassing the proxy path) will fail over the tunnel — the HMR connection won't reach the VPS. Vite works because its HMR client connects relative to the page origin. If a dev server's HMR breaks, configure it to use a relative or custom WebSocket path. |
| 161 | + |
| 162 | +## Tech Stack |
| 163 | + |
| 164 | +React Router v7, Express, Socket.IO, node-pty, xterm.js, Monaco Editor, Zustand, Tailwind CSS v4, TypeScript |
| 165 | + |
| 166 | +## Requirements |
| 167 | + |
| 168 | +- **Node.js** 20+ |
| 169 | +- **pnpm** (package manager) |
| 170 | +- **C compiler** for node-pty on Linux (`apt install build-essential`) |
| 171 | +- **cloudflared** (auto-installed by `start.sh` if not found) for remote access via Cloudflare tunnel |
| 172 | + |
| 173 | +## License |
| 174 | + |
| 175 | +[MIT](LICENSE) |
0 commit comments