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.
- package/README.md +202 -0
- package/bin/termeet.js +60 -0
- 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
|
+
[](https://www.npmjs.com/package/termeet)
|
|
15
|
+
[](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
|
+
}
|