termeet 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +202 -0
  2. package/bin/termeet.js +60 -0
  3. package/package.json +65 -0
package/README.md ADDED
@@ -0,0 +1,202 @@
1
+ <div align="center">
2
+
3
+ ```
4
+ ████████╗███████╗██████╗ ███╗ ███╗███████╗███████╗████████╗
5
+ ╚══██╔══╝██╔════╝██╔══██╗████╗ ████║██╔════╝██╔════╝╚══██╔══╝
6
+ ██║ █████╗ ██████╔╝██╔████╔██║█████╗ █████╗ ██║
7
+ ██║ ██╔══╝ ██╔══██╗██║╚██╔╝██║██╔══╝ ██╔══╝ ██║
8
+ ██║ ███████╗██║ ██║██║ ╚═╝ ██║███████╗███████╗ ██║
9
+ ╚═╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚══════╝ ╚═╝
10
+ ```
11
+
12
+ **Video conferencing that lives where you live — the terminal.**
13
+
14
+ [![npm](https://img.shields.io/npm/v/termeet)](https://www.npmjs.com/package/termeet)
15
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
16
+
17
+ Your face → pixels → ASCII → WebSocket → their terminal. No browser. No Electron. Just characters.
18
+
19
+ `npm install -g termeet`
20
+
21
+ ---
22
+
23
+ </div>
24
+
25
+ ```
26
+ ┌──────────────────────────────────────────────────────┐
27
+ │ @@@@@@@@ %%%%%%%% ######## │
28
+ │ @@ @@ %% %% ## ## Participants: 3 │
29
+ │ @ o o @ % o o % # o o # Room: a7f3x │
30
+ │ @ __ @ % __ % # __ # │
31
+ │ @@@@@@@@ %%%%%%%% ######## [M]ute [V]ideo │
32
+ │ [T]chat [Q]uit │
33
+ │ > hey, can everyone see my screen? │
34
+ │ > looks great in ASCII! │
35
+ └──────────────────────────────────────────────────────┘
36
+ ```
37
+
38
+ Termeet is a real-time video conferencing app that turns your camera into live ASCII art — right in the terminal. Built with [OpenTUI](https://opentui.com), powered by [Bun](https://bun.sh), and stitched together with ffmpeg and WebSockets. No browser required.
39
+
40
+ ## Features
41
+
42
+ - **ASCII Video** — Camera streams rendered as real-time ASCII art
43
+ - **Multi-participant** — Grid layout adapts to number of participants
44
+ - **Live Chat** — In-meeting text chat with timestamps
45
+ - **Audio Streaming** — Microphone capture and playback via ffmpeg
46
+ - **Room Management** — Create or join rooms with shareable room IDs
47
+ - **Controls** — Mute/unmute, camera toggle, chat toggle
48
+ - **Test Pattern** — Animated gradient pattern when no camera is available
49
+
50
+ **Tech Stack:**
51
+
52
+ - **Runtime**: [Bun](https://bun.sh)
53
+ - **Terminal UI**: [@opentui/react](https://opentui.com) (Zig-powered, React renderer)
54
+ - **Camera/Audio**: ffmpeg subprocess (cross-platform)
55
+ - **ASCII Rendering**: p5.js-inspired brightness mapping with edge detection
56
+ - **Networking**: WebSocket with JSON protocol
57
+ - **Server**: Bun built-in HTTP/WebSocket server
58
+
59
+ ## Prerequisites
60
+
61
+ **From this repo (development):**
62
+
63
+ - [Bun](https://bun.sh) (v1.0+)
64
+ - [ffmpeg](https://ffmpeg.org) (and **ffplay** if you want to hear remote audio — often the same package as ffmpeg)
65
+
66
+ **`npm install -g termeet`:** no Bun; ffmpeg is bundled next to the binary (optional **ffplay** for playback — install ffmpeg fully or copy `ffplay` beside `termeet`).
67
+
68
+ ```bash
69
+ # Install Bun
70
+ curl -fsSL https://bun.sh/install | bash
71
+
72
+ # Install ffmpeg (macOS)
73
+ brew install ffmpeg
74
+
75
+ # Install ffmpeg (Ubuntu/Debian)
76
+ sudo apt install ffmpeg
77
+ ```
78
+
79
+ On **macOS**, grant **Camera** and **Microphone** to your terminal (Terminal.app, iTerm, etc.) under **System Settings → Privacy & Security**, or capture will stay on “No signal”.
80
+
81
+ ## Quick Start
82
+
83
+ ```bash
84
+ # Install dependencies
85
+ bun install
86
+
87
+ # Terminal 1: Start the server
88
+ bun run server
89
+
90
+ # Terminal 2: Start the client
91
+ bun run dev
92
+ ```
93
+
94
+ Or run both together:
95
+
96
+ ```bash
97
+ bun run start
98
+ ```
99
+
100
+ ## Standalone CLI (no Bun or ffmpeg in PATH)
101
+
102
+ ### npm (same pattern as multiplayer-debugger)
103
+
104
+ The published **`termeet` command is client-only** — it connects to your signaling server via `TERMEET_HOST` / `TERMEET_PORT`. Host the WebSocket server separately (`bun run server` from this repo on a VPS, etc.).
105
+
106
+ A small **Node wrapper** (`bin/termeet.js`) plus **optional** packages `termeet-cli-<platform>-<arch>` that ship the compiled Bun binary and bundled ffmpeg.
107
+
108
+ ```bash
109
+ npm install -g termeet
110
+ termeet --help
111
+ ```
112
+
113
+ Ensure `package.json` → `repository.url` matches this GitHub repo (needed for **provenance** on publish). Add a repo secret **`NPM_TOKEN`** (npm publish token). Trigger **Publish Packages** by pushing a tag `v1.2.3` or via **Actions → Publish Packages → Run workflow** (see `.github/workflows/publish-packages.yml`).
114
+
115
+ ### Maintainer builds
116
+
117
+ ```bash
118
+ bun install
119
+ bun run build:cli # this machine only (+ ffplay if on PATH)
120
+ bun run build:cli:all # all platforms (CI)
121
+ ```
122
+
123
+ Each `dist/<platform-arch>/bin/` contains `termeet`, downloaded `ffmpeg` (see [eugeneware/ffmpeg-static](https://github.com/eugeneware/ffmpeg-static)), and optionally `ffplay`. You can zip a single `dist/<slug>/` folder for sidecar distribution.
124
+
125
+ Use `FFMPEG_PATH`, `FFPLAY_PATH`, or `TERMEET_BIN_PATH` when needed.
126
+
127
+ ## Usage
128
+
129
+ ### Server
130
+
131
+ ```bash
132
+ # Start with default port (3483)
133
+ bun run server
134
+
135
+ # Custom port
136
+ TERMEET_PORT=8080 bun run server
137
+ ```
138
+
139
+ ### Client
140
+
141
+ ```bash
142
+ # Connect to local server
143
+ bun run dev
144
+
145
+ # Connect to remote server
146
+ TERMEET_HOST=192.168.1.100 TERMEET_PORT=3483 bun run dev
147
+ ```
148
+
149
+ ### Controls
150
+
151
+ | Key | Action |
152
+ | -------- | ------------------------------------------------------------------- |
153
+ | `M` | Toggle microphone mute |
154
+ | `V` | Toggle camera |
155
+ | `T` | Toggle chat panel |
156
+ | `I` | Copy room ID (or use **Room** in the bar) |
157
+ | `Tab` | Focus chat / cycle fields (lobby create & join forms) |
158
+ | `Esc` | Lobby: quit on main menu, else back to menu · Meeting: unfocus chat |
159
+ | `Q` | Lobby: quit · Meeting: leave room |
160
+ | `Ctrl+Q` | Meeting only: quit app |
161
+ | `P` | Meeting: clear pinned participant |
162
+
163
+ ## How It Works
164
+
165
+ 1. **Camera Capture**: ffmpeg captures raw RGB frames from your camera (or a test pattern if capture fails).
166
+ 2. **ASCII Rendering**: Each frame is processed with a p5.js–style pipeline (luminance, contrast/brightness, optional **Sobel** edges — the engine supports edges; the default app config keeps them off).
167
+ 3. **Video over WebSocket**: Downsampled **raw RGB (base64)** is sent to peers; each client runs its own ASCII renderer for its terminal size. (So the wire format is pixels, not pre-rendered ASCII text.)
168
+ 4. **Audio**: PCM chunks (base64) over the same WebSocket; ffmpeg/ffplay for capture and playback where available.
169
+ 5. **Display**: OpenTUI lays out lobby, grid, chat, and controls.
170
+
171
+ ## Project Structure
172
+
173
+ ```
174
+ src/
175
+ ├── index.tsx # Client CLI entry (connects to signaling server)
176
+ ├── app.tsx # Main App component (state management)
177
+ ├── protocol.ts # Shared types and message definitions
178
+ ├── lib/
179
+ │ ├── media-binaries.ts # Resolve bundled ffmpeg/ffplay next to executable
180
+ │ └── clipboard.ts # Room ID copy helper
181
+ ├── ui/
182
+ │ ├── lobby.tsx # Create/join room screen
183
+ │ ├── meeting-room.tsx # Main meeting view with video grid
184
+ │ ├── video-panel.tsx # Individual ASCII video panel
185
+ │ ├── chat-panel.tsx # Chat sidebar with message input
186
+ │ ├── controls-bar.tsx # Bottom controls bar
187
+ │ └── participants-bar.tsx # Participant status bar
188
+ ├── media/
189
+ │ ├── camera.ts # Camera capture via ffmpeg
190
+ │ ├── ascii-renderer.ts # Frame-to-ASCII conversion engine
191
+ │ └── audio.ts # Audio capture and playback
192
+ ├── network/
193
+ │ └── client.ts # WebSocket client with event system
194
+ └── server/
195
+ ├── index.ts # Server entry point
196
+ ├── room-manager.ts # Room lifecycle and participant tracking
197
+ └── ws-handler.ts # WebSocket message handling and broadcast
198
+ ```
199
+
200
+ ## License
201
+
202
+ MIT
package/bin/termeet.js ADDED
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * npm global entry: resolves the native binary from optionalDependency
4
+ * termeet-cli-<platform>-<arch> (same pattern as @multiplayer-app/cli).
5
+ */
6
+ import { execFileSync } from "node:child_process"
7
+ import { existsSync } from "node:fs"
8
+ import { dirname, join } from "node:path"
9
+ import { fileURLToPath } from "node:url"
10
+
11
+ const PLATFORM_MAP = { darwin: "darwin", linux: "linux", win32: "windows" }
12
+ const ARCH_MAP = { x64: "x64", arm64: "arm64" }
13
+
14
+ const platform = PLATFORM_MAP[/** @type {keyof typeof PLATFORM_MAP} */ (process.platform)]
15
+ const arch = ARCH_MAP[/** @type {keyof typeof ARCH_MAP} */ (process.arch)]
16
+
17
+ if (!platform || !arch) {
18
+ process.stderr.write(`Unsupported platform: ${process.platform}-${process.arch}\n`)
19
+ process.exit(1)
20
+ }
21
+
22
+ const slug = `${platform}-${arch}`
23
+ const pkgName = `termeet-cli-${slug}`
24
+ const binName = process.platform === "win32" ? "termeet.exe" : "termeet"
25
+
26
+ function findBinary() {
27
+ if (process.env["TERMEET_BIN_PATH"]) return process.env["TERMEET_BIN_PATH"]
28
+
29
+ let dir = dirname(fileURLToPath(import.meta.url))
30
+ while (true) {
31
+ const candidate = join(dir, "node_modules", pkgName, "bin", binName)
32
+ if (existsSync(candidate)) return candidate
33
+ const parent = dirname(dir)
34
+ if (parent === dir) break
35
+ dir = parent
36
+ }
37
+
38
+ const devBin = join(
39
+ dirname(fileURLToPath(import.meta.url)),
40
+ "..",
41
+ "dist",
42
+ slug,
43
+ "bin",
44
+ binName,
45
+ )
46
+ if (existsSync(devBin)) return devBin
47
+
48
+ return null
49
+ }
50
+
51
+ const bin = findBinary()
52
+ if (!bin) {
53
+ process.stderr.write(
54
+ `Could not find Termeet binary for ${slug}.\n` +
55
+ `Try reinstalling: npm install -g termeet\n`,
56
+ )
57
+ process.exit(1)
58
+ }
59
+
60
+ execFileSync(bin, process.argv.slice(2), { stdio: "inherit" })
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "termeet",
3
+ "version": "0.1.3",
4
+ "description": "Termeet - Video conferencing with ASCII art",
5
+ "module": "src/index.tsx",
6
+ "type": "module",
7
+ "private": false,
8
+ "author": "Gegham Khachatryan <gegham.k99@gmail.com>",
9
+ "homepage": "https://termeet.app",
10
+ "keywords": [
11
+ "video",
12
+ "conferencing",
13
+ "ascii",
14
+ "art",
15
+ "terminal"
16
+ ],
17
+ "bin": {
18
+ "termeet": "./bin/termeet.js"
19
+ },
20
+ "files": [
21
+ "bin"
22
+ ],
23
+ "optionalDependencies": {
24
+ "termeet-cli-darwin-arm64": "0.1.3",
25
+ "termeet-cli-darwin-x64": "0.1.3",
26
+ "termeet-cli-linux-arm64": "0.1.3",
27
+ "termeet-cli-linux-x64": "0.1.3",
28
+ "termeet-cli-windows-arm64": "0.1.3",
29
+ "termeet-cli-windows-x64": "0.1.3"
30
+ },
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "git+https://github.com/gegham-khachatryan/termeet"
34
+ },
35
+ "license": "MIT",
36
+ "scripts": {
37
+ "dev": "bun run src/index.tsx",
38
+ "server": "bun run src/server/index.ts",
39
+ "start": "bun run server & sleep 1 && bun run dev",
40
+ "build:cli": "bun run scripts/cli-build.ts --single",
41
+ "build:cli:all": "bun run scripts/cli-build.ts",
42
+ "publish:cli": "bun run scripts/cli-publish.ts",
43
+ "build:web": "cd web && bun install && bun run build",
44
+ "typecheck": "tsc --noEmit"
45
+ },
46
+ "devDependencies": {
47
+ "@types/bun": "latest",
48
+ "@types/react": "^19.2.14",
49
+ "@types/uuid": "^11.0.0",
50
+ "@types/ws": "^8.18.1",
51
+ "ffmpeg-static": "^5.3.0"
52
+ },
53
+ "peerDependencies": {
54
+ "typescript": "^5"
55
+ },
56
+ "dependencies": {
57
+ "@opentui/core": "^0.1.97",
58
+ "@opentui/react": "^0.1.97",
59
+ "chalk": "^5.6.2",
60
+ "nanoid": "^5.1.7",
61
+ "react": "^19.2.4",
62
+ "uuid": "^13.0.0",
63
+ "ws": "^8.20.0"
64
+ }
65
+ }