openmux 0.1.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 monotykamary
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,194 @@
1
+ # openmux
2
+
3
+ A terminal multiplexer with master-stack layout (Zellij-style), built with:
4
+
5
+ - **Bun** - Fast JavaScript runtime
6
+ - **OpenTUI** - Terminal UI library with React reconciler
7
+ - **bun-pty** - PTY support for Bun
8
+ - **ghostty-web** - Terminal emulation via WASM
9
+
10
+ ## Features
11
+
12
+ - Master-stack tiling layout (like Zellij)
13
+ - i3-gaps style gaps and borders
14
+ - Vim-style `hjkl` navigation
15
+ - Tmux-style `Ctrl+b` prefix key
16
+ - 9 workspaces with isolated pane layouts
17
+ - Session persistence and management
18
+ - Pane zoom (fullscreen focused pane)
19
+ - Kitty Graphics and Sixel protocol support
20
+ - Mouse tracking (click to focus, tabbed pane switching)
21
+ - Scrollback support with mouse wheel and scrollbar
22
+
23
+ ## Installation
24
+
25
+ ### Quick Install (curl)
26
+
27
+ ```bash
28
+ curl -fsSL https://raw.githubusercontent.com/monotykamary/openmux/main/scripts/install.sh | bash
29
+ ```
30
+
31
+ ### npm / bun
32
+
33
+ ```bash
34
+ npm install -g openmux
35
+ # or
36
+ bun add -g openmux
37
+ ```
38
+
39
+ ### From Source
40
+
41
+ ```bash
42
+ git clone https://github.com/monotykamary/openmux.git
43
+ cd openmux
44
+ bun install
45
+ bun run build --install
46
+ ```
47
+
48
+ ### GitHub Releases
49
+
50
+ Download prebuilt binaries from [GitHub Releases](https://github.com/monotykamary/openmux/releases).
51
+
52
+ Available platforms:
53
+ - macOS (Apple Silicon)
54
+ - Linux (x64 / arm64)
55
+
56
+ ## Usage
57
+
58
+ ```bash
59
+ openmux
60
+ ```
61
+
62
+ For development:
63
+
64
+ ```bash
65
+ bun start # Run from source
66
+ bun dev # Run with watch mode
67
+ ```
68
+
69
+ ## Keyboard Shortcuts
70
+
71
+ ### Normal Mode (Alt shortcuts - no prefix needed)
72
+ - `Alt+h/j/k/l` - Navigate panes
73
+ - `Alt+n` - New pane
74
+ - `Alt+1-9` - Switch to workspace 1-9
75
+ - `Alt+[` / `Alt+]` - Cycle layout mode (vertical → horizontal → stacked)
76
+ - `Alt+x` - Close pane
77
+ - `Alt+z` - Toggle zoom (fullscreen focused pane)
78
+ - `Alt+s` - Open session picker
79
+ - `Ctrl+b` - Enter prefix mode
80
+
81
+ ### Mouse
82
+ - `Click` - Focus pane
83
+ - `Click tab` - Switch to stacked pane (in stacked mode)
84
+ - `Scroll wheel` - Scroll through terminal history (when not in alternate screen apps like vim)
85
+ - `Click scrollbar` - Jump to position in scrollback
86
+ - `Drag scrollbar` - Scroll through history by dragging
87
+
88
+ ### Prefix Mode (Ctrl+b, 2s timeout)
89
+ - `n` or `Enter` - New pane
90
+ - `h/j/k/l` - Navigate panes
91
+ - `1-9` - Switch to workspace 1-9
92
+ - `v` - Set layout mode: vertical
93
+ - `H` - Set layout mode: horizontal
94
+ - `t` - Set layout mode: stacked (tabbed)
95
+ - `x` - Close current pane
96
+ - `z` - Toggle zoom
97
+ - `s` - Open session picker
98
+ - `]` - Paste from clipboard
99
+ - `r` - Enter resize mode
100
+ - `?` - Toggle keyboard hints
101
+ - `Esc` - Exit prefix mode
102
+
103
+ ### Resize Mode
104
+ - `h/l` - Shrink/grow width
105
+ - `j/k` - Grow/shrink height
106
+ - `Enter/Esc` - Exit resize mode
107
+
108
+ ## Concepts
109
+
110
+ ### Workspaces
111
+ Like i3/sway, openmux supports multiple workspaces (1-9). Each workspace has its own layout tree of panes. The status bar shows populated workspaces dynamically - empty workspaces don't appear unless active.
112
+
113
+ ### Layout Modes (Zellij-style)
114
+ Each workspace has a layout mode that determines how panes are arranged:
115
+ - **Vertical** (`│`): Main pane on left, stack panes split vertically on right
116
+ - **Horizontal** (`─`): Main pane on top, stack panes split horizontally on bottom
117
+ - **Stacked** (`▣`): Main pane on left, stack panes tabbed on right (only active visible)
118
+
119
+ ### Sessions
120
+ Sessions persist your workspace layouts and pane working directories. Sessions are auto-saved to `~/.config/openmux/sessions/` and can be switched via the session picker (`Alt+s` or `Ctrl+b s`).
121
+
122
+ ## Project Structure
123
+
124
+ ```
125
+ src/
126
+ ├── core/ # Core layout and session management
127
+ │ ├── types.ts # Type definitions (Workspace, Pane, etc.)
128
+ │ ├── config.ts # Configuration and defaults
129
+ │ ├── keyboard-utils.ts # hjkl to Direction conversion
130
+ │ ├── operations/
131
+ │ │ ├── index.ts # Layout operations exports
132
+ │ │ └── master-stack-layout.ts # Master-stack layout calculations
133
+ │ └── session/ # Session persistence
134
+ │ ├── index.ts # Session exports
135
+ │ ├── session-manager.ts # High-level session operations
136
+ │ ├── session-serializer.ts # Serialize/deserialize sessions
137
+ │ └── session-storage.ts # Disk I/O for sessions
138
+
139
+ ├── components/ # OpenTUI React components
140
+ │ ├── index.ts # Component exports
141
+ │ ├── Pane.tsx # Individual pane with border/focus
142
+ │ ├── PaneContainer.tsx # Layout pane renderer
143
+ │ ├── TerminalView.tsx # Terminal rendering with buffer API
144
+ │ ├── StatusBar.tsx # Bottom status bar
145
+ │ ├── KeyboardHints.tsx # Keyboard shortcuts overlay
146
+ │ └── SessionPicker.tsx # Session selection modal
147
+
148
+ ├── contexts/ # React contexts for state
149
+ │ ├── index.ts # Context exports
150
+ │ ├── LayoutContext.tsx # Workspace/pane layout state reducer
151
+ │ ├── TerminalContext.tsx # PTY management and lifecycle
152
+ │ ├── KeyboardContext.tsx # Prefix mode and key state
153
+ │ ├── SessionContext.tsx # Session management and persistence
154
+ │ └── ThemeContext.tsx # Theme/styling configuration
155
+
156
+ ├── terminal/ # PTY and terminal emulation
157
+ │ ├── index.ts # Terminal exports
158
+ │ ├── pty-manager.ts # PTY session lifecycle (bun-pty)
159
+ │ ├── ghostty-emulator.ts # Terminal emulator (ghostty-web WASM)
160
+ │ ├── input-handler.ts # Key/mouse to escape sequence encoder
161
+ │ ├── graphics-passthrough.ts # Kitty Graphics/Sixel protocol
162
+ │ ├── capabilities.ts # Terminal capability detection
163
+ │ └── terminal-colors.ts # Color palette detection
164
+
165
+ ├── utils/
166
+ │ ├── index.ts # Utils exports
167
+ │ └── clipboard.ts # Clipboard read/write
168
+
169
+ ├── App.tsx # Main app component with context hierarchy
170
+ └── index.tsx # Entry point (Bun serve + OpenTUI renderer)
171
+ ```
172
+
173
+ ## Development Status
174
+
175
+ Current status:
176
+
177
+ - [x] Master-stack layout with gaps
178
+ - [x] OpenTUI component layer
179
+ - [x] Keyboard navigation system
180
+ - [x] PTY integration
181
+ - [x] ghostty-web WASM terminal emulation
182
+ - [x] Workspaces (1-9)
183
+ - [x] Layout modes (vertical/horizontal/stacked)
184
+ - [x] Session persistence
185
+ - [x] Pane zoom
186
+ - [x] Mouse support
187
+ - [x] Graphics protocol passthrough (Kitty/Sixel)
188
+ - [x] Scrollback support
189
+ - [ ] Session restore on startup
190
+ - [ ] Configurable keybindings
191
+
192
+ ## License
193
+
194
+ MIT
package/bin/openmux ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ # openmux npm bin wrapper
5
+ # Locates and executes the downloaded binary
6
+
7
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
8
+ PACKAGE_DIR="$(dirname "$SCRIPT_DIR")"
9
+ DIST_DIR="$PACKAGE_DIR/dist"
10
+
11
+ # Determine library extension based on OS
12
+ case "$(uname -s)" in
13
+ Darwin) LIB_EXT="dylib" ;;
14
+ Linux) LIB_EXT="so" ;;
15
+ *) echo "Unsupported OS" >&2; exit 1 ;;
16
+ esac
17
+
18
+ # Check if binary exists
19
+ if [[ ! -x "$DIST_DIR/openmux-bin" ]]; then
20
+ echo "Error: openmux binary not found at $DIST_DIR/openmux-bin" >&2
21
+ echo "Run 'npm run build' or reinstall the package" >&2
22
+ exit 1
23
+ fi
24
+
25
+ # Set library path and execute
26
+ export BUN_PTY_LIB="${BUN_PTY_LIB:-$DIST_DIR/librust_pty.$LIB_EXT}"
27
+ exec "$DIST_DIR/openmux-bin" "$@"
package/package.json ADDED
@@ -0,0 +1,75 @@
1
+ {
2
+ "name": "openmux",
3
+ "version": "0.1.1",
4
+ "description": "Terminal multiplexer with master-stack tiling layout",
5
+ "module": "src/index.tsx",
6
+ "type": "module",
7
+ "bin": {
8
+ "openmux": "./bin/openmux"
9
+ },
10
+ "files": [
11
+ "bin",
12
+ "scripts/postinstall.cjs"
13
+ ],
14
+ "scripts": {
15
+ "start": "bun run src/index.tsx",
16
+ "dev": "bun run --watch src/index.tsx",
17
+ "typecheck": "tsc --noEmit",
18
+ "build": "./scripts/build.sh",
19
+ "build:release": "./scripts/build.sh --release",
20
+ "install:local": "./scripts/build.sh --install",
21
+ "prepare": "effect-language-service patch",
22
+ "postinstall": "node scripts/postinstall.cjs",
23
+ "test": "vitest run",
24
+ "test:watch": "vitest",
25
+ "release": "standard-version",
26
+ "release:minor": "standard-version --release-as minor",
27
+ "release:major": "standard-version --release-as major",
28
+ "release:patch": "standard-version --release-as patch",
29
+ "release:first": "standard-version --first-release",
30
+ "publish:npm": "./scripts/publish.sh"
31
+ },
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "git+https://github.com/monotykamary/openmux.git"
35
+ },
36
+ "author": "monotykamary",
37
+ "license": "MIT",
38
+ "homepage": "https://github.com/monotykamary/openmux#readme",
39
+ "bugs": {
40
+ "url": "https://github.com/monotykamary/openmux/issues"
41
+ },
42
+ "keywords": [
43
+ "terminal",
44
+ "multiplexer",
45
+ "tui",
46
+ "tmux",
47
+ "zellij",
48
+ "bun",
49
+ "opentui"
50
+ ],
51
+ "engines": {
52
+ "bun": ">=1.0.0"
53
+ },
54
+ "devDependencies": {
55
+ "@effect/language-service": "^0.60.0",
56
+ "@effect/vitest": "^0.27.0",
57
+ "@types/bun": "latest",
58
+ "@types/react": "^19.2.7",
59
+ "standard-version": "^9.5.0",
60
+ "vitest": "^4.0.15"
61
+ },
62
+ "peerDependencies": {
63
+ "typescript": "^5.9.3"
64
+ },
65
+ "dependencies": {
66
+ "@effect/cli": "^0.72.1",
67
+ "@effect/platform": "^0.93.6",
68
+ "@opentui/core": "^0.1.57",
69
+ "@opentui/react": "^0.1.57",
70
+ "bun-pty": "^0.4.2",
71
+ "effect": "^3.19.9",
72
+ "ghostty-web": "^0.3.0",
73
+ "react": "^19.2.1"
74
+ }
75
+ }
@@ -0,0 +1,137 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * openmux postinstall script
5
+ * Downloads prebuilt binaries from GitHub releases
6
+ */
7
+
8
+ const fs = require("fs");
9
+ const path = require("path");
10
+ const https = require("https");
11
+ const { execSync } = require("child_process");
12
+
13
+ const REPO = "monotykamary/openmux";
14
+ const PACKAGE_ROOT = path.join(__dirname, "..");
15
+ const DIST_DIR = path.join(PACKAGE_ROOT, "dist");
16
+
17
+ function getPlatform() {
18
+ const platform = process.platform;
19
+ const arch = process.arch;
20
+
21
+ let os;
22
+ switch (platform) {
23
+ case "darwin":
24
+ os = "darwin";
25
+ break;
26
+ case "linux":
27
+ os = "linux";
28
+ break;
29
+ default:
30
+ throw new Error(`Unsupported platform: ${platform}`);
31
+ }
32
+
33
+ let cpu;
34
+ switch (arch) {
35
+ case "x64":
36
+ cpu = "x64";
37
+ break;
38
+ case "arm64":
39
+ cpu = "arm64";
40
+ break;
41
+ default:
42
+ throw new Error(`Unsupported architecture: ${arch}`);
43
+ }
44
+
45
+ return { os, arch: cpu, target: `${os}-${cpu}` };
46
+ }
47
+
48
+ function getVersion() {
49
+ const packageJson = require(path.join(PACKAGE_ROOT, "package.json"));
50
+ return `v${packageJson.version}`;
51
+ }
52
+
53
+ function fetch(url) {
54
+ return new Promise((resolve, reject) => {
55
+ https.get(url, { headers: { "User-Agent": "openmux-installer" } }, (res) => {
56
+ if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
57
+ // Follow redirect
58
+ fetch(res.headers.location).then(resolve).catch(reject);
59
+ return;
60
+ }
61
+ if (res.statusCode !== 200) {
62
+ reject(new Error(`HTTP ${res.statusCode}: ${url}`));
63
+ return;
64
+ }
65
+ resolve(res);
66
+ }).on("error", reject);
67
+ });
68
+ }
69
+
70
+ async function downloadFile(url, destPath) {
71
+ console.log(`Downloading ${url}...`);
72
+
73
+ const response = await fetch(url);
74
+ const writeStream = fs.createWriteStream(destPath);
75
+
76
+ return new Promise((resolve, reject) => {
77
+ response.pipe(writeStream);
78
+ writeStream.on("finish", () => {
79
+ writeStream.close();
80
+ resolve();
81
+ });
82
+ writeStream.on("error", reject);
83
+ });
84
+ }
85
+
86
+ async function main() {
87
+ // Skip in CI or when OPENMUX_SKIP_DOWNLOAD is set
88
+ if (process.env.CI || process.env.OPENMUX_SKIP_DOWNLOAD) {
89
+ console.log("Skipping binary download");
90
+ return;
91
+ }
92
+
93
+ try {
94
+ const { target } = getPlatform();
95
+ const version = getVersion();
96
+
97
+ console.log(`Installing openmux ${version} for ${target}...`);
98
+
99
+ const url = `https://github.com/${REPO}/releases/download/${version}/openmux-${version}-${target}.tar.gz`;
100
+
101
+ // Ensure dist directory exists
102
+ fs.mkdirSync(DIST_DIR, { recursive: true });
103
+
104
+ const tarballPath = path.join(DIST_DIR, "download.tar.gz");
105
+
106
+ await downloadFile(url, tarballPath);
107
+
108
+ console.log("Extracting...");
109
+
110
+ // Use native tar command to extract
111
+ execSync(`tar -xzf "${tarballPath}" -C "${DIST_DIR}"`, { stdio: "inherit" });
112
+
113
+ // Clean up tarball
114
+ fs.unlinkSync(tarballPath);
115
+
116
+ // Make binary executable
117
+ const binaryPath = path.join(DIST_DIR, "openmux-bin");
118
+ if (fs.existsSync(binaryPath)) {
119
+ fs.chmodSync(binaryPath, 0o755);
120
+ }
121
+
122
+ // Make wrapper executable
123
+ const wrapperPath = path.join(DIST_DIR, "openmux");
124
+ if (fs.existsSync(wrapperPath)) {
125
+ fs.chmodSync(wrapperPath, 0o755);
126
+ }
127
+
128
+ console.log("openmux installed successfully!");
129
+ } catch (error) {
130
+ // Don't fail the install if download fails
131
+ // User might be building from source
132
+ console.warn(`Warning: Could not download prebuilt binary: ${error.message}`);
133
+ console.warn("You may need to build from source: bun run build");
134
+ }
135
+ }
136
+
137
+ main();