mulmoterminal 0.4.0 → 0.5.0

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 CHANGED
@@ -25,7 +25,7 @@ Requires the [`claude`](https://claude.com/claude-code) CLI on your `PATH` and
25
25
  **Node ≥ 22.9**.
26
26
 
27
27
  ```bash
28
- npx mulmoterminal # start on http://localhost:3456 and open the browser
28
+ npx mulmoterminal # start on http://localhost:34567 and open the browser
29
29
  # or install globally:
30
30
  npm install -g mulmoterminal
31
31
  mulmoterminal
@@ -36,7 +36,7 @@ prints a one-line notice when a newer version is available (`npm i -g mulmotermi
36
36
  to update). Disable with `MULMOTERMINAL_NO_UPDATE_CHECK=1` (or `NO_UPDATE_NOTIFIER=1`).
37
37
 
38
38
  Options: `--cwd <dir>` (working directory — relative paths allowed; defaults to the
39
- directory you run the command from), `--port <n>` (default 3456), `--no-open`,
39
+ directory you run the command from), `--port <n>` (default 34567), `--no-open`,
40
40
  `--version`, `--help`.
41
41
 
42
42
  ```bash
@@ -96,8 +96,11 @@ server, and opens the browser. For local development from a clone, see
96
96
  - **Script commands** (`yarn dev`, tests, …), launched from a cell's directory
97
97
  picker, run in their own ephemeral PTY over a separate WebSocket (`/ws/run`);
98
98
  they are not Claude sessions. See [Scripts (Run menu)](#scripts-run-menu).
99
- - The Vite dev server proxies `/ws` (covers `/ws/run`), `/ws/pubsub`, and `/api` to
100
- the backend; in production the backend serves the built client from `dist/`.
99
+ - In dev (`yarn dev`) the Vite dev server runs on its own port (`CLIENT_PORT`,
100
+ default `6856`) and proxies `/ws` (covers `/ws/run`), `/ws/pubsub`, and `/api` to
101
+ the backend (`PORT`, default `34567`) — so you open the Vite port (e.g.
102
+ `http://localhost:6856`). In production the backend serves the built client from
103
+ `dist/` on `PORT`, and you open that.
101
104
 
102
105
  ---
103
106
 
@@ -139,7 +142,8 @@ the server runs without one.
139
142
 
140
143
  | Variable | Default | Description |
141
144
  | ------------ | -------------- | ----------- |
142
- | `PORT` | `3456` | HTTP/WebSocket port. |
145
+ | `PORT` | `34567` | Backend HTTP/WebSocket port (prod: the URL you open). |
146
+ | `CLIENT_PORT` | `6856` | Vite dev-server port (dev only: the URL you open with `yarn dev`). |
143
147
  | `CLAUDE_BIN` | `claude` | The Claude Code binary to spawn. |
144
148
  | `CLAUDE_CWD` | current dir | Working directory each `claude` PTY runs in; determines which project's sessions the sidebar lists. Via `npx mulmoterminal` it defaults to the directory you ran the command from (override with `--cwd <dir>`, relative allowed); when the server is run directly it falls back to `~/mulmoclaude`. A value read from `.env` must be an absolute path (`~` is not expanded). |
145
149
 
@@ -166,6 +170,37 @@ chime) or in the config file; the server streams that file at `GET /api/sound` a
166
170
  the client decodes it (falling back to the chime if it's missing or not audio). It's
167
171
  your own local file referenced by absolute path — nothing is added to the package.
168
172
 
173
+ ### Per-directory settings (`<project>/.mulmoterminal.json`)
174
+
175
+ Drop a `.mulmoterminal.json` in a project directory to give terminals opened **in
176
+ that directory** their own look and sound. It applies per terminal (per grid cell) —
177
+ the rest of the app keeps your chosen theme — and a directory's theme overrides your
178
+ manual theme pick for that terminal only. Every field is optional; a missing or
179
+ malformed file is ignored.
180
+
181
+ ```jsonc
182
+ {
183
+ "name": "PROD · payments", // badge shown on this directory's terminals
184
+ "badgeColor": "#cf222e", // badge color (hex #rrggbb)
185
+ "theme": "nord", // terminal palette: midnight | nord | daylight | solarized
186
+ "colors": { "background": "#190a23", "cursor": "#ff2e63" }, // per-key palette overrides
187
+ "sound": "./.mulmoterminal/alert.mp3" // attention sound, RELATIVE to this directory
188
+ }
189
+ ```
190
+
191
+ | Field | Meaning |
192
+ | ------------ | ------- |
193
+ | `name` | Label shown as a badge in the terminal/cell header. |
194
+ | `badgeColor` | Badge background color (`#rrggbb`); text auto-contrasts. |
195
+ | `theme` | xterm palette for terminals in this directory (one of the built-in theme ids). |
196
+ | `colors` | Per-key xterm palette overrides applied on top of `theme` (or the app theme when `theme` is unset). Keys are xterm `ITheme` names (`background`, `foreground`, `cursor`, `selectionBackground`, the 16 ANSI colors, …); values are hex (`#rgb` / `#rrggbb` / `#rrggbbaa`). Unknown keys / bad values are dropped. |
197
+ | `sound` | Attention sound for this directory's sessions, a path **relative to the directory** (served at `GET /api/dir-sound`). |
198
+
199
+ **Security.** `sound` is a directory-relative path only — absolute paths and any
200
+ `../` that escapes the directory are rejected, and the path is never taken from the
201
+ HTTP request, so an opened project can't point the player at arbitrary files.
202
+ Changes take effect when the terminal is next opened (no live file watch).
203
+
169
204
  ---
170
205
 
171
206
  ## Running
@@ -173,14 +208,14 @@ your own local file referenced by absolute path — nothing is added to the pack
173
208
  ```bash
174
209
  yarn install # postinstall fixes node-pty prebuilt binary perms
175
210
 
176
- yarn dev # server (:3456) + Vite dev server, concurrently
211
+ yarn dev # backend (:34567) + Vite UI (:6856), concurrently — open http://localhost:6856
177
212
  # or individually:
178
213
  yarn dev:server # backend only (node --import tsx --env-file-if-exists=.env server/index.ts)
179
214
  yarn dev:client # Vite dev server only
180
215
 
181
216
  yarn build # type-check (vue-tsc) + vite build -> dist/
182
217
  yarn typecheck:server # type-check the server (tsconfig.server.json)
183
- yarn server # run backend; serves dist/ + the APIs on :3456
218
+ yarn server # run backend; serves dist/ + the APIs on :34567
184
219
  yarn test # vitest run
185
220
  ```
186
221
 
@@ -189,8 +224,8 @@ type-checked separately through `tsconfig.server.json` (`strict`), kept out of
189
224
  the main `build` so the two type-check independently.
190
225
 
191
226
  In dev, open the Vite URL; its proxy forwards `/ws`, `/ws/pubsub`, and `/api` to
192
- `:3456`. In production, run `yarn build` then `yarn server` and open
193
- `http://localhost:3456`.
227
+ `:34567`. In production, run `yarn build` then `yarn server` and open
228
+ `http://localhost:34567`.
194
229
 
195
230
  ---
196
231
 
@@ -245,7 +280,7 @@ command, so the file is the allowlist of what can run.
245
280
 
246
281
  ## Server API specification
247
282
 
248
- Base URL: `http://localhost:$PORT` (default `http://localhost:3456`).
283
+ Base URL: `http://localhost:$PORT` (default `http://localhost:34567`).
249
284
 
250
285
  ### HTTP: `GET /api/sessions`
251
286
 
@@ -468,6 +503,12 @@ Key rules:
468
503
  - **Reattach over respawn**: selecting a session that still has a live background
469
504
  PTY reattaches to it (replaying a ≤ 64 KB output tail) instead of spawning a
470
505
  duplicate `claude`.
506
+ - **One live viewer per session**: a session is bound to a single socket. Opening
507
+ it in a second place (another tab, or another grid cell pointed at the same dir)
508
+ reattaches there and **supersedes** the first, which detaches. To avoid doing
509
+ this by accident, a grid launcher's resume list **flags rows already open in
510
+ another terminal** (`● open`) and **asks for confirmation** before taking one
511
+ over.
471
512
  - Brand-new sessions appear in the sidebar **immediately** (before their `.jsonl`
472
513
  exists) via the in-memory `knownSessions` registry + a `created` push; an
473
514
  unused one disappears when its PTY is reaped.
@@ -534,9 +575,10 @@ src/
534
575
  Sidebar.vue Session list; working dot + waiting bold; pub/sub driven
535
576
  Sidebar.spec.ts Vitest component tests
536
577
  Terminal.vue xterm.js terminal; /ws (or /ws/run); single-view ▶ Run menu
537
- GridView.vue Grid toolbar (auto-layout, + Terminal); runs handed-off scripts
578
+ AppToolbar.vue Shared header (single + grid); grid-only + Terminal + ⇅/↔ cell-order toggle
579
+ GridView.vue Grid view: auto-layout, pages, manual/auto cell order; runs handed-off scripts
538
580
  RunMenu.vue ▶ Run dropdown: lists a dir's script.json, emits the pick
539
- TerminalCell.vue A cell: Claude launcher (dir picker + resume + run-a-script)
581
+ TerminalCell.vue A cell: Claude launcher (dir picker + resume + run-a-script); ◀▶ to reorder
540
582
  TerminalGrid.vue Grid of cells; auto-sizes by count; zoom lines up every tab
541
583
  CommandCell.vue A grid cell that runs a script.json command (ephemeral)
542
584
  composables/
@@ -17,7 +17,7 @@ import { fetchLatestVersion, isNewerVersion } from "./update-check.js";
17
17
  const __dirname = dirname(fileURLToPath(import.meta.url));
18
18
  const PKG_DIR = join(__dirname, "..");
19
19
  const SERVER_ENTRY = join(PKG_DIR, "server", "index.ts");
20
- const DEFAULT_PORT = 3456;
20
+ const DEFAULT_PORT = 34567;
21
21
  const READY_TIMEOUT_MS = 15_000;
22
22
  const MAX_BIND_RETRIES = 5;
23
23
  // Server exit code meaning "port taken at bind time" — keep in sync with