mulmoterminal 2.3.0 → 2.4.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 (116) hide show
  1. package/README.md +18 -0
  2. package/bin/cli-args.d.ts +1 -0
  3. package/bin/cli-args.js +16 -0
  4. package/bin/mulmoterminal.js +2 -1
  5. package/common/dirConfigSource.ts +101 -0
  6. package/dist/assets/{abnfDiagram-VRR7QNED-RjiYivmv-B8nu2pUR.js → abnfDiagram-VRR7QNED-RjiYivmv-CqId6L6N.js} +1 -1
  7. package/dist/assets/architecture-TIHT7OUA-0NsD2xW1-CfmmIOkB.js +1 -0
  8. package/dist/assets/{architectureDiagram-ZJ3FMSHR-3nWA91tG-ByxXyiOP.js → architectureDiagram-ZJ3FMSHR-3nWA91tG-CWhhLcfM.js} +1 -1
  9. package/dist/assets/{blockDiagram-677ZJIJ3-BPuAJQRW-Brt9e7H_.js → blockDiagram-677ZJIJ3-BPuAJQRW-89yfYblY.js} +1 -1
  10. package/dist/assets/{c4Diagram-LMCZKHZV-C0LAqQso-D598g8JV.js → c4Diagram-LMCZKHZV-C0LAqQso-Cq70UauP.js} +1 -1
  11. package/dist/assets/channel-7wUqSdoX-Bc9EmVWS.js +1 -0
  12. package/dist/assets/{chunk-32BRIVSS-BRrYpgtb-BEOPPcME.js → chunk-32BRIVSS-BRrYpgtb-Bj5FdJ3A.js} +1 -1
  13. package/dist/assets/{chunk-52WLFC77-BJ-ss3Xr-BmZ-jgWV.js → chunk-52WLFC77-BJ-ss3Xr-Cf6wft_5.js} +1 -1
  14. package/dist/assets/{chunk-C7G6YPKG-BZEucKEL-DhvCnMVg.js → chunk-C7G6YPKG-BZEucKEL-CLA5COUu.js} +1 -1
  15. package/dist/assets/{chunk-EX3LRPZG-DLS6FBN1-CxeaHZOV.js → chunk-EX3LRPZG-DLS6FBN1-ChsLH6_y.js} +1 -1
  16. package/dist/assets/{chunk-FWX5IMBZ-DHLSFw1H-COykUZUW.js → chunk-FWX5IMBZ-DHLSFw1H-BIvnIbPU.js} +2 -2
  17. package/dist/assets/{chunk-HOUHSVGY-Bhlt8hXJ-DFKJuoMh.js → chunk-HOUHSVGY-Bhlt8hXJ-Ck-Dxtw-.js} +1 -1
  18. package/dist/assets/{chunk-ICXQ74PX-DwgHBX_g-CFoH6Yjf.js → chunk-ICXQ74PX-DwgHBX_g-CSlWVcWs.js} +1 -1
  19. package/dist/assets/{chunk-MOJQB5TN-CMZRaeqt-Dy6S_sS_.js → chunk-MOJQB5TN-CMZRaeqt-CYfpp2V7.js} +1 -1
  20. package/dist/assets/{chunk-OGEWGWER-8Qy4a8b5-XcjK3kCc.js → chunk-OGEWGWER-8Qy4a8b5-DPIlRee4.js} +1 -1
  21. package/dist/assets/{chunk-PUDLZKDR-DcrWQRYh-CFVQ5Aq8.js → chunk-PUDLZKDR-DcrWQRYh-J86vFVAN.js} +1 -1
  22. package/dist/assets/{chunk-Q4XR5HBZ-ZXVGkG8Z-D2iTH8ap.js → chunk-Q4XR5HBZ-ZXVGkG8Z-D-40tbjk.js} +1 -1
  23. package/dist/assets/{chunk-V7JOEXUC-DmGdheTX-Dfius2MW.js → chunk-V7JOEXUC-DmGdheTX-7gJmS-F9.js} +1 -1
  24. package/dist/assets/{chunk-VAUOI2AC-CS9QJ4yz-Dd_VpRsu.js → chunk-VAUOI2AC-CS9QJ4yz-DFjCsQc6.js} +1 -1
  25. package/dist/assets/{chunk-VR4S4FIN-C6a91eNY-U_jwmwMS.js → chunk-VR4S4FIN-C6a91eNY-Ba059rhB.js} +1 -1
  26. package/dist/assets/{chunk-WYO6CB5R-BlzOfotS-04XZ1Jfd.js → chunk-WYO6CB5R-BlzOfotS-CTDodsnw.js} +1 -1
  27. package/dist/assets/{chunk-ZGVPDNZ5-BYwxNFTK-DBPsh-p3.js → chunk-ZGVPDNZ5-BYwxNFTK-CMF9FJ7K.js} +1 -1
  28. package/dist/assets/classDiagram-OUVF2IWQ-BYlRi6YQ-Dk_90vZD.js +1 -0
  29. package/dist/assets/classDiagram-v2-EOCWNBFH-BCHaYHxj-Dk_90vZD.js +1 -0
  30. package/dist/assets/cynefin-VYW2F7L2-CGYr2F6h-B1MlaJhj.js +1 -0
  31. package/dist/assets/{cynefinDiagram-TSTJHNR4-DHA9iPo--D-iTSsn0.js → cynefinDiagram-TSTJHNR4-DHA9iPo--BUwfErNr.js} +1 -1
  32. package/dist/assets/{dagre-VKFMJZFB--oJKqXBZ-G72EKlyP.js → dagre-VKFMJZFB--oJKqXBZ-FEoLipSZ.js} +1 -1
  33. package/dist/assets/{diagram-FQU43EPY-CcJCB9bG-DgT-eqMs.js → diagram-FQU43EPY-CcJCB9bG-DwH2nER4.js} +1 -1
  34. package/dist/assets/{diagram-G47NLZAW-C_o-WGG1-D_JxH6o2.js → diagram-G47NLZAW-C_o-WGG1-BuSqgofP.js} +1 -1
  35. package/dist/assets/{diagram-NH7WQ7WH-CXJCYvY--pVhp9uBi.js → diagram-NH7WQ7WH-CXJCYvY--CoMsubYQ.js} +1 -1
  36. package/dist/assets/{diagram-OA4YK3LP-BMzeJ87A-CFoCoA1K.js → diagram-OA4YK3LP-BMzeJ87A-Ds_49Q4I.js} +1 -1
  37. package/dist/assets/{diagram-WEI45ONY-D_93NKqo-COhtVv5k.js → diagram-WEI45ONY-D_93NKqo-DeDTEgI-.js} +1 -1
  38. package/dist/assets/{ebnfDiagram-CCIWWBDH-CVai1Ii9-cHbupDvE.js → ebnfDiagram-CCIWWBDH-CVai1Ii9-B84fKP1h.js} +1 -1
  39. package/dist/assets/{erDiagram-Q63AITRT-CiABnA0s-Ch6F8aLL.js → erDiagram-Q63AITRT-CiABnA0s-QGRolPYH.js} +1 -1
  40. package/dist/assets/eventmodeling-45OFAUF4-CWmqhm3a-C0eB3veQ.js +1 -0
  41. package/dist/assets/flowDiagram-23GEKE2U-DKSB5AiY-BAfA7eXR.js +1 -0
  42. package/dist/assets/{ganttDiagram-NO4QXBWP-DQZvdFo1-DcmaS_P9.js → ganttDiagram-NO4QXBWP-DQZvdFo1-BBXcle76.js} +1 -1
  43. package/dist/assets/gitGraph-TEB2WS4Q-BR9qPwdN-CotjI6SA.js +1 -0
  44. package/dist/assets/{gitGraphDiagram-IHSO6WYX-C2ovBouh-CgaMpUqZ.js → gitGraphDiagram-IHSO6WYX-C2ovBouh-BxGBV6Hj.js} +1 -1
  45. package/dist/assets/index-B7LbAi10.css +1 -0
  46. package/dist/assets/{index-QEGG3YTz.js → index-_XRnaxAj.js} +202 -202
  47. package/dist/assets/info-DKCQHKI2-Dplx5kMp-DKHWzOor.js +1 -0
  48. package/dist/assets/{infoDiagram-FWYZ7A6U-7UnoB5AP-f2daXl9J.js → infoDiagram-FWYZ7A6U-7UnoB5AP-DCqG4iB9.js} +1 -1
  49. package/dist/assets/{ishikawaDiagram-FXEZZL3T-ByUDM_N2-DYE7Ld9-.js → ishikawaDiagram-FXEZZL3T-ByUDM_N2-Dkzr63Uo.js} +1 -1
  50. package/dist/assets/{journeyDiagram-5HDEW3XC-c5xIah9o-B0woNVtP.js → journeyDiagram-5HDEW3XC-c5xIah9o-3K64pBTH.js} +1 -1
  51. package/dist/assets/{kanban-definition-HUTT4EX6-Cnt6loYD-Dm8evmYs.js → kanban-definition-HUTT4EX6-Cnt6loYD-B0U7Mkg9.js} +1 -1
  52. package/dist/assets/{lib-DtgnGSMW.js → lib-rFkGOb1O.js} +1 -1
  53. package/dist/assets/{line-D7ziSjKi-CRwDdfSt.js → line-D7ziSjKi-D25izB30.js} +1 -1
  54. package/dist/assets/{marp-Djuncd6G.js → marp-79PhoGbq.js} +1 -1
  55. package/dist/assets/{mermaid-parser.core-DxEa8E3F-BILJo_fK.js → mermaid-parser.core-DxEa8E3F-BzSqp82H.js} +2 -2
  56. package/dist/assets/{mermaid.core-V0OYwIz3-CabnyPOO.js → mermaid.core-V0OYwIz3-0p62b-zk.js} +3 -3
  57. package/dist/assets/{mindmap-definition-LN4V7U3C-CexN3O6L-DF1aNqGu.js → mindmap-definition-LN4V7U3C-CexN3O6L-BzFX06oy.js} +1 -1
  58. package/dist/assets/packet-7NZHBO7P-CQI3flND-DyAt_QAV.js +1 -0
  59. package/dist/assets/{pegDiagram-2B236MQR-BiM4G0cW-q-89nsVP.js → pegDiagram-2B236MQR-BiM4G0cW-BMRJsJZN.js} +1 -1
  60. package/dist/assets/pie-RZYD4A2V-CnmKE2Wa-nlXXCSHg.js +1 -0
  61. package/dist/assets/{pieDiagram-ENE6RG2P-B_fBBS-2-B3pcC0Pg.js → pieDiagram-ENE6RG2P-B_fBBS-2-Bl2zNUvh.js} +1 -1
  62. package/dist/assets/{quadrantDiagram-ABIIQ3AL-BigGVxCR-BBKSbiVD.js → quadrantDiagram-ABIIQ3AL-BigGVxCR-BzQ392_S.js} +1 -1
  63. package/dist/assets/radar-I7S5WNFK-us6x-Z9R-Dnn5SKpT.js +1 -0
  64. package/dist/assets/railroad-3IZDKUUU-BKdz6pdN-DjF0R5Zr.js +1 -0
  65. package/dist/assets/railroad-abnf-AHOZXSZD-m_CSSIfd-GH7jGfhq.js +1 -0
  66. package/dist/assets/railroad-ebnf-EBAXGLYW-Nu3nAG7C-CH8TSRFp.js +1 -0
  67. package/dist/assets/railroad-peg-LSFZ7HO6--Y3mD4R_-QfsXYBPl.js +1 -0
  68. package/dist/assets/{railroadDiagram-RFXS5EU6--lNIlG64-Do0ZI2kT.js → railroadDiagram-RFXS5EU6--lNIlG64-D-Yn0Dpg.js} +1 -1
  69. package/dist/assets/{requirementDiagram-TGXJPOKE-D-B0Y5Wu-B4oci7Dv.js → requirementDiagram-TGXJPOKE-D-B0Y5Wu-DIYnBCWZ.js} +1 -1
  70. package/dist/assets/{sankeyDiagram-HTMAVEWB-DlutaXDv-KRXYn9rF.js → sankeyDiagram-HTMAVEWB-DlutaXDv-Dsr5VJzO.js} +1 -1
  71. package/dist/assets/{sequenceDiagram-DBY2YBRQ-BOLgtggH-zw0J8MZu.js → sequenceDiagram-DBY2YBRQ-BOLgtggH-mR1rIlJQ.js} +1 -1
  72. package/dist/assets/{stateDiagram-2N3HPSRC-jf_dXUEU-vFGH-mvy.js → stateDiagram-2N3HPSRC-jf_dXUEU-Cq2A7Udg.js} +1 -1
  73. package/dist/assets/stateDiagram-v2-6OUMAXLB-CuzgCL2v-D7ipapeO.js +1 -0
  74. package/dist/assets/{swimlanes-5IMT3BWC-CYjtALQH-DhegOTK_.js → swimlanes-5IMT3BWC-CYjtALQH-CtgZ4USf.js} +1 -1
  75. package/dist/assets/swimlanesDiagram-G3AALYLV-3bmSBtKs-D1yGYNNh.js +8 -0
  76. package/dist/assets/{timeline-definition-FHXFAJF6-DYJ4oUm8-a7d-hF5Y.js → timeline-definition-FHXFAJF6-DYJ4oUm8-CpuMCUFK.js} +1 -1
  77. package/dist/assets/treeView-QDETBFTQ-D27HWG6Q-nxCnsDvO.js +1 -0
  78. package/dist/assets/treemap-6X3UGDF4-CmGIU3H5-7UfnOcZW.js +1 -0
  79. package/dist/assets/{vennDiagram-L72KCM5P-CdKAHoek-La4xWHGC.js → vennDiagram-L72KCM5P-CdKAHoek-B-FSFzf3.js} +1 -1
  80. package/dist/assets/wardley-OPB4EBWU-CbHWsb2k-DHZIQf1x.js +1 -0
  81. package/dist/assets/{wardleyDiagram-EHGQE667-BF6c4_CW-M6-Z6fe5.js → wardleyDiagram-EHGQE667-BF6c4_CW-CB01oT0C.js} +1 -1
  82. package/dist/assets/{xychartDiagram-FW5EYKEG-CGiKngj7-BtTeKKIV.js → xychartDiagram-FW5EYKEG-CGiKngj7-D1vr6bQ-.js} +1 -1
  83. package/dist/index.html +2 -2
  84. package/package.json +1 -1
  85. package/server/config/config-routes.ts +6 -1
  86. package/server/config/dir-config.ts +83 -0
  87. package/server/config/env.ts +12 -0
  88. package/server/config/workspace.ts +17 -7
  89. package/server/index.ts +10 -5
  90. package/server/infra/allowed-origin.ts +106 -25
  91. package/server/infra/hide-error-stacks.ts +15 -0
  92. package/server/infra/spa-fallback.ts +18 -0
  93. package/server/routes/app-routes.ts +2 -2
  94. package/server/routes/dir-routes.ts +14 -2
  95. package/dist/assets/architecture-TIHT7OUA-0NsD2xW1-TY_v-qbq.js +0 -1
  96. package/dist/assets/channel-7wUqSdoX-DRUj8A2e.js +0 -1
  97. package/dist/assets/classDiagram-OUVF2IWQ-BYlRi6YQ-BVAZDjb0.js +0 -1
  98. package/dist/assets/classDiagram-v2-EOCWNBFH-BCHaYHxj-BVAZDjb0.js +0 -1
  99. package/dist/assets/cynefin-VYW2F7L2-CGYr2F6h-CFSruy5n.js +0 -1
  100. package/dist/assets/eventmodeling-45OFAUF4-CWmqhm3a-B09FMUUq.js +0 -1
  101. package/dist/assets/flowDiagram-23GEKE2U-DKSB5AiY-BswknzuF.js +0 -1
  102. package/dist/assets/gitGraph-TEB2WS4Q-BR9qPwdN-Bg_TZB3f.js +0 -1
  103. package/dist/assets/index-DWePsXYt.css +0 -1
  104. package/dist/assets/info-DKCQHKI2-Dplx5kMp-BAI4sdgi.js +0 -1
  105. package/dist/assets/packet-7NZHBO7P-CQI3flND-Bqib4t7m.js +0 -1
  106. package/dist/assets/pie-RZYD4A2V-CnmKE2Wa-C7f4Ij-s.js +0 -1
  107. package/dist/assets/radar-I7S5WNFK-us6x-Z9R-C0_E6OiV.js +0 -1
  108. package/dist/assets/railroad-3IZDKUUU-BKdz6pdN-C2_X_Txm.js +0 -1
  109. package/dist/assets/railroad-abnf-AHOZXSZD-m_CSSIfd-wMka3Tbm.js +0 -1
  110. package/dist/assets/railroad-ebnf-EBAXGLYW-Nu3nAG7C-B7uu2NPX.js +0 -1
  111. package/dist/assets/railroad-peg-LSFZ7HO6--Y3mD4R_-D4Sj2mdo.js +0 -1
  112. package/dist/assets/stateDiagram-v2-6OUMAXLB-CuzgCL2v-PiMBzK3e.js +0 -1
  113. package/dist/assets/swimlanesDiagram-G3AALYLV-3bmSBtKs-ByceWEns.js +0 -8
  114. package/dist/assets/treeView-QDETBFTQ-D27HWG6Q-Bf6uz3xV.js +0 -1
  115. package/dist/assets/treemap-6X3UGDF4-CmGIU3H5-CLzvNsC2.js +0 -1
  116. package/dist/assets/wardley-OPB4EBWU-CbHWsb2k-OXtZqto9.js +0 -1
package/README.md CHANGED
@@ -93,6 +93,13 @@ than as bytes (files within the session's working directory only):
93
93
  | source, config, logs, and `.txt` — 46 extensions | the app's own **Files** view (`/files?path=`), where CodeMirror highlights it, the tree is right there, and it can be edited |
94
94
  | everything else — images, PDF, SVG, HTML, video | raw bytes in a new tab, which the browser renders better than an editor would |
95
95
 
96
+ **While a grid cell is enlarged, the [Files pane](#files-view-browse--edit) takes the click first** — every
97
+ row above except the last one, since the pane is the same editor plus a Markdown preview. The
98
+ file opens *beside* the terminal that printed it, and the pane opens itself if it was closed.
99
+ It declines, leaving the routing above untouched, when nothing is enlarged, when the path is
100
+ not under that cell's own directory (the pane cannot walk above its root), or for the raw-bytes
101
+ row, where it would only show an empty editor.
102
+
96
103
  Highlighting in the Files view covers the JS/TS family, JSON and Markdown (the modes
97
104
  `cmEditor.ts` bundles); other languages open as plain text.
98
105
 
@@ -620,6 +627,12 @@ as the reload signal, so colors, palette, font size and grid order update withou
620
627
  anything. There is no filesystem watcher, so an edit made **outside** a session (your own
621
628
  editor) is picked up when the terminal is next opened.
622
629
 
630
+ **Checking what took effect.** Settings → **Directory settings** lists your recent directories
631
+ and expands each one to the values in force, with a swatch per color and the path of the file
632
+ they came from. It also names the keys it **dropped** (a color that isn't `#rrggbb`, a size out
633
+ of range) and the keys it doesn't read at all (`badgeColour`, a global-only setting) — which is
634
+ what tells "I never set that" apart from "I set it and it didn't take".
635
+
623
636
  ---
624
637
 
625
638
  ## Running
@@ -771,6 +784,10 @@ shrinks the pane rather than reflowing xterm into garbage. It works in both zoom
771
784
  (cockpit roster and thumbnail filmstrip), the pane re-roots as you walk the zoom between
772
785
  terminals, and whether it's open plus how wide it is are remembered per browser.
773
786
 
787
+ The toggle is not the only way in: while a cell is enlarged, **clicking a file path the agent
788
+ printed** opens it here too, rather than in a new tab or full-screen — see
789
+ [Clicking a file path](#clicking-a-file-path).
790
+
774
791
  All reads and writes go through `GET/PUT /api/files/browse/*?cwd=&path=`, and every
775
792
  `path` is **contained within the project root** (server-side) — `..`/absolute escapes
776
793
  are rejected for reads and writes alike, so editing can't reach outside the directory
@@ -1137,6 +1154,7 @@ same-origin-guarded.
1137
1154
  | -------- | ------- |
1138
1155
  | `GET\|POST /api/config` | User UI config (`cwdPresets`, `soundFile`, `soundKinds`, `sounds`, `prRepos`, `launchers`, `quickCommands`, `userMcpServers`, `providers`). |
1139
1156
  | `GET /api/sound?kind=` · `/api/dir-sound?cwd=&kind=` · `/api/sound-preset/:id` · `/api/dir-config?cwd=` | Custom / per-directory / preset attention sound + per-dir config. `kind` selects a config entry, never a path. |
1157
+ | `GET /api/dir-config-detail?cwd=` | The same per-dir config, **plus** the settings a running terminal doesn't need (`provider`, `model`, `skills`, `addDirs`, header button/chip **labels**), **plus** which keys the file set and how each fared (applied / dropped in validation / not a setting at all). Read-only; backs the Settings modal's **Directory settings** preview. Unlike the other `?cwd=` routes this one does **not** fall back to the default workspace — it reports on the directory it was asked about, so a path that no longer exists comes back as `exists:false`. Sound paths and button commands stay server-side. |
1140
1158
  | `GET /api/launch-options` | The Anthropic-compatible backends this server can reach, each with its models and — when it can't — the reason. Reports the **name** of the env var a key is read from, never the key. |
1141
1159
  | `GET /api/notifications`(`/history`) · `POST /api/notifications/:id/clear` | Notification feed. |
1142
1160
  | `POST /api/transcribe`(`/model`…) | Voice-input transcription (Whisper, macOS). |
package/bin/cli-args.d.ts CHANGED
@@ -10,3 +10,4 @@ export declare const SECOND_INSTANCE_NOTE: string;
10
10
  export declare const MIN_NODE_LABEL: string;
11
11
  export declare function nodeMeetsMinimum(version: string): boolean;
12
12
  export declare function serverNodeArgs(serverEntry: string, launchDir: string): string[];
13
+ export declare function serverSpawnEnv(env: Record<string, string | undefined>, port: number, cwd: string): Record<string, string | undefined>;
package/bin/cli-args.js CHANGED
@@ -150,3 +150,19 @@ export function nodeMeetsMinimum(version) {
150
150
  export function serverNodeArgs(serverEntry, launchDir) {
151
151
  return ["--import", "tsx", `--env-file-if-exists=${join(launchDir, ".env")}`, serverEntry];
152
152
  }
153
+
154
+ /**
155
+ * The environment the launcher spawns the server with.
156
+ *
157
+ * Only the two values the server cannot work out for itself. In particular NOT `NODE_ENV`:
158
+ * the server hands its own environment to every PTY it spawns, so a `NODE_ENV=production`
159
+ * set here reaches every terminal in every cell — where yarn v1 reads it and installs
160
+ * WITHOUT devDependencies while still reporting success (#955). Express is pinned to
161
+ * production separately, in the server, so nothing here needs to say it.
162
+ *
163
+ * A `NODE_ENV` the user exported themselves passes through untouched: this decides what the
164
+ * launcher adds, not what it removes.
165
+ */
166
+ export function serverSpawnEnv(env, port, cwd) {
167
+ return { ...env, PORT: String(port), CLAUDE_CWD: cwd };
168
+ }
@@ -26,6 +26,7 @@ import {
26
26
  nodeMeetsMinimum,
27
27
  MIN_NODE_LABEL,
28
28
  serverNodeArgs,
29
+ serverSpawnEnv,
29
30
  } from "./cli-args.js";
30
31
 
31
32
  const __dirname = dirname(fileURLToPath(import.meta.url));
@@ -272,7 +273,7 @@ function runServer(port, noOpen, cwd, onChild) {
272
273
  // package directory, so serverNodeArgs makes that path absolute (#795).
273
274
  const server = spawn(process.execPath, serverNodeArgs(SERVER_ENTRY, process.cwd()), {
274
275
  cwd: PKG_DIR,
275
- env: { ...process.env, NODE_ENV: "production", PORT: String(port), CLAUDE_CWD: cwd },
276
+ env: serverSpawnEnv(process.env, port, cwd),
276
277
  stdio: ["inherit", "inherit", "pipe"],
277
278
  });
278
279
  let stderrTail = "";
@@ -0,0 +1,101 @@
1
+ // Where a directory's settings actually came from, for the settings modal's preview.
2
+ //
3
+ // The rest of the app only ever needs the RESOLVED config — the values a terminal adopts.
4
+ // That is exactly what makes a mistyped or rejected setting invisible: the file is on disk,
5
+ // the app reads it, and the value silently isn't there. This says which keys the file set,
6
+ // which of them survived validation, and which the app doesn't read at all.
7
+
8
+ // Every key `loadDirConfig` reads. A key outside this list is a typo as far as the app is
9
+ // concerned, however sensible it looks — `server/config/dir-config.spec.ts` pins the two
10
+ // together so a field added to the loader can't quietly go missing here.
11
+ export const DIR_CONFIG_KEYS = [
12
+ "name",
13
+ "badgeColor",
14
+ "headerColor",
15
+ "headerTextColor",
16
+ "cellColor",
17
+ "cellBorderColor",
18
+ "dotColor",
19
+ "buttonColor",
20
+ "fontSize",
21
+ "fontFamily",
22
+ "orderPriority",
23
+ "theme",
24
+ "colors",
25
+ "sound",
26
+ "sounds",
27
+ "buttons",
28
+ "chips",
29
+ "skills",
30
+ "provider",
31
+ "model",
32
+ "addDirs",
33
+ ] as const;
34
+
35
+ export interface DirConfigSource {
36
+ // Keys the file set that the app is using.
37
+ applied: string[];
38
+ // Keys the file set that the app read and then dropped — an out-of-range number, a colour
39
+ // that isn't #rrggbb, a sound outside the directory. The value is gone; the key is the clue.
40
+ ignored: string[];
41
+ // Keys the app doesn't read at all: a misspelling (`badgeColour`), or a setting that belongs
42
+ // in the global config instead of a directory's file.
43
+ unknown: string[];
44
+ }
45
+
46
+ export const EMPTY_DIR_CONFIG_SOURCE: DirConfigSource = { applied: [], ignored: [], unknown: [] };
47
+
48
+ // The settings a directory can make that the per-cell config deliberately doesn't carry — the
49
+ // browser has no use for them while running a terminal, but the preview has to SHOW them or a
50
+ // key like `provider` can only be named as applied, never read back.
51
+ //
52
+ // Labels only for buttons and chips: their `cmd` / `text` is what the button would type into
53
+ // the session, which is not what "did my config take effect" needs, and is the sort of thing a
54
+ // screenshot of the settings screen shouldn't carry. Sound paths stay out for the same reason
55
+ // they stay out of the per-cell config — that path is the confinement boundary.
56
+ export interface DirConfigExtras {
57
+ provider: string | null;
58
+ model: string | null;
59
+ skills: string[] | null;
60
+ addDirs: string[] | null;
61
+ buttonLabels: string[];
62
+ chipLabels: string[];
63
+ }
64
+
65
+ export const EMPTY_DIR_CONFIG_EXTRAS: DirConfigExtras = {
66
+ provider: null,
67
+ model: null,
68
+ skills: null,
69
+ addDirs: null,
70
+ buttonLabels: [],
71
+ chipLabels: [],
72
+ };
73
+
74
+ // "The loader kept nothing for this key" — null/undefined, but also the empty collections the
75
+ // loader returns when every entry in a map or list was rejected.
76
+ function isEmptyValue(value: unknown): boolean {
77
+ if (value === null || value === undefined) return true;
78
+ if (Array.isArray(value)) return value.length === 0;
79
+ if (typeof value === "object") return Object.keys(value as Record<string, unknown>).length === 0;
80
+ return false;
81
+ }
82
+
83
+ // The keys a resolved config actually holds a value for. Taken as a SET by describeDirConfig
84
+ // rather than the config object itself: the loader's type is an interface with named fields,
85
+ // which no amount of `Record<string, unknown>` will accept without a cast.
86
+ export function keysWithValue(resolved: object): Set<string> {
87
+ const entries: [string, unknown][] = Object.entries(resolved);
88
+ return new Set(entries.filter(([, value]) => !isEmptyValue(value)).map(([key]) => key));
89
+ }
90
+
91
+ // `raw` is the file as parsed; `kept` is which keys survived the loader (see keysWithValue).
92
+ export function describeDirConfig(raw: Record<string, unknown>, kept: ReadonlySet<string>): DirConfigSource {
93
+ const known = new Set<string>(DIR_CONFIG_KEYS);
94
+ const source: DirConfigSource = { applied: [], ignored: [], unknown: [] };
95
+ for (const key of Object.keys(raw)) {
96
+ if (!known.has(key)) source.unknown.push(key);
97
+ else if (kept.has(key)) source.applied.push(key);
98
+ else source.ignored.push(key);
99
+ }
100
+ return source;
101
+ }
@@ -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-BILJo_fK.js";import"./chunk-WYO6CB5R-BlzOfotS-04XZ1Jfd.js";import"./chunk-VAUOI2AC-CS9QJ4yz-Dd_VpRsu.js";import{n as i,r as a,t as o}from"./chunk-MOJQB5TN-CMZRaeqt-Dy6S_sS_.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-BzSqp82H.js";import"./chunk-WYO6CB5R-BlzOfotS-CTDodsnw.js";import"./chunk-VAUOI2AC-CS9QJ4yz-DFjCsQc6.js";import{n as i,r as a,t as o}from"./chunk-MOJQB5TN-CMZRaeqt-CYfpp2V7.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-BzSqp82H.js";export{e as createArchitectureServices};