mulmoterminal 2.0.1 → 2.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/README.md +28 -10
- package/bin/mulmoterminal.js +26 -7
- package/common/cockpitLines.ts +46 -0
- package/common/dirChrome.ts +6 -1
- package/common/terminalFontSize.ts +20 -0
- package/common/voiceInputStatus.ts +21 -0
- package/dist/assets/{abnfDiagram-VRR7QNED-RjiYivmv-CMyzg2zY.js → abnfDiagram-VRR7QNED-RjiYivmv-1RrSDjxg.js} +1 -1
- package/dist/assets/architecture-TIHT7OUA-0NsD2xW1-CtE1YID5.js +1 -0
- package/dist/assets/{architectureDiagram-ZJ3FMSHR-3nWA91tG-CSIT7RtO.js → architectureDiagram-ZJ3FMSHR-3nWA91tG-sTMTrNOq.js} +1 -1
- package/dist/assets/{blockDiagram-677ZJIJ3-BPuAJQRW-CnVQRl8Q.js → blockDiagram-677ZJIJ3-BPuAJQRW-CQmbTR0O.js} +1 -1
- package/dist/assets/{c4Diagram-LMCZKHZV-C0LAqQso-CrpIWU_7.js → c4Diagram-LMCZKHZV-C0LAqQso-kjKqpuxO.js} +1 -1
- package/dist/assets/channel-7wUqSdoX-i8mXF_QX.js +1 -0
- package/dist/assets/{chunk-32BRIVSS-BRrYpgtb-D_PiqrVh.js → chunk-32BRIVSS-BRrYpgtb-YnuI_kVF.js} +1 -1
- package/dist/assets/{chunk-52WLFC77-BJ-ss3Xr-BCpG-w5L.js → chunk-52WLFC77-BJ-ss3Xr-Dv-_qNms.js} +1 -1
- package/dist/assets/{chunk-C7G6YPKG-BZEucKEL-CP9wac26.js → chunk-C7G6YPKG-BZEucKEL-VvGb94ad.js} +1 -1
- package/dist/assets/{chunk-EX3LRPZG-DLS6FBN1-BJZF7tEb.js → chunk-EX3LRPZG-DLS6FBN1-Cv9JdwrA.js} +1 -1
- package/dist/assets/{chunk-FWX5IMBZ-DHLSFw1H-Cnk0SpZp.js → chunk-FWX5IMBZ-DHLSFw1H-CFK4Xl3Y.js} +2 -2
- package/dist/assets/{chunk-HOUHSVGY-Bhlt8hXJ-CRGS9q8z.js → chunk-HOUHSVGY-Bhlt8hXJ-l9dGXTcV.js} +1 -1
- package/dist/assets/{chunk-ICXQ74PX-DwgHBX_g-DpPUV9Tl.js → chunk-ICXQ74PX-DwgHBX_g-oBhC9ghH.js} +1 -1
- package/dist/assets/{chunk-MOJQB5TN-CMZRaeqt-CG59XkeN.js → chunk-MOJQB5TN-CMZRaeqt-CKo27qok.js} +1 -1
- package/dist/assets/{chunk-OGEWGWER-8Qy4a8b5-B59YGczL.js → chunk-OGEWGWER-8Qy4a8b5-Di7gzOY_.js} +1 -1
- package/dist/assets/{chunk-PUDLZKDR-DcrWQRYh-CXGtfuoa.js → chunk-PUDLZKDR-DcrWQRYh-DuMYBLYy.js} +1 -1
- package/dist/assets/{chunk-Q4XR5HBZ-ZXVGkG8Z-eHuKxxBN.js → chunk-Q4XR5HBZ-ZXVGkG8Z-BH8ZxP7u.js} +1 -1
- package/dist/assets/{chunk-V7JOEXUC-DmGdheTX-BJZa8auK.js → chunk-V7JOEXUC-DmGdheTX-e5Lem5jk.js} +1 -1
- package/dist/assets/{chunk-VAUOI2AC-CS9QJ4yz-DviHNtmN.js → chunk-VAUOI2AC-CS9QJ4yz-CfgcnwuB.js} +1 -1
- package/dist/assets/{chunk-VR4S4FIN-C6a91eNY-DsA9hbD5.js → chunk-VR4S4FIN-C6a91eNY-BcBE3OXA.js} +1 -1
- package/dist/assets/{chunk-WYO6CB5R-BlzOfotS-qckQG4MR.js → chunk-WYO6CB5R-BlzOfotS-BRwSb4rl.js} +1 -1
- package/dist/assets/{chunk-ZGVPDNZ5-BYwxNFTK-Cr8j6FMw.js → chunk-ZGVPDNZ5-BYwxNFTK-yy_t_DUF.js} +1 -1
- package/dist/assets/classDiagram-OUVF2IWQ-BYlRi6YQ-BM_zLSMQ.js +1 -0
- package/dist/assets/classDiagram-v2-EOCWNBFH-BCHaYHxj-BM_zLSMQ.js +1 -0
- package/dist/assets/cynefin-VYW2F7L2-CGYr2F6h-BBb6pP0M.js +1 -0
- package/dist/assets/{cynefinDiagram-TSTJHNR4-DHA9iPo--BuPVUHaQ.js → cynefinDiagram-TSTJHNR4-DHA9iPo--CEHib9RV.js} +1 -1
- package/dist/assets/{dagre-VKFMJZFB--oJKqXBZ-l8CP-U85.js → dagre-VKFMJZFB--oJKqXBZ-DufbQ1ld.js} +1 -1
- package/dist/assets/{diagram-FQU43EPY-CcJCB9bG-CV6yO6d6.js → diagram-FQU43EPY-CcJCB9bG-Bcp75QDM.js} +1 -1
- package/dist/assets/{diagram-G47NLZAW-C_o-WGG1-DmUMCyqP.js → diagram-G47NLZAW-C_o-WGG1-C_2FVIX_.js} +1 -1
- package/dist/assets/{diagram-NH7WQ7WH-CXJCYvY--D1Zcc9sH.js → diagram-NH7WQ7WH-CXJCYvY--B1kVCM4I.js} +1 -1
- package/dist/assets/{diagram-OA4YK3LP-BMzeJ87A-CsC2S-ug.js → diagram-OA4YK3LP-BMzeJ87A-Bd1BRg0M.js} +1 -1
- package/dist/assets/{diagram-WEI45ONY-D_93NKqo-C3Xq1urQ.js → diagram-WEI45ONY-D_93NKqo-VJzlhYeh.js} +1 -1
- package/dist/assets/{ebnfDiagram-CCIWWBDH-CVai1Ii9-BtjcwSW_.js → ebnfDiagram-CCIWWBDH-CVai1Ii9-BXziXSdO.js} +1 -1
- package/dist/assets/{erDiagram-Q63AITRT-CiABnA0s-DhQMPhj9.js → erDiagram-Q63AITRT-CiABnA0s-DzjaMys9.js} +1 -1
- package/dist/assets/eventmodeling-45OFAUF4-CWmqhm3a-C1_24lZu.js +1 -0
- package/dist/assets/flowDiagram-23GEKE2U-DKSB5AiY-DQrHefx7.js +1 -0
- package/dist/assets/{ganttDiagram-NO4QXBWP-DQZvdFo1-Bw2aYbCJ.js → ganttDiagram-NO4QXBWP-DQZvdFo1-DqlM0R3b.js} +1 -1
- package/dist/assets/gitGraph-TEB2WS4Q-BR9qPwdN-DSTb91Xn.js +1 -0
- package/dist/assets/{gitGraphDiagram-IHSO6WYX-C2ovBouh-C1Dlsswx.js → gitGraphDiagram-IHSO6WYX-C2ovBouh-Dy5WrrGR.js} +1 -1
- package/dist/assets/{index-Dwc5wfJg.js → index-D4LB9YoW.js} +196 -196
- package/dist/assets/index-u-r21U2t.css +1 -0
- package/dist/assets/info-DKCQHKI2-Dplx5kMp-Cdy5Xn2F.js +1 -0
- package/dist/assets/{infoDiagram-FWYZ7A6U-7UnoB5AP-CjB00vD7.js → infoDiagram-FWYZ7A6U-7UnoB5AP-ByceYPA5.js} +1 -1
- package/dist/assets/{ishikawaDiagram-FXEZZL3T-ByUDM_N2-BxVGf9t3.js → ishikawaDiagram-FXEZZL3T-ByUDM_N2-BZEvgyxt.js} +1 -1
- package/dist/assets/{journeyDiagram-5HDEW3XC-c5xIah9o-BPLsgkBp.js → journeyDiagram-5HDEW3XC-c5xIah9o-D3IMe-Kx.js} +1 -1
- package/dist/assets/{kanban-definition-HUTT4EX6-Cnt6loYD-C0UF5ruR.js → kanban-definition-HUTT4EX6-Cnt6loYD-cjfYxgHr.js} +1 -1
- package/dist/assets/{lib-Ca9R1hlX.js → lib-BP5ftgj4.js} +1 -1
- package/dist/assets/{line-D7ziSjKi-B4ejYAqw.js → line-D7ziSjKi-C07Dlbdw.js} +1 -1
- package/dist/assets/{marp-J4wSkl3Q.js → marp-ytjNgY2U.js} +1 -1
- package/dist/assets/{mermaid-parser.core-DxEa8E3F-D1ef8DoR.js → mermaid-parser.core-DxEa8E3F-BAh0ySn4.js} +2 -2
- package/dist/assets/{mermaid.core-V0OYwIz3-BSn9HyWz.js → mermaid.core-V0OYwIz3-CIFgdt3N.js} +3 -3
- package/dist/assets/{mindmap-definition-LN4V7U3C-CexN3O6L-BLTr4gMi.js → mindmap-definition-LN4V7U3C-CexN3O6L-1SEo71tq.js} +1 -1
- package/dist/assets/packet-7NZHBO7P-CQI3flND-BFv5gKuv.js +1 -0
- package/dist/assets/{pegDiagram-2B236MQR-BiM4G0cW-E_eG9LVV.js → pegDiagram-2B236MQR-BiM4G0cW-DkewOOUw.js} +1 -1
- package/dist/assets/pie-RZYD4A2V-CnmKE2Wa-9hvyyDzu.js +1 -0
- package/dist/assets/{pieDiagram-ENE6RG2P-B_fBBS-2-f4ZSZ6GC.js → pieDiagram-ENE6RG2P-B_fBBS-2-DdJe9KqM.js} +1 -1
- package/dist/assets/{quadrantDiagram-ABIIQ3AL-BigGVxCR-DnwDJxmc.js → quadrantDiagram-ABIIQ3AL-BigGVxCR-C8kNdasc.js} +1 -1
- package/dist/assets/radar-I7S5WNFK-us6x-Z9R-DBeFDuLF.js +1 -0
- package/dist/assets/railroad-3IZDKUUU-BKdz6pdN-FnTYQEAW.js +1 -0
- package/dist/assets/railroad-abnf-AHOZXSZD-m_CSSIfd-Bo4xQewA.js +1 -0
- package/dist/assets/railroad-ebnf-EBAXGLYW-Nu3nAG7C-YFFxMVbA.js +1 -0
- package/dist/assets/railroad-peg-LSFZ7HO6--Y3mD4R_-DMzqIAqS.js +1 -0
- package/dist/assets/{railroadDiagram-RFXS5EU6--lNIlG64-BUH_ocB5.js → railroadDiagram-RFXS5EU6--lNIlG64-DawLoSCW.js} +1 -1
- package/dist/assets/{requirementDiagram-TGXJPOKE-D-B0Y5Wu-CT5zmLHk.js → requirementDiagram-TGXJPOKE-D-B0Y5Wu-BMFbGAmA.js} +1 -1
- package/dist/assets/{sankeyDiagram-HTMAVEWB-DlutaXDv-9sorx_WA.js → sankeyDiagram-HTMAVEWB-DlutaXDv-Dwt5PZ2I.js} +1 -1
- package/dist/assets/{sequenceDiagram-DBY2YBRQ-BOLgtggH-CnjR9ja7.js → sequenceDiagram-DBY2YBRQ-BOLgtggH-yb89czG2.js} +1 -1
- package/dist/assets/{stateDiagram-2N3HPSRC-jf_dXUEU-DboFu0BL.js → stateDiagram-2N3HPSRC-jf_dXUEU-kqvWM0jR.js} +1 -1
- package/dist/assets/stateDiagram-v2-6OUMAXLB-CuzgCL2v-QnrqYDdZ.js +1 -0
- package/dist/assets/{swimlanes-5IMT3BWC-CYjtALQH-Cqxsp00w.js → swimlanes-5IMT3BWC-CYjtALQH-CiVWAsob.js} +1 -1
- package/dist/assets/swimlanesDiagram-G3AALYLV-3bmSBtKs-CE7hrF_H.js +8 -0
- package/dist/assets/{timeline-definition-FHXFAJF6-DYJ4oUm8-Du08XOrV.js → timeline-definition-FHXFAJF6-DYJ4oUm8-DrqIflbo.js} +1 -1
- package/dist/assets/treeView-QDETBFTQ-D27HWG6Q-DaEUFpbs.js +1 -0
- package/dist/assets/treemap-6X3UGDF4-CmGIU3H5-Cb7ZUpob.js +1 -0
- package/dist/assets/{vennDiagram-L72KCM5P-CdKAHoek-DtBtukcO.js → vennDiagram-L72KCM5P-CdKAHoek-COepU8y_.js} +1 -1
- package/dist/assets/wardley-OPB4EBWU-CbHWsb2k-BON96qJv.js +1 -0
- package/dist/assets/{wardleyDiagram-EHGQE667-BF6c4_CW-CW4nfoJ3.js → wardleyDiagram-EHGQE667-BF6c4_CW-Z2AgrgfV.js} +1 -1
- package/dist/assets/{xychartDiagram-FW5EYKEG-CGiKngj7-Ruux6STV.js → xychartDiagram-FW5EYKEG-CGiKngj7-ChC7g8sJ.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +1 -1
- package/server/backends/whisper.ts +3 -5
- package/server/config/app-config.ts +41 -15
- package/server/config/config-routes.ts +13 -0
- package/server/config/config-schema.ts +12 -0
- package/server/config/dir-config.ts +18 -2
- package/server/config/header-config.ts +4 -4
- package/server/git/pr-footer.ts +20 -0
- package/server/git/worktree-pr.ts +32 -4
- package/server/index.ts +1 -1
- package/server/infra/tmux-routes.ts +1 -1
- package/server/session/pty-connection.ts +1 -1
- package/server/session/scheduled-sessions.ts +2 -2
- package/server/skills/mulmoterminal-config/SKILL.md +45 -12
- package/server/skills/mulmoterminal-config/palettes.json +6 -6
- package/dist/assets/architecture-TIHT7OUA-0NsD2xW1-3kpQCFcP.js +0 -1
- package/dist/assets/channel-7wUqSdoX-D8_GjCXH.js +0 -1
- package/dist/assets/classDiagram-OUVF2IWQ-BYlRi6YQ-axLhYHqO.js +0 -1
- package/dist/assets/classDiagram-v2-EOCWNBFH-BCHaYHxj-axLhYHqO.js +0 -1
- package/dist/assets/cynefin-VYW2F7L2-CGYr2F6h-C9DtePCD.js +0 -1
- package/dist/assets/eventmodeling-45OFAUF4-CWmqhm3a-BCX3cvpw.js +0 -1
- package/dist/assets/flowDiagram-23GEKE2U-DKSB5AiY-DjZRfq7e.js +0 -1
- package/dist/assets/gitGraph-TEB2WS4Q-BR9qPwdN-pIAqsAx0.js +0 -1
- package/dist/assets/index-DVTKApMs.css +0 -1
- package/dist/assets/info-DKCQHKI2-Dplx5kMp-S1We2Nw2.js +0 -1
- package/dist/assets/packet-7NZHBO7P-CQI3flND-DaLVcMu_.js +0 -1
- package/dist/assets/pie-RZYD4A2V-CnmKE2Wa-BKDCOtxh.js +0 -1
- package/dist/assets/radar-I7S5WNFK-us6x-Z9R-Cw4aJNVl.js +0 -1
- package/dist/assets/railroad-3IZDKUUU-BKdz6pdN-DJZYlsdK.js +0 -1
- package/dist/assets/railroad-abnf-AHOZXSZD-m_CSSIfd-D9wfK70u.js +0 -1
- package/dist/assets/railroad-ebnf-EBAXGLYW-Nu3nAG7C-C2rW9Qx8.js +0 -1
- package/dist/assets/railroad-peg-LSFZ7HO6--Y3mD4R_-CCUOq6go.js +0 -1
- package/dist/assets/stateDiagram-v2-6OUMAXLB-CuzgCL2v-FGJ3mpM7.js +0 -1
- package/dist/assets/swimlanesDiagram-G3AALYLV-3bmSBtKs-B3qFCxjl.js +0 -8
- package/dist/assets/treeView-QDETBFTQ-D27HWG6Q-dIhNDmqB.js +0 -1
- package/dist/assets/treemap-6X3UGDF4-CmGIU3H5-C8SEy1d3.js +0 -1
- package/dist/assets/wardley-OPB4EBWU-CbHWsb2k-SYQlwZRo.js +0 -1
package/README.md
CHANGED
|
@@ -70,7 +70,7 @@ output you haven't seen) — driven by Claude/Codex activity hooks the server in
|
|
|
70
70
|
|
|
71
71
|

|
|
72
72
|
|
|
73
|
-
*Besides the grid there's a **single view** for focusing on one agent: the conversation/terminal on the left, and a **GUI panel** ("Canvas") on the right where the agent's tool calls render as documents, forms, charts, images, and HTML — not just printed text. Switch between the two with the chat / grid icons in the toolbar.*
|
|
73
|
+
*Besides the grid there's a **single view** for focusing on one agent: the conversation/terminal on the left, and a **GUI panel** ("Canvas") on the right where the agent's tool calls render as documents, forms, charts, images, and HTML — not just printed text. Switch between the two with the chat / grid icons in the toolbar. **The app opens on the grid** (`/`); the single view has its own URL, `/chat`, so you can bookmark either.*
|
|
74
74
|
|
|
75
75
|
**Inserting a file path** — like a native terminal, you can put a file's absolute path into
|
|
76
76
|
the prompt: **drag a file** onto the terminal (works where the browser exposes the path via
|
|
@@ -111,11 +111,23 @@ dotfiles are server-only. The 45 extensions both sides agree on live in
|
|
|
111
111
|
|
|
112
112
|
## Install & run
|
|
113
113
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
114
|
+
Needs **Node ≥ 22.9**, plus these CLIs on your `PATH`:
|
|
115
|
+
|
|
116
|
+
| | Tool | What it gives you | Install |
|
|
117
|
+
| --- | --- | --- | --- |
|
|
118
|
+
| **Required** | [`claude`](https://claude.com/claude-code) | every Claude session — this app is a cockpit for it | `npm i -g @anthropic-ai/claude-code`, then run `claude` once to log in |
|
|
119
|
+
| **Required** | `git` | [worktree isolation](#git-worktrees--pull-requests), each cell's branch / unsaved-dot / diff readout, the PR footer | `brew install git` · `sudo apt install git` · `sudo dnf install git` · Windows: [git-scm.com](https://git-scm.com/download/win) |
|
|
120
|
+
| **Required** | `gh` | the cross-repo **PRs & Issues** view and one-click PR creation — it uses your `gh` login, so no token is stored | [cli.github.com](https://cli.github.com), then `gh auth login` |
|
|
121
|
+
| Recommended | `tmux` | [session persistence](#session-persistence-tmux) — terminals survive a server restart | `brew install tmux` · `sudo apt install tmux` · `sudo dnf install tmux` · no native Windows build (falls back to plain PTYs) |
|
|
122
|
+
| Optional | `codex` | [Codex sessions](#agents-claude--codex) in a cell, alongside Claude | `npm i -g @openai/codex` |
|
|
123
|
+
| Optional | `docker` | the experimental [Docker sandbox](#docker-sandbox-experimental-single-view) | [docs.docker.com](https://docs.docker.com/get-started/get-docker/) |
|
|
124
|
+
| Optional | `ffmpeg` | video rendering from the [mulmo-script panel](#wiki-collections--the-gui-panel) (its plugin ships enabled) | `brew install ffmpeg` · `sudo apt install ffmpeg` · `sudo dnf install ffmpeg` |
|
|
125
|
+
| Optional | `ollama` | [`claude-ollama`](https://receptron.github.io/mulmoterminal/guide/en/claude-ollama.html) — Claude Code against a fully local model | [ollama.com/download](https://ollama.com/download) |
|
|
126
|
+
|
|
127
|
+
The server starts without any of the non-required rows; you just lose that row's feature,
|
|
128
|
+
and the header/panel for it says so. `git` and `gh` are marked required because losing them
|
|
129
|
+
costs whole views rather than one button. `npx mulmoterminal init` (below) reports which of
|
|
130
|
+
these it can find.
|
|
119
131
|
|
|
120
132
|
```bash
|
|
121
133
|
npx mulmoterminal # start on http://localhost:34567 and open the browser
|
|
@@ -124,8 +136,8 @@ npm install -g mulmoterminal
|
|
|
124
136
|
mulmoterminal
|
|
125
137
|
```
|
|
126
138
|
|
|
127
|
-
**First-run setup (optional).** `npx mulmoterminal init` checks your environment (Node ≥ 22.9
|
|
128
|
-
|
|
139
|
+
**First-run setup (optional).** `npx mulmoterminal init` checks your environment (Node ≥ 22.9
|
|
140
|
+
and every CLI in the table above), seeds the launcher's **directory
|
|
129
141
|
presets** from the projects in your Claude Code history, and writes `~/.mulmoterminal/config.json`.
|
|
130
142
|
It's **idempotent** — re-run it any time to refresh the presets; it overwrites the managed parts
|
|
131
143
|
and keeps your other settings. When `claude` is installed it can hand off to the
|
|
@@ -434,7 +446,7 @@ The Settings modal (⚙) persists per-user UI choices to `~/.mulmoterminal/confi
|
|
|
434
446
|
|
|
435
447
|

|
|
436
448
|
|
|
437
|
-
*Open it from the ⚙ button in the toolbar. Pick a **theme**, set a custom **attention sound**, list the repos the cross-repo **PRs & Issues** view should aggregate, add **launch commands** for grid cells, and register your own **MCP servers** — no need to hand-edit the config file.*
|
|
449
|
+
*Open it from the ⚙ button in the toolbar. Pick a **theme**, set the **terminal font size**, set a custom **attention sound**, list the repos the cross-repo **PRs & Issues** view should aggregate, add **launch commands** for grid cells, and register your own **MCP servers** — no need to hand-edit the config file. Note that **theme and font size are stored per browser** (they're display preferences, so a phone and a desktop keep their own); the rest live in `~/.mulmoterminal/config.json` and are shared by every client.*
|
|
438
450
|
|
|
439
451
|
| Field | Meaning |
|
|
440
452
|
| ------------ | ------- |
|
|
@@ -451,6 +463,7 @@ The Settings modal (⚙) persists per-user UI choices to `~/.mulmoterminal/confi
|
|
|
451
463
|
| `worklogEnabled` | `true` to run the built-in **dev worklog** batch (see below). Off by default (each run spawns an LLM session, so it costs tokens). |
|
|
452
464
|
| `worklogIntervalHours` | Worklog cadence in hours (default `6`, clamped to `1`–`168`). |
|
|
453
465
|
| `terminalSubmit` | Which bytes Claude reads as **submit** vs **newline**: `"cr"` (default — Enter submits, Shift+Enter makes a newline) or `"esc-cr"` (for a Claude Code rebound the other way). Applies to the keyboard **and** the phone remote-view submit, for **Claude sessions only** (shell/codex keep plain Enter). See the [Configuration guide](https://receptron.github.io/mulmoterminal/guide/en/config.html#terminal-submit). |
|
|
466
|
+
| `prWorkdirFooter` | Ends the body of a PR **⧉ Open PR** creates with `work in <clone>` — the directory name of the clone the work happened in, so a PR says which of several side-by-side checkouts produced it. **On by default**; set `false` to opt out — read from the file per PR, so no restart is needed (there is no Settings control for it). Only applied to PRs this app creates (pressing the button again on an existing PR never re-appends). |
|
|
454
467
|
|
|
455
468
|
#### Header buttons
|
|
456
469
|
|
|
@@ -527,6 +540,7 @@ malformed file is ignored.
|
|
|
527
540
|
"buttonColor": "#c7cdf0", // header icon buttons (hex #rrggbb)
|
|
528
541
|
"theme": "nord", // terminal palette: midnight | nord | daylight | solarized
|
|
529
542
|
"colors": { "background": "#190a23", "cursor": "#ff2e63" }, // per-key palette overrides
|
|
543
|
+
"fontSize": 16, // terminal font size in px (8–32); overrides Settings
|
|
530
544
|
"sound": "./.mulmoterminal/alert.mp3" // attention sound, RELATIVE to this directory
|
|
531
545
|
}
|
|
532
546
|
```
|
|
@@ -547,6 +561,7 @@ malformed file is ignored.
|
|
|
547
561
|
| `buttonColor` | Header **icon button** color (`#rrggbb`) — expand / close / attach / folder / etc., across both header rows. |
|
|
548
562
|
| `theme` | xterm palette for terminals in this directory (one of the built-in theme ids). |
|
|
549
563
|
| `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. |
|
|
564
|
+
| `fontSize` | Terminal font size in px for this directory (8–32), overriding the Settings value. A size outside the range is clamped; a non-number is ignored. Changing it re-fits the terminal, so the PTY learns the new width — unlike browser zoom, which leaves the two disagreeing. |
|
|
550
565
|
| `sound` | Attention sound for this directory's sessions, a path **relative to the directory** (served at `GET /api/dir-sound`). |
|
|
551
566
|
|
|
552
567
|
**Security.** `sound` is a directory-relative path only — absolute paths and any
|
|
@@ -835,7 +850,10 @@ Favorited collections get their own toolbar buttons.
|
|
|
835
850
|
- **Notifications** (🔔) — a toolbar bell with an unread badge and a dropdown of active
|
|
836
851
|
notifications; click a row to jump to its session.
|
|
837
852
|
- **Voice input** — dictate a prompt via on-device Whisper (`POST /api/transcribe`, macOS
|
|
838
|
-
only; the model downloads on first use).
|
|
853
|
+
only; the model downloads on first use). Settings picks **the language you dictate in**
|
|
854
|
+
(per browser): your browser's, whisper's own per-clip detection, or a fixed one. Worth
|
|
855
|
+
setting — speech in a language the mic is not expecting comes back *translated* into the
|
|
856
|
+
one it is, so an English browser silently turned Japanese dictation into English.
|
|
839
857
|
- **Remote host** — link MulmoTerminal to the companion phone client (Google sign-in) to
|
|
840
858
|
watch and start sessions from your phone.
|
|
841
859
|
- **Themes** — four terminal palettes (midnight / nord / daylight / solarized), your pick
|
package/bin/mulmoterminal.js
CHANGED
|
@@ -76,6 +76,31 @@ function claudeInstalled() {
|
|
|
76
76
|
return hasCommand("claude");
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
// PATH tools the app shells out to; mirrors the requirements table in README.md. `required`
|
|
80
|
+
// ones back the core grid — without them a developer loses whole views rather than one
|
|
81
|
+
// feature — so a miss is an ✗, not an ○.
|
|
82
|
+
const PATH_TOOLS = [
|
|
83
|
+
{ cmd: "git", versionArg: "--version", required: true, why: "worktrees, per-cell branch/diff, PR footer", hint: "brew install git · apt install git" },
|
|
84
|
+
{ cmd: "gh", versionArg: "--version", required: true, why: "PRs & Issues view + one-click PRs", hint: "https://cli.github.com (then: gh auth login)" },
|
|
85
|
+
{ cmd: "tmux", versionArg: "-V", required: false, why: "sessions survive a restart", hint: "brew install tmux · apt install tmux" },
|
|
86
|
+
{ cmd: "codex", versionArg: "--version", required: false, why: "run OpenAI Codex as an agent", hint: "npm install -g @openai/codex" },
|
|
87
|
+
{ cmd: "docker", versionArg: "--version", required: false, why: "the experimental Docker sandbox", hint: "https://docs.docker.com/get-started/get-docker/" },
|
|
88
|
+
{
|
|
89
|
+
cmd: "ffmpeg",
|
|
90
|
+
versionArg: "-version",
|
|
91
|
+
required: false,
|
|
92
|
+
why: "video rendering in the mulmo-script panel",
|
|
93
|
+
hint: "brew install ffmpeg · apt install ffmpeg",
|
|
94
|
+
},
|
|
95
|
+
{ cmd: "ollama", versionArg: "--version", required: false, why: "a fully local model via claude-ollama", hint: "https://ollama.com/download" },
|
|
96
|
+
];
|
|
97
|
+
|
|
98
|
+
function toolCheckLine({ cmd, versionArg, required, why, hint }) {
|
|
99
|
+
if (hasCommand(cmd, versionArg)) return ` ✓ ${cmd} — ${why}`;
|
|
100
|
+
const head = required ? ` ✗ ${cmd} — not found, needed for ${why}` : ` ○ ${cmd} — optional (${why})`;
|
|
101
|
+
return `${head}\n → ${hint}`;
|
|
102
|
+
}
|
|
103
|
+
|
|
79
104
|
function promptYesNo(question) {
|
|
80
105
|
return new Promise((res) => {
|
|
81
106
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
@@ -103,13 +128,7 @@ async function runInit(initArgs) {
|
|
|
103
128
|
console.log(" → npm install -g @anthropic-ai/claude-code (then run `claude` and log in)");
|
|
104
129
|
}
|
|
105
130
|
|
|
106
|
-
|
|
107
|
-
["tmux", "-V", "sessions survive a restart", "brew install tmux · apt install tmux"],
|
|
108
|
-
["gh", "--version", "PRs & Issues view + one-click PRs", "https://cli.github.com (then: gh auth login)"],
|
|
109
|
-
["codex", "--version", "run OpenAI Codex as an agent", "npm install -g @openai/codex"],
|
|
110
|
-
]) {
|
|
111
|
-
console.log(hasCommand(cmd, versionArg) ? ` ✓ ${cmd} — ${why}` : ` ○ ${cmd} — optional (${why})\n → ${hint}`);
|
|
112
|
-
}
|
|
131
|
+
PATH_TOOLS.forEach((tool) => console.log(toolCheckLine(tool)));
|
|
113
132
|
|
|
114
133
|
// Config half: derive working-dir presets from Claude history + write config.json.
|
|
115
134
|
console.log("");
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// How many lines each row of the cockpit roster (the list beside a zoomed terminal) shows before
|
|
2
|
+
// it clamps. The roster trades two things against each other: how many sessions fit on screen,
|
|
3
|
+
// and how much of each one you can read without opening it. 2/2/3 is the tight end — it fits a
|
|
4
|
+
// long roster, but a summary written as a full sentence gets cut mid-thought, which is exactly
|
|
5
|
+
// when you wanted to read it. That is a trade the user should get to make, not a bug to fix, so
|
|
6
|
+
// the defaults stay put and the knob is opt-in.
|
|
7
|
+
//
|
|
8
|
+
// One knob per field: the three are worth different amounts. A summary says what a session is
|
|
9
|
+
// doing NOW, while a prompt is usually done in two lines.
|
|
10
|
+
//
|
|
11
|
+
// Shared because both sides decide from it: the server sanitizes and serves it, the grid renders
|
|
12
|
+
// from it. The CSS that applies the clamp is NOT here — the server has no use for it (#877).
|
|
13
|
+
|
|
14
|
+
import { isRecord } from "./isRecord.js";
|
|
15
|
+
|
|
16
|
+
export interface CockpitLines {
|
|
17
|
+
summary: number;
|
|
18
|
+
prompt: number;
|
|
19
|
+
response: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const DEFAULT_COCKPIT_LINES: CockpitLines = { summary: 2, prompt: 2, response: 3 };
|
|
23
|
+
|
|
24
|
+
// 1 is "one line, still clamped". Past ~20 a single row fills the column and the roster stops
|
|
25
|
+
// being a roster.
|
|
26
|
+
export const COCKPIT_LINES_MIN = 1;
|
|
27
|
+
export const COCKPIT_LINES_MAX = 20;
|
|
28
|
+
|
|
29
|
+
// Clamp an out-of-range number and round a fractional one — the contract normalizeFontSize
|
|
30
|
+
// states for the font size, for the same reason: on a bounded number the user asked for a
|
|
31
|
+
// DIRECTION, so honouring it reads as working where falling back to the default reads as
|
|
32
|
+
// ignored. Non-numeric is a different thing — "nothing configured here" — and falls back.
|
|
33
|
+
const oneField = (input: unknown, fallback: number): number => {
|
|
34
|
+
if (typeof input !== "number" || !Number.isFinite(input)) return fallback;
|
|
35
|
+
return Math.min(COCKPIT_LINES_MAX, Math.max(COCKPIT_LINES_MIN, Math.round(input)));
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/** Per field, so one typo can't discard the two the user set correctly. */
|
|
39
|
+
export function sanitizeCockpitLines(input: unknown): CockpitLines {
|
|
40
|
+
if (!isRecord(input)) return { ...DEFAULT_COCKPIT_LINES };
|
|
41
|
+
return {
|
|
42
|
+
summary: oneField(input.summary, DEFAULT_COCKPIT_LINES.summary),
|
|
43
|
+
prompt: oneField(input.prompt, DEFAULT_COCKPIT_LINES.prompt),
|
|
44
|
+
response: oneField(input.response, DEFAULT_COCKPIT_LINES.response),
|
|
45
|
+
};
|
|
46
|
+
}
|
package/common/dirChrome.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// The
|
|
1
|
+
// The `.mulmoterminal.json` fields whose type is the SAME on both sides. Shared across the
|
|
2
2
|
// build boundary: the server's DirConfig/PublicDirConfig and the client's DirConfig
|
|
3
3
|
// all extend this, so a field added on one side can't go missing on the other.
|
|
4
4
|
// `theme` and `colors` stay out of it — each side declares them with its own type
|
|
@@ -17,6 +17,10 @@ export interface DirChrome {
|
|
|
17
17
|
cellBorderColor: string | null;
|
|
18
18
|
dotColor: string | null;
|
|
19
19
|
buttonColor: string | null;
|
|
20
|
+
// xterm font size in px for terminals opened here, or null to use the global setting.
|
|
21
|
+
// Unlike the colors above, this changes the cell metrics — every path that applies it has
|
|
22
|
+
// to re-fit and push the new cols/rows to the PTY, or the grid drifts from the canvas.
|
|
23
|
+
fontSize: number | null;
|
|
20
24
|
}
|
|
21
25
|
|
|
22
26
|
// "Nothing configured" — the base every DirConfig/PublicDirConfig empty spreads, so adding
|
|
@@ -32,4 +36,5 @@ export const EMPTY_DIR_CHROME: Readonly<DirChrome> = {
|
|
|
32
36
|
cellBorderColor: null,
|
|
33
37
|
dotColor: null,
|
|
34
38
|
buttonColor: null,
|
|
39
|
+
fontSize: null,
|
|
35
40
|
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// The xterm font size, in px. Shared because all three sides decide from the same numbers:
|
|
2
|
+
// the server validates `.mulmoterminal.json`, the Settings stepper bounds its own buttons, and
|
|
3
|
+
// the client validates what it reads back out of localStorage.
|
|
4
|
+
export const TERMINAL_FONT_SIZE_DEFAULT = 14;
|
|
5
|
+
// Below ~8px the canvas renderer's glyphs stop being legible; above ~32px a grid cell holds so
|
|
6
|
+
// few columns that Claude's output wraps into noise. Neither is a hard xterm limit — they bound
|
|
7
|
+
// the stepper and reject a value that would leave the terminal unusable.
|
|
8
|
+
export const TERMINAL_FONT_SIZE_MIN = 8;
|
|
9
|
+
export const TERMINAL_FONT_SIZE_MAX = 32;
|
|
10
|
+
export const TERMINAL_FONT_SIZE_STEP = 1;
|
|
11
|
+
|
|
12
|
+
// Clamp rather than reject an out-of-range number: for a size, honouring the direction the user
|
|
13
|
+
// asked for reads as working, where falling back to the default reads as ignored. Non-numeric
|
|
14
|
+
// input is a different thing — "nothing configured here" — so it stays null and lets the caller
|
|
15
|
+
// fall back. Fractional sizes round: xterm accepts them, but they make cell metrics drift from
|
|
16
|
+
// the integer the UI shows.
|
|
17
|
+
export function normalizeFontSize(input: unknown): number | null {
|
|
18
|
+
if (typeof input !== "number" || !Number.isFinite(input)) return null;
|
|
19
|
+
return Math.min(TERMINAL_FONT_SIZE_MAX, Math.max(TERMINAL_FONT_SIZE_MIN, Math.round(input)));
|
|
20
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// The `GET /api/transcribe/model` response. The server builds it; the UI decides the mic
|
|
2
|
+
// button's visibility and the settings section's from it — both sides read it, so it lives
|
|
3
|
+
// here rather than as one interface per side.
|
|
4
|
+
//
|
|
5
|
+
// Deliberately independent of whisper's own types: this is the API contract, and which
|
|
6
|
+
// package implements transcription behind it is not the UI's business. The server's
|
|
7
|
+
// `getVoiceInputStatus(): VoiceInputStatus` annotation is what keeps the two in step.
|
|
8
|
+
|
|
9
|
+
export type VoiceModelState = "idle" | "downloading" | "ready" | "error";
|
|
10
|
+
|
|
11
|
+
export interface VoiceInputStatus {
|
|
12
|
+
/** The platform and binaries are present — the mic can appear. */
|
|
13
|
+
capable: boolean;
|
|
14
|
+
model: {
|
|
15
|
+
name: string;
|
|
16
|
+
state: VoiceModelState;
|
|
17
|
+
/** 0..1, only while downloading and only when the size is known. */
|
|
18
|
+
progress?: number;
|
|
19
|
+
error?: string;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{n as e}from"./chunk-Y2CYZVJY-Bdt8pFDJ-DsF7k-Jl.js";import{f as t}from"./src-Brzfja-q-ch1VcTiG.js";import{_ as n,g as r}from"./mermaid-parser.core-DxEa8E3F-
|
|
1
|
+
import{n as e}from"./chunk-Y2CYZVJY-Bdt8pFDJ-DsF7k-Jl.js";import{f as t}from"./src-Brzfja-q-ch1VcTiG.js";import{_ as n,g as r}from"./mermaid-parser.core-DxEa8E3F-BAh0ySn4.js";import"./chunk-WYO6CB5R-BlzOfotS-BRwSb4rl.js";import"./chunk-VAUOI2AC-CS9QJ4yz-CfgcnwuB.js";import{n as i,r as a,t as o}from"./chunk-MOJQB5TN-CMZRaeqt-CKo27qok.js";import{t as s}from"./chunk-JWPE2WC7-BQ3zXr2k-DSd3Ct95.js";var c=r().RailroadAbnf.parser.LangiumParser,l=e(e=>{let t=e.alternatives.map(u);return t.length===1?t[0]:{type:`choice`,alternatives:t}},`transformAlternation`),u=e(e=>{let t=e.elements.map(f);return t.length===1?t[0]:{type:`sequence`,elements:t}},`transformConcatenation`),d=e(e=>{if(e.includes(`*`)){let[t,n]=e.split(`*`);return{min:t?parseInt(t,10):0,max:n?parseInt(n,10):1/0}}let t=parseInt(e,10);return{min:t,max:t}},`parseRepeat`),f=e(e=>{let t=p(e.primary);if(!e.repeat)return t;let{min:n,max:r}=d(e.repeat);return n===0&&r===1?{type:`optional`,element:t}:{type:`repetition`,element:t,min:n,max:r}},`transformElement`),p=e(e=>{switch(e.$type){case`AbnfStringLiteral`:return{type:`terminal`,value:e.value};case`AbnfNumVal`:return{type:`terminal`,value:e.value};case`AbnfRuleName`:return{type:`nonterminal`,name:e.name};case`AbnfGroup`:return l(e.element);case`AbnfOptionalGroup`:return{type:`optional`,element:l(e.element)};default:throw Error(`Unsupported ABNF primary node: ${e.$type}`)}},`transformPrimary`),m=e(e=>({name:e.name,definition:l(e.definition)}),`transformRule`),h=e(e=>{s(e,a),e.title&&a.setTitle(e.title),e.rules.map(e=>a.addRule(m(e)))},`populateDb`),g={parser:{parse:e(e=>{a.clear(),t.debug(`[ABNF Parser] Starting Langium parse`);let r=c.parse(e);if(r.lexerErrors.length>0||r.parserErrors.length>0)throw new n(r);let i=r.value;t.debug(`[ABNF Parser] Parsed rules:`,i.rules.length),h(i),t.debug(`[ABNF Parser] Parse complete`)},`parse`),parser:{yy:a}},db:a,renderer:o,styles:i};export{g as diagram};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{f as e}from"./mermaid-parser.core-DxEa8E3F-BAh0ySn4.js";export{e as createArchitectureServices};
|