mulmoterminal 2.0.1 → 2.1.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.
Files changed (118) hide show
  1. package/README.md +5 -2
  2. package/common/cockpitLines.ts +46 -0
  3. package/common/dirChrome.ts +6 -1
  4. package/common/terminalFontSize.ts +20 -0
  5. package/dist/assets/{abnfDiagram-VRR7QNED-RjiYivmv-CMyzg2zY.js → abnfDiagram-VRR7QNED-RjiYivmv-BMVn5P4x.js} +1 -1
  6. package/dist/assets/architecture-TIHT7OUA-0NsD2xW1-JfeahpZ_.js +1 -0
  7. package/dist/assets/{architectureDiagram-ZJ3FMSHR-3nWA91tG-CSIT7RtO.js → architectureDiagram-ZJ3FMSHR-3nWA91tG-BaVwq5YO.js} +1 -1
  8. package/dist/assets/{blockDiagram-677ZJIJ3-BPuAJQRW-CnVQRl8Q.js → blockDiagram-677ZJIJ3-BPuAJQRW-O8HvKllF.js} +1 -1
  9. package/dist/assets/{c4Diagram-LMCZKHZV-C0LAqQso-CrpIWU_7.js → c4Diagram-LMCZKHZV-C0LAqQso-qV_LVJW3.js} +1 -1
  10. package/dist/assets/channel-7wUqSdoX-C4JGl6VC.js +1 -0
  11. package/dist/assets/{chunk-32BRIVSS-BRrYpgtb-D_PiqrVh.js → chunk-32BRIVSS-BRrYpgtb-B9twQFRU.js} +1 -1
  12. package/dist/assets/{chunk-52WLFC77-BJ-ss3Xr-BCpG-w5L.js → chunk-52WLFC77-BJ-ss3Xr-OC_lubzj.js} +1 -1
  13. package/dist/assets/{chunk-C7G6YPKG-BZEucKEL-CP9wac26.js → chunk-C7G6YPKG-BZEucKEL-mCVH59ut.js} +1 -1
  14. package/dist/assets/{chunk-EX3LRPZG-DLS6FBN1-BJZF7tEb.js → chunk-EX3LRPZG-DLS6FBN1-CL8lofeg.js} +1 -1
  15. package/dist/assets/{chunk-FWX5IMBZ-DHLSFw1H-Cnk0SpZp.js → chunk-FWX5IMBZ-DHLSFw1H-BcVi6QV6.js} +2 -2
  16. package/dist/assets/{chunk-HOUHSVGY-Bhlt8hXJ-CRGS9q8z.js → chunk-HOUHSVGY-Bhlt8hXJ-D3M0sdaj.js} +1 -1
  17. package/dist/assets/{chunk-ICXQ74PX-DwgHBX_g-DpPUV9Tl.js → chunk-ICXQ74PX-DwgHBX_g-9pz8KjdB.js} +1 -1
  18. package/dist/assets/{chunk-MOJQB5TN-CMZRaeqt-CG59XkeN.js → chunk-MOJQB5TN-CMZRaeqt-DsiY753w.js} +1 -1
  19. package/dist/assets/{chunk-OGEWGWER-8Qy4a8b5-B59YGczL.js → chunk-OGEWGWER-8Qy4a8b5-DzD8vBv9.js} +1 -1
  20. package/dist/assets/{chunk-PUDLZKDR-DcrWQRYh-CXGtfuoa.js → chunk-PUDLZKDR-DcrWQRYh-B3EqHnC7.js} +1 -1
  21. package/dist/assets/{chunk-Q4XR5HBZ-ZXVGkG8Z-eHuKxxBN.js → chunk-Q4XR5HBZ-ZXVGkG8Z-DAzhqKqQ.js} +1 -1
  22. package/dist/assets/{chunk-V7JOEXUC-DmGdheTX-BJZa8auK.js → chunk-V7JOEXUC-DmGdheTX-sWPxsoge.js} +1 -1
  23. package/dist/assets/{chunk-VAUOI2AC-CS9QJ4yz-DviHNtmN.js → chunk-VAUOI2AC-CS9QJ4yz-C6T0-Q0n.js} +1 -1
  24. package/dist/assets/{chunk-VR4S4FIN-C6a91eNY-DsA9hbD5.js → chunk-VR4S4FIN-C6a91eNY-CzCW244T.js} +1 -1
  25. package/dist/assets/{chunk-WYO6CB5R-BlzOfotS-qckQG4MR.js → chunk-WYO6CB5R-BlzOfotS-BRhqiyoJ.js} +1 -1
  26. package/dist/assets/{chunk-ZGVPDNZ5-BYwxNFTK-Cr8j6FMw.js → chunk-ZGVPDNZ5-BYwxNFTK-2uN_ofIP.js} +1 -1
  27. package/dist/assets/classDiagram-OUVF2IWQ-BYlRi6YQ-DtJISQUb.js +1 -0
  28. package/dist/assets/classDiagram-v2-EOCWNBFH-BCHaYHxj-DtJISQUb.js +1 -0
  29. package/dist/assets/cynefin-VYW2F7L2-CGYr2F6h-DB6705af.js +1 -0
  30. package/dist/assets/{cynefinDiagram-TSTJHNR4-DHA9iPo--BuPVUHaQ.js → cynefinDiagram-TSTJHNR4-DHA9iPo--rvLMXkNB.js} +1 -1
  31. package/dist/assets/{dagre-VKFMJZFB--oJKqXBZ-l8CP-U85.js → dagre-VKFMJZFB--oJKqXBZ-CGanW7aY.js} +1 -1
  32. package/dist/assets/{diagram-FQU43EPY-CcJCB9bG-CV6yO6d6.js → diagram-FQU43EPY-CcJCB9bG-BcrxMiAD.js} +1 -1
  33. package/dist/assets/{diagram-G47NLZAW-C_o-WGG1-DmUMCyqP.js → diagram-G47NLZAW-C_o-WGG1-4PTH1NOO.js} +1 -1
  34. package/dist/assets/{diagram-NH7WQ7WH-CXJCYvY--D1Zcc9sH.js → diagram-NH7WQ7WH-CXJCYvY--DLVaK2vZ.js} +1 -1
  35. package/dist/assets/{diagram-OA4YK3LP-BMzeJ87A-CsC2S-ug.js → diagram-OA4YK3LP-BMzeJ87A-C0axta9a.js} +1 -1
  36. package/dist/assets/{diagram-WEI45ONY-D_93NKqo-C3Xq1urQ.js → diagram-WEI45ONY-D_93NKqo-3zfR6xo1.js} +1 -1
  37. package/dist/assets/{ebnfDiagram-CCIWWBDH-CVai1Ii9-BtjcwSW_.js → ebnfDiagram-CCIWWBDH-CVai1Ii9-Bfd3ExQu.js} +1 -1
  38. package/dist/assets/{erDiagram-Q63AITRT-CiABnA0s-DhQMPhj9.js → erDiagram-Q63AITRT-CiABnA0s-BGq_5dj0.js} +1 -1
  39. package/dist/assets/eventmodeling-45OFAUF4-CWmqhm3a-Bl19zj9_.js +1 -0
  40. package/dist/assets/flowDiagram-23GEKE2U-DKSB5AiY-DdIGCbvn.js +1 -0
  41. package/dist/assets/{ganttDiagram-NO4QXBWP-DQZvdFo1-Bw2aYbCJ.js → ganttDiagram-NO4QXBWP-DQZvdFo1-z8Owlyg6.js} +1 -1
  42. package/dist/assets/gitGraph-TEB2WS4Q-BR9qPwdN-BnZW4EXW.js +1 -0
  43. package/dist/assets/{gitGraphDiagram-IHSO6WYX-C2ovBouh-C1Dlsswx.js → gitGraphDiagram-IHSO6WYX-C2ovBouh-BA-6Yy_K.js} +1 -1
  44. package/dist/assets/{index-Dwc5wfJg.js → index-DbKckN7Z.js} +196 -196
  45. package/dist/assets/index-u-r21U2t.css +1 -0
  46. package/dist/assets/info-DKCQHKI2-Dplx5kMp-CrJfKOA1.js +1 -0
  47. package/dist/assets/{infoDiagram-FWYZ7A6U-7UnoB5AP-CjB00vD7.js → infoDiagram-FWYZ7A6U-7UnoB5AP-BW36KQ8e.js} +1 -1
  48. package/dist/assets/{ishikawaDiagram-FXEZZL3T-ByUDM_N2-BxVGf9t3.js → ishikawaDiagram-FXEZZL3T-ByUDM_N2-DrQDG1os.js} +1 -1
  49. package/dist/assets/{journeyDiagram-5HDEW3XC-c5xIah9o-BPLsgkBp.js → journeyDiagram-5HDEW3XC-c5xIah9o-CtJ0D_mQ.js} +1 -1
  50. package/dist/assets/{kanban-definition-HUTT4EX6-Cnt6loYD-C0UF5ruR.js → kanban-definition-HUTT4EX6-Cnt6loYD-bm-RqJor.js} +1 -1
  51. package/dist/assets/{lib-Ca9R1hlX.js → lib-BmwIEKvb.js} +1 -1
  52. package/dist/assets/{line-D7ziSjKi-B4ejYAqw.js → line-D7ziSjKi-DrYnqDhF.js} +1 -1
  53. package/dist/assets/{marp-J4wSkl3Q.js → marp-gEV1mQM4.js} +1 -1
  54. package/dist/assets/{mermaid-parser.core-DxEa8E3F-D1ef8DoR.js → mermaid-parser.core-DxEa8E3F-iymJ5XTw.js} +2 -2
  55. package/dist/assets/{mermaid.core-V0OYwIz3-BSn9HyWz.js → mermaid.core-V0OYwIz3-CEFC0tc3.js} +3 -3
  56. package/dist/assets/{mindmap-definition-LN4V7U3C-CexN3O6L-BLTr4gMi.js → mindmap-definition-LN4V7U3C-CexN3O6L-maxHe_oT.js} +1 -1
  57. package/dist/assets/packet-7NZHBO7P-CQI3flND-mf_ng5Fx.js +1 -0
  58. package/dist/assets/{pegDiagram-2B236MQR-BiM4G0cW-E_eG9LVV.js → pegDiagram-2B236MQR-BiM4G0cW-CrLCH9dK.js} +1 -1
  59. package/dist/assets/pie-RZYD4A2V-CnmKE2Wa-CSySnxGv.js +1 -0
  60. package/dist/assets/{pieDiagram-ENE6RG2P-B_fBBS-2-f4ZSZ6GC.js → pieDiagram-ENE6RG2P-B_fBBS-2-DVbko3rP.js} +1 -1
  61. package/dist/assets/{quadrantDiagram-ABIIQ3AL-BigGVxCR-DnwDJxmc.js → quadrantDiagram-ABIIQ3AL-BigGVxCR-DUwLLJXe.js} +1 -1
  62. package/dist/assets/radar-I7S5WNFK-us6x-Z9R-DfA0aimG.js +1 -0
  63. package/dist/assets/railroad-3IZDKUUU-BKdz6pdN-T35PQ5gg.js +1 -0
  64. package/dist/assets/railroad-abnf-AHOZXSZD-m_CSSIfd-DavwfL2m.js +1 -0
  65. package/dist/assets/railroad-ebnf-EBAXGLYW-Nu3nAG7C-Yk8Oj2bi.js +1 -0
  66. package/dist/assets/railroad-peg-LSFZ7HO6--Y3mD4R_-DOPPkkTw.js +1 -0
  67. package/dist/assets/{railroadDiagram-RFXS5EU6--lNIlG64-BUH_ocB5.js → railroadDiagram-RFXS5EU6--lNIlG64-t4pt5DRk.js} +1 -1
  68. package/dist/assets/{requirementDiagram-TGXJPOKE-D-B0Y5Wu-CT5zmLHk.js → requirementDiagram-TGXJPOKE-D-B0Y5Wu-DJVGeSnt.js} +1 -1
  69. package/dist/assets/{sankeyDiagram-HTMAVEWB-DlutaXDv-9sorx_WA.js → sankeyDiagram-HTMAVEWB-DlutaXDv-2m-PGq5V.js} +1 -1
  70. package/dist/assets/{sequenceDiagram-DBY2YBRQ-BOLgtggH-CnjR9ja7.js → sequenceDiagram-DBY2YBRQ-BOLgtggH-DSwO6_CW.js} +1 -1
  71. package/dist/assets/{stateDiagram-2N3HPSRC-jf_dXUEU-DboFu0BL.js → stateDiagram-2N3HPSRC-jf_dXUEU-DuBsukdi.js} +1 -1
  72. package/dist/assets/stateDiagram-v2-6OUMAXLB-CuzgCL2v-BuaQTnnn.js +1 -0
  73. package/dist/assets/{swimlanes-5IMT3BWC-CYjtALQH-Cqxsp00w.js → swimlanes-5IMT3BWC-CYjtALQH-BwT2NtP8.js} +1 -1
  74. package/dist/assets/swimlanesDiagram-G3AALYLV-3bmSBtKs-CVMbYKXX.js +8 -0
  75. package/dist/assets/{timeline-definition-FHXFAJF6-DYJ4oUm8-Du08XOrV.js → timeline-definition-FHXFAJF6-DYJ4oUm8-B7uglxWU.js} +1 -1
  76. package/dist/assets/treeView-QDETBFTQ-D27HWG6Q-DSRzdn6E.js +1 -0
  77. package/dist/assets/treemap-6X3UGDF4-CmGIU3H5-pjqGjUmo.js +1 -0
  78. package/dist/assets/{vennDiagram-L72KCM5P-CdKAHoek-DtBtukcO.js → vennDiagram-L72KCM5P-CdKAHoek-Bi97b0Q1.js} +1 -1
  79. package/dist/assets/wardley-OPB4EBWU-CbHWsb2k-_qoOv9rX.js +1 -0
  80. package/dist/assets/{wardleyDiagram-EHGQE667-BF6c4_CW-CW4nfoJ3.js → wardleyDiagram-EHGQE667-BF6c4_CW-DuE3v_zR.js} +1 -1
  81. package/dist/assets/{xychartDiagram-FW5EYKEG-CGiKngj7-Ruux6STV.js → xychartDiagram-FW5EYKEG-CGiKngj7-B0CJGTzS.js} +1 -1
  82. package/dist/index.html +2 -2
  83. package/package.json +1 -1
  84. package/server/config/app-config.ts +41 -15
  85. package/server/config/config-routes.ts +13 -0
  86. package/server/config/config-schema.ts +12 -0
  87. package/server/config/dir-config.ts +18 -2
  88. package/server/config/header-config.ts +4 -4
  89. package/server/git/pr-footer.ts +20 -0
  90. package/server/git/worktree-pr.ts +32 -4
  91. package/server/index.ts +1 -1
  92. package/server/infra/tmux-routes.ts +1 -1
  93. package/server/session/pty-connection.ts +1 -1
  94. package/server/session/scheduled-sessions.ts +2 -2
  95. package/server/skills/mulmoterminal-config/SKILL.md +45 -12
  96. package/server/skills/mulmoterminal-config/palettes.json +6 -6
  97. package/dist/assets/architecture-TIHT7OUA-0NsD2xW1-3kpQCFcP.js +0 -1
  98. package/dist/assets/channel-7wUqSdoX-D8_GjCXH.js +0 -1
  99. package/dist/assets/classDiagram-OUVF2IWQ-BYlRi6YQ-axLhYHqO.js +0 -1
  100. package/dist/assets/classDiagram-v2-EOCWNBFH-BCHaYHxj-axLhYHqO.js +0 -1
  101. package/dist/assets/cynefin-VYW2F7L2-CGYr2F6h-C9DtePCD.js +0 -1
  102. package/dist/assets/eventmodeling-45OFAUF4-CWmqhm3a-BCX3cvpw.js +0 -1
  103. package/dist/assets/flowDiagram-23GEKE2U-DKSB5AiY-DjZRfq7e.js +0 -1
  104. package/dist/assets/gitGraph-TEB2WS4Q-BR9qPwdN-pIAqsAx0.js +0 -1
  105. package/dist/assets/index-DVTKApMs.css +0 -1
  106. package/dist/assets/info-DKCQHKI2-Dplx5kMp-S1We2Nw2.js +0 -1
  107. package/dist/assets/packet-7NZHBO7P-CQI3flND-DaLVcMu_.js +0 -1
  108. package/dist/assets/pie-RZYD4A2V-CnmKE2Wa-BKDCOtxh.js +0 -1
  109. package/dist/assets/radar-I7S5WNFK-us6x-Z9R-Cw4aJNVl.js +0 -1
  110. package/dist/assets/railroad-3IZDKUUU-BKdz6pdN-DJZYlsdK.js +0 -1
  111. package/dist/assets/railroad-abnf-AHOZXSZD-m_CSSIfd-D9wfK70u.js +0 -1
  112. package/dist/assets/railroad-ebnf-EBAXGLYW-Nu3nAG7C-C2rW9Qx8.js +0 -1
  113. package/dist/assets/railroad-peg-LSFZ7HO6--Y3mD4R_-CCUOq6go.js +0 -1
  114. package/dist/assets/stateDiagram-v2-6OUMAXLB-CuzgCL2v-FGJ3mpM7.js +0 -1
  115. package/dist/assets/swimlanesDiagram-G3AALYLV-3bmSBtKs-B3qFCxjl.js +0 -8
  116. package/dist/assets/treeView-QDETBFTQ-D27HWG6Q-dIhNDmqB.js +0 -1
  117. package/dist/assets/treemap-6X3UGDF4-CmGIU3H5-C8SEy1d3.js +0 -1
  118. 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
  ![Single view — one agent in focus, terminal on the left and a GUI panel on the right](https://raw.githubusercontent.com/receptron/mulmoterminal/main/docs/guide/images/single-view.png)
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
@@ -434,7 +434,7 @@ The Settings modal (⚙) persists per-user UI choices to `~/.mulmoterminal/confi
434
434
 
435
435
  ![The Settings modal — theme, notification sound, PR repos, launch commands, and MCP servers](https://raw.githubusercontent.com/receptron/mulmoterminal/main/docs/guide/images/settings.png)
436
436
 
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.*
437
+ *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
438
 
439
439
  | Field | Meaning |
440
440
  | ------------ | ------- |
@@ -451,6 +451,7 @@ The Settings modal (⚙) persists per-user UI choices to `~/.mulmoterminal/confi
451
451
  | `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
452
  | `worklogIntervalHours` | Worklog cadence in hours (default `6`, clamped to `1`–`168`). |
453
453
  | `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). |
454
+ | `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
455
 
455
456
  #### Header buttons
456
457
 
@@ -527,6 +528,7 @@ malformed file is ignored.
527
528
  "buttonColor": "#c7cdf0", // header icon buttons (hex #rrggbb)
528
529
  "theme": "nord", // terminal palette: midnight | nord | daylight | solarized
529
530
  "colors": { "background": "#190a23", "cursor": "#ff2e63" }, // per-key palette overrides
531
+ "fontSize": 16, // terminal font size in px (8–32); overrides Settings
530
532
  "sound": "./.mulmoterminal/alert.mp3" // attention sound, RELATIVE to this directory
531
533
  }
532
534
  ```
@@ -547,6 +549,7 @@ malformed file is ignored.
547
549
  | `buttonColor` | Header **icon button** color (`#rrggbb`) — expand / close / attach / folder / etc., across both header rows. |
548
550
  | `theme` | xterm palette for terminals in this directory (one of the built-in theme ids). |
549
551
  | `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. |
552
+ | `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
553
  | `sound` | Attention sound for this directory's sessions, a path **relative to the directory** (served at `GET /api/dir-sound`). |
551
554
 
552
555
  **Security.** `sound` is a directory-relative path only — absolute paths and any
@@ -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
+ }
@@ -1,4 +1,4 @@
1
- // The name + fixed-color fields a `.mulmoterminal.json` may set. Shared across 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
+ }
@@ -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-D1ef8DoR.js";import"./chunk-WYO6CB5R-BlzOfotS-qckQG4MR.js";import"./chunk-VAUOI2AC-CS9QJ4yz-DviHNtmN.js";import{n as i,r as a,t as o}from"./chunk-MOJQB5TN-CMZRaeqt-CG59XkeN.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};
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-iymJ5XTw.js";import"./chunk-WYO6CB5R-BlzOfotS-BRhqiyoJ.js";import"./chunk-VAUOI2AC-CS9QJ4yz-C6T0-Q0n.js";import{n as i,r as a,t as o}from"./chunk-MOJQB5TN-CMZRaeqt-DsiY753w.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-iymJ5XTw.js";export{e as createArchitectureServices};