mulmoterminal 4.16.1 → 4.18.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 +7 -5
- package/common/dirPathKey.ts +19 -0
- package/common/shortcuts.ts +13 -0
- package/common/toolGroups.ts +39 -3
- package/common/toolbarPins.ts +107 -0
- package/dist/assets/{AllPackages-CF2GkZ5g-BR1ZQVLR.js → AllPackages-CF2GkZ5g-BpghxJjj.js} +1 -1
- package/dist/assets/{BaseConfiguration-CAwZOAki-CH1MWBaY.js → BaseConfiguration-CAwZOAki-WQURtaX1.js} +1 -1
- package/dist/assets/{Element-BMI7Nj5w-U7cD6zKw.js → Element-BMI7Nj5w-CcDgA_9-.js} +1 -1
- package/dist/assets/{Entities-C8p_0jCn-dSDbGTsJ.js → Entities-C8p_0jCn-DRWzxsVw.js} +1 -1
- package/dist/assets/{FunctionList-YWk54TCy-BkQb8DA8.js → FunctionList-YWk54TCy-BrIR4n-1.js} +1 -1
- package/dist/assets/{InputJax-C5dj-ntL-BN7NCKKO.js → InputJax-C5dj-ntL-AT5iU-td.js} +1 -1
- package/dist/assets/{MathItem-YJvQL-XJ-DIEn5osO.js → MathItem-YJvQL-XJ-CxJPVleP.js} +1 -1
- package/dist/assets/{MmlFactory-DuXYIgx8-jZrlbK77.js → MmlFactory-DuXYIgx8-Ey_YXuDi.js} +1 -1
- package/dist/assets/{Options-oARQeeuT-BFoe45j7.js → Options-oARQeeuT-BZ9bNEz5.js} +1 -1
- package/dist/assets/{OutputJax-DfUZNNCu-D647fvVr.js → OutputJax-DfUZNNCu-De4Z1Pn_.js} +1 -1
- package/dist/assets/{PrioritizedList-6lun1Xqd-BDXLCciz.js → PrioritizedList-6lun1Xqd-BTQmhniB.js} +1 -1
- package/dist/assets/{Styles-BPXeoqzi-CWQXCVHq.js → Styles-BPXeoqzi-Cwo9PIXj.js} +1 -1
- package/dist/assets/{TeXAtom-LHT3KFWH-BXe_YUAD.js → TeXAtom-LHT3KFWH-B58fWn67.js} +1 -1
- package/dist/assets/{abnfDiagram-VCTEODGH-DoYNjdBB-BKBHvI11.js → abnfDiagram-VCTEODGH-DoYNjdBB-N4p9eIcR.js} +1 -1
- package/dist/assets/{arc-CqByPKkF-B2A6kmY0.js → arc-CqByPKkF-o90vYcFT.js} +1 -1
- package/dist/assets/architecture-7GRP2DOG-CRS69XmP-Dofb7uTe.js +1 -0
- package/dist/assets/{architectureDiagram-5GKGNRK7-BhcT6qS5-CZaKxcvH.js → architectureDiagram-5GKGNRK7-BhcT6qS5-y2WkXlFP.js} +1 -1
- package/dist/assets/{assistive-mml-DXJXho4o-Duj6EibK.js → assistive-mml-DXJXho4o-DHpoehaR.js} +1 -1
- package/dist/assets/{blockDiagram-I7D4REHJ-1Cng0Q4K-BXZWrL4K.js → blockDiagram-I7D4REHJ-1Cng0Q4K-BDINYHUk.js} +1 -1
- package/dist/assets/{c4Diagram-7LVT6UL2-CAb9c8nc-XML4meyN.js → c4Diagram-7LVT6UL2-CAb9c8nc-BtCR-OQb.js} +1 -1
- package/dist/assets/channel-BjESmYnj-D2HJk1kK.js +1 -0
- package/dist/assets/{chunk-4HAMMTFA-ChwfIAnF-D3Xu-FmM.js → chunk-4HAMMTFA-ChwfIAnF-BoqlitDw.js} +1 -1
- package/dist/assets/{chunk-75Z2AOVW-CbPA0Zt_-B3EpUXO6.js → chunk-75Z2AOVW-CbPA0Zt_-sG1wziTf.js} +1 -1
- package/dist/assets/{chunk-DU6HZSFF-DuOx2LFX-BNLbgzMj.js → chunk-DU6HZSFF-DuOx2LFX-jw2gw2WG.js} +1 -1
- package/dist/assets/{chunk-F27PBJKO-CgBMw9yN-DGYrs_Xh.js → chunk-F27PBJKO-CgBMw9yN-BIA-Rq1B.js} +1 -1
- package/dist/assets/{chunk-GMAD6QVW-JTqlimUX-D6fbHeH8.js → chunk-GMAD6QVW-JTqlimUX-B7VyMMki.js} +1 -1
- package/dist/assets/{chunk-GVQU2GXP-D6ccB4DO-CLm1L51U.js → chunk-GVQU2GXP-D6ccB4DO-CxN0Y161.js} +1 -1
- package/dist/assets/{chunk-IMKFNOWR-ij1DSzMb-3ueGg7Ju.js → chunk-IMKFNOWR-ij1DSzMb-Cp5MHDVx.js} +1 -1
- package/dist/assets/{chunk-L3NEJ4N5-CUoAQVG2-C2QaFk-g.js → chunk-L3NEJ4N5-CUoAQVG2-Cq8hXkjf.js} +1 -1
- package/dist/assets/{chunk-OSK3NFVY-_VNbXQSu-C7F93M3m.js → chunk-OSK3NFVY-_VNbXQSu-CLHHccp_.js} +1 -1
- package/dist/assets/{chunk-P2QGCYS3-B8lzOn3Y-DqTS4kWt.js → chunk-P2QGCYS3-B8lzOn3Y-9R8JiTOW.js} +1 -1
- package/dist/assets/{chunk-POPQ4Y6H-Bh_uOTmp-yzH0pUKG.js → chunk-POPQ4Y6H-Bh_uOTmp-IIk4SISs.js} +1 -1
- package/dist/assets/{chunk-PWAF6VOD-DzjnfYSw-Bkp-2n9L.js → chunk-PWAF6VOD-DzjnfYSw-FXKbHA4z.js} +1 -1
- package/dist/assets/{chunk-SHT3W25Y-B8dQLS5w-B6vDUDbt.js → chunk-SHT3W25Y-B8dQLS5w-asO6U825.js} +1 -1
- package/dist/assets/{chunk-SVP7TREG-B6A-T8Th-C36d9rwX.js → chunk-SVP7TREG-B6A-T8Th-C_31sNJS.js} +1 -1
- package/dist/assets/{chunk-TICWLB2K-C1kwD29l-8bma78sl.js → chunk-TICWLB2K-C1kwD29l-De2Aa9yO.js} +1 -1
- package/dist/assets/{chunk-XXDRQBXY-vg0kfC73-DikArmyN.js → chunk-XXDRQBXY-vg0kfC73-MJrwfLy3.js} +1 -1
- package/dist/assets/classDiagram-ZZMXUADV-C7gP-pUH-CTNdATck.js +1 -0
- package/dist/assets/classDiagram-v2-VYDZK3BY-CUwV5k1F-CTNdATck.js +1 -0
- package/dist/assets/{cose-bilkent-JH36ORCC-NuHCtE3P-B1XlcY4_.js → cose-bilkent-JH36ORCC-NuHCtE3P-CBmhuRye.js} +1 -1
- package/dist/assets/cynefin-OW5HDTMX-C4iKivN4-BAbBOyvn.js +1 -0
- package/dist/assets/{cynefinDiagram-5FMLGOSQ-CA7sro34-Dpxb2Y3q.js → cynefinDiagram-5FMLGOSQ-CA7sro34-C7EC0rCC.js} +1 -1
- package/dist/assets/{dagre-GXQ25YYZ-DOztjUR_-tCVxsIWx.js → dagre-GXQ25YYZ-DOztjUR_-DUYDKcad.js} +1 -1
- package/dist/assets/{diagram-S7CK7UJ4-D_nsLfRT-C0oowP2x.js → diagram-S7CK7UJ4-D_nsLfRT-6xfPNGOi.js} +1 -1
- package/dist/assets/{diagram-UQ7AKVKN-DNN-YX9a-BhdVmuTQ.js → diagram-UQ7AKVKN-DNN-YX9a-DabWPcsA.js} +1 -1
- package/dist/assets/{diagram-VSXAHHWV--HENzo-l-CpmR-QX7.js → diagram-VSXAHHWV--HENzo-l-0O7qAbYx.js} +1 -1
- package/dist/assets/{diagram-VX7I27RA-m2m9dROj-BHMZbFwF.js → diagram-VX7I27RA-m2m9dROj-DSsL6lbk.js} +1 -1
- package/dist/assets/{diagram-Z3DM3KII-D4qpv4-B-BdXuf-Jf.js → diagram-Z3DM3KII-D4qpv4-B-x5aaLfdn.js} +1 -1
- package/dist/assets/{dist-G6SuIVg-.js → dist-BHbP5Rdy.js} +1 -1
- package/dist/assets/{dist-C9lyUYv8.js → dist-CI0Sz8VW.js} +1 -1
- package/dist/assets/{dist-DeiyD9Ki-DblgSNGE.js → dist-DeiyD9Ki-CyYR8D-0.js} +1 -1
- package/dist/assets/{dist-DKTnxp-h.js → dist-KHYYM69R.js} +1 -1
- package/dist/assets/{dist-DAo7Oyn5.js → dist-to-wCxn9.js} +1 -1
- package/dist/assets/{ebnfDiagram-PWID7BFC-CAbv3sZg-C48moA_l.js → ebnfDiagram-PWID7BFC-CAbv3sZg-CzJDavSQ.js} +1 -1
- package/dist/assets/{erDiagram-RLTQ6QDP-a10Ryh0--Mt1Za2Av.js → erDiagram-RLTQ6QDP-a10Ryh0--BfSvF1ta.js} +1 -1
- package/dist/assets/eventmodeling-NTZA5JFV-DhqtNS8R-DZDQj3ay.js +1 -0
- package/dist/assets/flowDiagram-HODETNUW-CAW_8mwY-B4f7Dpo6.js +1 -0
- package/dist/assets/{ganttDiagram-EL5Y4UJY-Cb7leeOC-7Ln8AUnj.js → ganttDiagram-EL5Y4UJY-Cb7leeOC-DKtB1NzV.js} +1 -1
- package/dist/assets/gitGraph-4MIJSDKK-S_Zs6Nxs-CZ6NmIpk.js +1 -0
- package/dist/assets/{gitGraphDiagram-WWUBYQGX-BCSbUpXG-CqXWIVg7.js → gitGraphDiagram-WWUBYQGX-BCSbUpXG-DKX1wbps.js} +1 -1
- package/dist/assets/{html-DdQiKDqM-BHusMiN-.js → html-DdQiKDqM-O0S29Dwr.js} +1 -1
- package/dist/assets/index-yoMfkmrj.js +5418 -0
- package/dist/assets/info-A6RAGUB7-4sGJh1is-DIi5EBfj.js +1 -0
- package/dist/assets/{infoDiagram-27XIBGKW-BJRaZItN-CpPDV4hG.js → infoDiagram-27XIBGKW-BJRaZItN-q1dLBYbI.js} +1 -1
- package/dist/assets/{ishikawaDiagram-5VMMS53U-DHlFe__S-Dq_O4Zho.js → ishikawaDiagram-5VMMS53U-DHlFe__S-DEAMGQtX.js} +1 -1
- package/dist/assets/{journeyDiagram-3NMN7TZE-BOIo3JM--wMJ4fbc1.js → journeyDiagram-3NMN7TZE-BOIo3JM--BO_7tBTd.js} +1 -1
- package/dist/assets/{kanban-definition-UXKFOSKX-yrQdG683-_5KdtmkY.js → kanban-definition-UXKFOSKX-yrQdG683-Bx4it8pk.js} +1 -1
- package/dist/assets/{lengths-CvmPfBid-CVCP8twv.js → lengths-CvmPfBid-BJ7XjTXv.js} +1 -1
- package/dist/assets/{line-DUb1TjJS-CF13RdVB.js → line-DUb1TjJS-DTQdqqNY.js} +1 -1
- package/dist/assets/{linear-xsuGWu5q-CL39nfZD.js → linear-xsuGWu5q-DAJY5eE1.js} +1 -1
- package/dist/assets/{liteAdaptor-C0FQiOEx-B7vjjyDw.js → liteAdaptor-C0FQiOEx-CpU2hMwG.js} +1 -1
- package/dist/assets/{mathjax-DVEo4UYj-DVc67oTA.js → mathjax-DVEo4UYj-DblCQqUM.js} +1 -1
- package/dist/assets/{mermaid-parser.core-fkjeiYO7-ZhNOsYx6.js → mermaid-parser.core-fkjeiYO7-xLW4or9n.js} +2 -2
- package/dist/assets/{mermaid.core-CZDx2Cyn-D0adQ2ma.js → mermaid.core-CZDx2Cyn-BWO3dTDf.js} +4 -4
- package/dist/assets/{mindmap-definition-YA3MSWOX-BpKAVyqs-DiEKJqkL.js → mindmap-definition-YA3MSWOX-BpKAVyqs-BrJpjBKU.js} +1 -1
- package/dist/assets/{mo-CrlNnGXX-CiO8TUoC.js → mo-CrlNnGXX-Eo4cINyg.js} +1 -1
- package/dist/assets/packet-AYTQ26CC-MQQyx8fI-yAikr6bQ.js +1 -0
- package/dist/assets/{pegDiagram-XKGWAZYB-C7eG_Ohl-C4Enb8H1.js → pegDiagram-XKGWAZYB-C7eG_Ohl-CaXo4ojb.js} +1 -1
- package/dist/assets/pie-WAS4IAKB-CyaL5i_d-BIdTCQS6.js +1 -0
- package/dist/assets/{pieDiagram-E7YTZNPT-5f9wSfDR-AKlM6fne.js → pieDiagram-E7YTZNPT-5f9wSfDR-BwnQjyyW.js} +1 -1
- package/dist/assets/{quadrantDiagram-AXDQQJYC-mWeaOj5S-CAwPohmQ.js → quadrantDiagram-AXDQQJYC-mWeaOj5S-DvsMFOc9.js} +1 -1
- package/dist/assets/radar-RG4KPBEZ-DR-SsHDJ-0JhYwnII.js +1 -0
- package/dist/assets/railroad-74A4TZTK-C2gqsOGX-C1zJ70Ow.js +1 -0
- package/dist/assets/railroad-abnf-HS5TGJTU-Dpzw6P5u-BMKd1LY4.js +1 -0
- package/dist/assets/railroad-ebnf-LZEXJU2U-J_BuIrPo-D5xXkaGL.js +1 -0
- package/dist/assets/railroad-peg-WCYAUIDC-DzMStOR--CTf0TsuO.js +1 -0
- package/dist/assets/{railroadDiagram-O6MQD6OU-DJskhE7u-D88MJVV3.js → railroadDiagram-O6MQD6OU-DJskhE7u-C_0njIFi.js} +1 -1
- package/dist/assets/{requirementDiagram-BXWQKSXE-B5E0bU4h-DhMi5aIj.js → requirementDiagram-BXWQKSXE-B5E0bU4h-C5hnNnLY.js} +1 -1
- package/dist/assets/{sankeyDiagram-P5KCCOFB-DS3AOTai-BqvLM5FN.js → sankeyDiagram-P5KCCOFB-DS3AOTai-pWFxLlmZ.js} +1 -1
- package/dist/assets/{sequenceDiagram-WJ2MYXX4-Bn2L9osv-DD00vS5e.js → sequenceDiagram-WJ2MYXX4-Bn2L9osv-Qj-GBMA5.js} +1 -1
- package/dist/assets/{src-CLC0ddSd-BiOrUmL4.js → src-CLC0ddSd-C6SesIb_.js} +1 -1
- package/dist/assets/{stateDiagram-D77RDMKH-DbtoW7HL-CIt9LbTt.js → stateDiagram-D77RDMKH-DbtoW7HL-0tDoAFAn.js} +1 -1
- package/dist/assets/stateDiagram-v2-MP3YSRHH-CX6RalKY-CyxuK80e.js +1 -0
- package/dist/assets/{svg-CaHU_sxA-CHHrP--F.js → svg-CaHU_sxA-hUQUwI5e.js} +1 -1
- package/dist/assets/{swimlanes-42K2YHIH-90h8w_3B-DNrY0PsR.js → swimlanes-42K2YHIH-90h8w_3B-BIANSbUk.js} +1 -1
- package/dist/assets/swimlanesDiagram-VR7AAH4N-49XH_TMr-nlZP2lTW.js +8 -0
- package/dist/assets/{tex-B1cONMvb-BZ8-XPoY.js → tex-B1cONMvb-BuuMe8DF.js} +1 -1
- package/dist/assets/{timeline-definition-24CTP7MA-Bx3KcICO-C2qvbp_0.js → timeline-definition-24CTP7MA-Bx3KcICO-uJwxqLxL.js} +1 -1
- package/dist/assets/treeView-Q6P3EWNA-gFs854Sn-BDnUAqSd.js +1 -0
- package/dist/assets/treemap-WGGIJYW6-DGh_szUA-BcyHS0XZ.js +1 -0
- package/dist/assets/{vennDiagram-4TSXK5OY-DSexNe_H-BYZN3BN_.js → vennDiagram-4TSXK5OY-DSexNe_H-CEWVEpb7.js} +1 -1
- package/dist/assets/wardley-WFR3VGLG-BVLYL3gB-CTDrPm0c.js +1 -0
- package/dist/assets/{wardleyDiagram-VM6X3IG4-DesYYyZW-Du8b9LqG.js → wardleyDiagram-VM6X3IG4-DesYYyZW-7T-CCnI4.js} +1 -1
- package/dist/assets/{xychartDiagram-S5SC5T6Z-dIt6pHWV-R_c8Thbw.js → xychartDiagram-S5SC5T6Z-dIt6pHWV-BWhy5WEU.js} +1 -1
- package/dist/index.html +1 -1
- package/package.json +3 -2
- package/plugins/plugins.json +2 -1
- package/server/agents/codex-sessions.ts +7 -7
- package/server/agents/codex-thread-names.ts +87 -0
- package/server/agents/codex-user-turn.ts +86 -0
- package/server/backends/fileChange.ts +9 -0
- package/server/backends/mulmoscript.ts +14 -8
- package/server/backends/openPath.ts +6 -0
- package/server/backends/presentPathRoot.ts +7 -1
- package/server/backends/shapescript.ts +51 -0
- package/server/backends/shortcuts.ts +47 -16
- package/server/config/app-config.ts +10 -0
- package/server/config/config-body.ts +1 -0
- package/server/infra/host-tools.ts +13 -1
- package/server/infra/plugins-registry.ts +27 -10
- package/server/infra/shapescript-render-tool.ts +79 -0
- package/server/routes/app-routes.ts +7 -0
- package/server/routes/plugin-routes.ts +21 -0
- package/server/routes/ws-routes.ts +5 -1
- package/server/session/last-turn.ts +9 -9
- package/server/session/prompt-history.ts +25 -8
- package/server/session/session-resolve.ts +15 -1
- package/server/skills/mulmoterminal-config/SKILL.md +29 -1
- package/dist/assets/architecture-7GRP2DOG-CRS69XmP-DmZ-HChU.js +0 -1
- package/dist/assets/channel-BjESmYnj-BFW3bEkR.js +0 -1
- package/dist/assets/classDiagram-ZZMXUADV-C7gP-pUH-BUgE4GLJ.js +0 -1
- package/dist/assets/classDiagram-v2-VYDZK3BY-CUwV5k1F-BUgE4GLJ.js +0 -1
- package/dist/assets/cynefin-OW5HDTMX-C4iKivN4-bsjBxOW3.js +0 -1
- package/dist/assets/eventmodeling-NTZA5JFV-DhqtNS8R-DEQxhhe6.js +0 -1
- package/dist/assets/flowDiagram-HODETNUW-CAW_8mwY-CeF3rDxG.js +0 -1
- package/dist/assets/gitGraph-4MIJSDKK-S_Zs6Nxs-CB3gPIVK.js +0 -1
- package/dist/assets/index-CdmvtxCQ.js +0 -1031
- package/dist/assets/info-A6RAGUB7-4sGJh1is-4bRJ5Ro5.js +0 -1
- package/dist/assets/packet-AYTQ26CC-MQQyx8fI-MEZA5KJl.js +0 -1
- package/dist/assets/pie-WAS4IAKB-CyaL5i_d-BIbJfKkP.js +0 -1
- package/dist/assets/radar-RG4KPBEZ-DR-SsHDJ-HLbo3sJW.js +0 -1
- package/dist/assets/railroad-74A4TZTK-C2gqsOGX-DuC_Eg0T.js +0 -1
- package/dist/assets/railroad-abnf-HS5TGJTU-Dpzw6P5u-CCehLGc2.js +0 -1
- package/dist/assets/railroad-ebnf-LZEXJU2U-J_BuIrPo-CpfaYN3N.js +0 -1
- package/dist/assets/railroad-peg-WCYAUIDC-DzMStOR--CAPvB8Q4.js +0 -1
- package/dist/assets/stateDiagram-v2-MP3YSRHH-CX6RalKY-B9ca3LBb.js +0 -1
- package/dist/assets/swimlanesDiagram-VR7AAH4N-49XH_TMr-CwENk-w_.js +0 -8
- package/dist/assets/treeView-Q6P3EWNA-gFs854Sn-Bq0gV9iU.js +0 -1
- package/dist/assets/treemap-WGGIJYW6-DGh_szUA-D1QZ74up.js +0 -1
- package/dist/assets/wardley-WFR3VGLG-BVLYL3gB-D_rI3R6m.js +0 -1
package/README.md
CHANGED
|
@@ -1083,11 +1083,13 @@ everything else on disk that happens to parse as a deck. Measured on a real work
|
|
|
1083
1083
|
matched, 33 were the user's own decks and 217 were a checked-out repository's test fixtures and
|
|
1084
1084
|
samples.** A menu is a short list of things you chose.
|
|
1085
1085
|
|
|
1086
|
-
**Where the decks have to live:**
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
**Open in the Canvas** — with no configuration at all
|
|
1086
|
+
**Where the decks have to live:** anywhere. A deck under the workspace — or under a directory in
|
|
1087
|
+
your launcher's saved list (up to 64 in total, read once at startup) — is addressed by its place in
|
|
1088
|
+
that directory; one anywhere else is addressed by its own path. Either way it opens, and a deck you
|
|
1089
|
+
open both ways is **one card**, not two. A deck you can see in the file tree is reachable there too
|
|
1090
|
+
— right-click a row and choose **Open in the Canvas** — with no configuration at all, as long as the
|
|
1091
|
+
pane knows the directory its tree is rooted at. It shows that path in its header; a pane still
|
|
1092
|
+
reading `(default workspace)` has nothing to resolve a row against and offers no entry.
|
|
1091
1093
|
|
|
1092
1094
|
If a deck cannot be opened (it was deleted, or the workspace moved since startup), the cell says
|
|
1093
1095
|
why rather than doing nothing.
|
package/common/dirPathKey.ts
CHANGED
|
@@ -43,3 +43,22 @@ export function dirPathKey(path: string): string {
|
|
|
43
43
|
|
|
44
44
|
/** Whether two paths name the same directory, as far as spelling can tell. */
|
|
45
45
|
export const isSameDirPath = (a: string | null | undefined, b: string | null | undefined): boolean => !!a && !!b && dirPathKey(a) === dirPathKey(b);
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Whether `path` names one file on its own — POSIX `/…`, a Windows drive root, a UNC share.
|
|
49
|
+
*
|
|
50
|
+
* NARROWER than `rootOf` on purpose, in the one place the two could disagree: a SINGLE leading
|
|
51
|
+
* backslash. `rootOf` keys `\deck.json` as `/deck.json`, which is right for its job (comparing two
|
|
52
|
+
* spellings of a directory lexically) and wrong for this one — on Windows that path is rooted on
|
|
53
|
+
* the process's CURRENT DRIVE, which the spelling does not carry and a browser cannot infer. Keying
|
|
54
|
+
* it would give `\deck.json` and `C:\deck.json` two identities for one file while conflating the
|
|
55
|
+
* first with a POSIX `/deck.json` (Codex P2 on #1976). A caller that needs a decision here gets
|
|
56
|
+
* "no", and whatever it falls back to is at worst what it did before.
|
|
57
|
+
*
|
|
58
|
+
* The same distinction `rootOf` already draws for `C:foo` and `\server\share`: a spelling that
|
|
59
|
+
* means something else must not borrow a rooted one's key.
|
|
60
|
+
*/
|
|
61
|
+
export const isRootedPath = (path: string): boolean => {
|
|
62
|
+
const trimmed = path.trim();
|
|
63
|
+
return /^[a-zA-Z]:[/\\]/.test(trimmed) || /^[/\\]{2}/.test(trimmed) || trimmed.startsWith("/");
|
|
64
|
+
};
|
package/common/shortcuts.ts
CHANGED
|
@@ -6,6 +6,11 @@
|
|
|
6
6
|
// show up in the other — so the on-disk format is the contract: an OBJECT WRAPPER
|
|
7
7
|
// `{ shortcuts: Shortcut[] }`, not a bare array, matching mulmoclaude/src/types/shortcuts.ts.
|
|
8
8
|
// Keep this type in sync with MulmoClaude's.
|
|
9
|
+
//
|
|
10
|
+
// It is a SUBSET of what a record may hold, not the whole of it: the file is the union of every
|
|
11
|
+
// version of both apps that has written it, and a stored record can carry fields this build has
|
|
12
|
+
// never heard of. `toShortcut` (server/backends/shortcuts.ts) carries those through rather than
|
|
13
|
+
// dropping them (#1996) — so this type says what MulmoTerminal understands, not what exists.
|
|
9
14
|
|
|
10
15
|
export const SHORTCUT_KINDS = ["collection", "feed"] as const;
|
|
11
16
|
export type ShortcutKind = (typeof SHORTCUT_KINDS)[number];
|
|
@@ -19,6 +24,14 @@ export interface Shortcut {
|
|
|
19
24
|
title: string;
|
|
20
25
|
/** Cached material-symbols glyph — refreshed on reconcile. */
|
|
21
26
|
icon: string;
|
|
27
|
+
/** Cached accent colour name — MulmoClaude's, and only MulmoClaude draws it (#1993).
|
|
28
|
+
*
|
|
29
|
+
* Carried rather than understood: this app has no accent palette, so it keeps whatever string
|
|
30
|
+
* the file holds and hands it back unchanged. Deliberately NOT validated here — deciding which
|
|
31
|
+
* colours are legal is the job of the app that draws them, and a check that drifted from theirs
|
|
32
|
+
* would silently delete a colour they consider valid, which is the bug this field exists to fix.
|
|
33
|
+
* Absent when the collection names none. */
|
|
34
|
+
color?: string;
|
|
22
35
|
}
|
|
23
36
|
|
|
24
37
|
/** True when two shortcuts target the same thing (the dedupe key). */
|
package/common/toolGroups.ts
CHANGED
|
@@ -8,7 +8,12 @@
|
|
|
8
8
|
// config already written against it.
|
|
9
9
|
|
|
10
10
|
// The groups, ordered by how much damage a call can do.
|
|
11
|
-
// render — draws
|
|
11
|
+
// render — draws a model or document for the user, and writes nothing but the
|
|
12
|
+
// artifact it just drew. It USED to mean "no side effect outside the
|
|
13
|
+
// Canvas panel"; presentShapeScript saves its `.shape` and
|
|
14
|
+
// renderShapeScript saves a PNG, so the honest line is that a render
|
|
15
|
+
// tool touches only its own output, never the workspace's data or
|
|
16
|
+
// anything off this machine.
|
|
12
17
|
// data — reads/writes the workspace's structured data (collections, accounting).
|
|
13
18
|
// media — generation that is slow, costly, and lands files on disk.
|
|
14
19
|
// external — reaches a third-party account or API.
|
|
@@ -84,6 +89,11 @@ const GROUP_BY_TOOL = new Map<string, ToolGroup>([
|
|
|
84
89
|
["presentForm", "render"],
|
|
85
90
|
["presentChart", "render"],
|
|
86
91
|
["presentHtml", "render"],
|
|
92
|
+
["presentShapeScript", "render"],
|
|
93
|
+
// Beside presentShapeScript deliberately: a cell that can show a 3D model should be
|
|
94
|
+
// able to CHECK one, and splitting the pair would leave an agent able to present a
|
|
95
|
+
// model it cannot look at first.
|
|
96
|
+
["renderShapeScript", "render"],
|
|
87
97
|
|
|
88
98
|
// presentCollection RENDERS, but it renders collection data and only makes sense next to
|
|
89
99
|
// manageCollection — a cell offered the view without the store gets a tool it cannot fill.
|
|
@@ -175,8 +185,34 @@ export const LEGACY_GUI_SERVER_IDS: readonly string[] = ["mulmoterminal-gui"];
|
|
|
175
185
|
// model spend money silently under a switch the UI presents as "let the agent draw", so it
|
|
176
186
|
// keeps Claude Code's prompt (answer it once per project and the prompt stops).
|
|
177
187
|
//
|
|
178
|
-
// The
|
|
179
|
-
|
|
188
|
+
// The four below save an artifact and draw it, and call nothing external.
|
|
189
|
+
//
|
|
190
|
+
// `presentShapeScript` was listed here on the grounds that it saved nothing at all —
|
|
191
|
+
// the source travelled in the tool result and was rendered client-side. That stopped
|
|
192
|
+
// being true at shapescript-plugin 1.1.0, which writes the model to
|
|
193
|
+
// `artifacts/shapes/` and can open a `.shape` the caller names. It stays on the list,
|
|
194
|
+
// but now for the same reason as the other three rather than a stronger one that no
|
|
195
|
+
// longer holds.
|
|
196
|
+
//
|
|
197
|
+
// `renderShapeScript` is here too, and the reasoning is worth stating because the first
|
|
198
|
+
// attempt got it wrong. It starts a PROCESS — a headless browser — which reads like the
|
|
199
|
+
// far side of "calls nothing external", so it was left off. But this bar is about REACH
|
|
200
|
+
// and COST, not about process creation: the tools kept off it spend money
|
|
201
|
+
// (presentDocument's image fill), publish to the internet, or act in someone else's
|
|
202
|
+
// account. A local Chromium rendering a page WE construct, with every request it makes
|
|
203
|
+
// aborted unless it is one of our two assets, does none of that. It writes a PNG under
|
|
204
|
+
// the workspace artifacts and burns CPU for at most the render budget.
|
|
205
|
+
//
|
|
206
|
+
// Leaving it off was also not the half-measure it looked like. This list governs GRID
|
|
207
|
+
// cells; the workspace passes `allowedToolNames()`, which auto-approves everything not
|
|
208
|
+
// in NEVER_AUTO_APPROVED_TOOLS — so the tool prompted in a cell and ran unattended in
|
|
209
|
+
// the workspace, which is the worst of both and matches no stated policy (codex on
|
|
210
|
+
// #2010). The two paths now agree.
|
|
211
|
+
//
|
|
212
|
+
// And the friction had no payoff: the tool exists so an agent can CHECK a model before
|
|
213
|
+
// showing it, which is a render-look-fix loop. A prompt in the middle of that costs the
|
|
214
|
+
// user attention to approve the agent looking at its own work.
|
|
215
|
+
export const AUTO_ALLOWED_TOOLS: readonly string[] = ["presentForm", "presentChart", "presentHtml", "presentShapeScript", "renderShapeScript"];
|
|
180
216
|
|
|
181
217
|
/** Tools that must keep the agent's permission prompt on EVERY claude session, including the
|
|
182
218
|
* workspace.
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// Which of the pinned favourites the app toolbar shows without opening Collections (#1984).
|
|
2
|
+
//
|
|
3
|
+
// Shared because both sides decide from it: the server sanitizes what `~/.mulmoterminal/config.json`
|
|
4
|
+
// says, the browser renders the toolbar and writes the same field back.
|
|
5
|
+
//
|
|
6
|
+
// Deliberately NOT a field on `Shortcut`. The pins themselves live in `<workspace>/config/shortcuts.json`,
|
|
7
|
+
// which MulmoTerminal SHARES with MulmoClaude, and both apps rebuild every record when they write it
|
|
8
|
+
// (`normalizeShortcuts`) — so a field one app added is dropped the first time the other pins anything.
|
|
9
|
+
// This is a MulmoTerminal-side view preference over that shared list, and it holds only the identity
|
|
10
|
+
// of the pins it promotes: the label and the icon are read from the pin, so renaming a collection
|
|
11
|
+
// cannot leave the toolbar saying the old name.
|
|
12
|
+
import { SHORTCUT_KINDS, type Shortcut, type ShortcutKind } from "./shortcuts.js";
|
|
13
|
+
|
|
14
|
+
/** How many buttons the toolbar draws, and so how many pins can be promoted at once. The row
|
|
15
|
+
* already carries the view switch, the grid's own controls, the status tally and two gauges; past
|
|
16
|
+
* a handful the pins stop being "one press away" and start pushing that row into its horizontal
|
|
17
|
+
* scroll, which is the two-step this feature exists to remove. */
|
|
18
|
+
export const MAX_TOOLBAR_PINS = 5;
|
|
19
|
+
|
|
20
|
+
/** How many keys the FILE may hold, which is not the same number.
|
|
21
|
+
*
|
|
22
|
+
* Nothing here ever deletes a key it did not just demote: unpinning a promoted collection leaves
|
|
23
|
+
* its key in the config, where it draws nothing and takes no slot — and re-pinning brings the
|
|
24
|
+
* button straight back. That is a deliberate choice over tidying the file on save. Tidying means
|
|
25
|
+
* deciding "this pin is gone" from a list that might be stale, and one wrong answer deletes a
|
|
26
|
+
* promotion the user still wants, silently; four separate review findings on PR #1991 were all
|
|
27
|
+
* ways of getting that decision wrong. A key nobody can see costs a line of JSON.
|
|
28
|
+
*
|
|
29
|
+
* So the file's own limit is a sanity bound, not a budget — you would have to unpin forty-five
|
|
30
|
+
* promoted collections to meet it. */
|
|
31
|
+
export const MAX_STORED_TOOLBAR_PINS = 50;
|
|
32
|
+
|
|
33
|
+
/** How one promoted pin is named in the config: `"collection:works"`. */
|
|
34
|
+
export const toolbarPinKey = (pin: Pick<Shortcut, "kind" | "slug">): string => `${pin.kind}:${pin.slug}`;
|
|
35
|
+
|
|
36
|
+
// Split on the FIRST colon, not every colon: `kind` is one of two known words, so everything after
|
|
37
|
+
// it is the slug even if a slug ever contains one.
|
|
38
|
+
function parseToolbarPin(key: unknown): { kind: ShortcutKind; slug: string } | null {
|
|
39
|
+
if (typeof key !== "string") return null;
|
|
40
|
+
const separator = key.indexOf(":");
|
|
41
|
+
if (separator <= 0) return null;
|
|
42
|
+
const kind = SHORTCUT_KINDS.find((candidate) => candidate === key.slice(0, separator));
|
|
43
|
+
if (kind === undefined) return null;
|
|
44
|
+
const slug = key.slice(separator + 1);
|
|
45
|
+
return slug.length > 0 ? { kind, slug } : null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Keep the well-formed keys, in order, deduped, up to the FILE's bound. Anything else — a config
|
|
49
|
+
* written by hand, a key naming a kind this build does not have — is dropped rather than rendered
|
|
50
|
+
* as a blank button. The bound here is `MAX_STORED_TOOLBAR_PINS`, not the number of buttons:
|
|
51
|
+
* truncating to five would delete keys whose pins are merely unpinned right now. */
|
|
52
|
+
export function sanitizeToolbarPins(input: unknown): string[] {
|
|
53
|
+
if (!Array.isArray(input)) return [];
|
|
54
|
+
const out: string[] = [];
|
|
55
|
+
input.forEach((raw) => {
|
|
56
|
+
const pin = parseToolbarPin(raw);
|
|
57
|
+
if (!pin) return;
|
|
58
|
+
const key = toolbarPinKey(pin);
|
|
59
|
+
if (out.includes(key) || out.length >= MAX_STORED_TOOLBAR_PINS) return;
|
|
60
|
+
out.push(key);
|
|
61
|
+
});
|
|
62
|
+
return out;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** The pins the toolbar draws: the configured keys, in the CONFIG's order, resolved against the
|
|
66
|
+
* favourites that exist now, and cut to the number of buttons the row can carry.
|
|
67
|
+
*
|
|
68
|
+
* A key whose pin is gone — unpinned here, or in MulmoClaude, which writes the same file — is
|
|
69
|
+
* skipped rather than drawn: the title and icon come from the pin, so there is nothing to label it
|
|
70
|
+
* with. Skipped, not deleted, and it does not consume one of the five: re-pin it and the button is
|
|
71
|
+
* back exactly where it was.
|
|
72
|
+
*
|
|
73
|
+
* What counts as "gone" is gone from the list the CALLER holds. The client store reads
|
|
74
|
+
* `/api/shortcuts` once per page, so a pin removed by the other app is still drawn until something
|
|
75
|
+
* re-reads it — which opening Settings' Toolbar pins does (Codex, PR #1991). */
|
|
76
|
+
export function resolveToolbarPins(shortcuts: readonly Shortcut[], keys: readonly string[]): Shortcut[] {
|
|
77
|
+
const byKey = new Map(shortcuts.map((shortcut) => [toolbarPinKey(shortcut), shortcut]));
|
|
78
|
+
return keys
|
|
79
|
+
.flatMap((key) => {
|
|
80
|
+
const shortcut = byKey.get(key);
|
|
81
|
+
return shortcut ? [shortcut] : [];
|
|
82
|
+
})
|
|
83
|
+
.slice(0, MAX_TOOLBAR_PINS);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** The list to save after promoting or demoting one pin.
|
|
87
|
+
*
|
|
88
|
+
* DEMOTING removes exactly the key the user unticked, and nothing else. PROMOTING appends, so a
|
|
89
|
+
* hand-ordered config is not reshuffled by ticking one more box. Neither ever drops a key that was
|
|
90
|
+
* not the subject of the click — see `MAX_STORED_TOOLBAR_PINS` for why that restraint is the whole
|
|
91
|
+
* design rather than an omission.
|
|
92
|
+
*
|
|
93
|
+
* `live` is what is pinned right now (`toolbarPinKey` of each favourite). It is read ONLY to count
|
|
94
|
+
* the promotions that can actually be drawn: a key whose pin is gone draws nothing, so counting it
|
|
95
|
+
* toward the five would fill the row with buttons that are not there and leave nothing to untick
|
|
96
|
+
* (Codex, PR #1991). Nothing is deleted on the strength of it, which is what makes a stale `live`
|
|
97
|
+
* harmless here — at worst the count is off by one until the next read.
|
|
98
|
+
*
|
|
99
|
+
* Returns the SAME array when nothing would change: already promoted, not promoted anyway, or a
|
|
100
|
+
* promotion refused because the row is full. */
|
|
101
|
+
export function nextToolbarPins(keys: readonly string[], live: readonly string[], key: string, promote: boolean): readonly string[] {
|
|
102
|
+
if (!promote) return keys.includes(key) ? keys.filter((entry) => entry !== key) : keys;
|
|
103
|
+
if (keys.includes(key)) return keys;
|
|
104
|
+
const drawn = keys.filter((entry) => live.includes(entry)).length;
|
|
105
|
+
if (drawn >= MAX_TOOLBAR_PINS || keys.length >= MAX_STORED_TOOLBAR_PINS) return keys;
|
|
106
|
+
return [...keys, key];
|
|
107
|
+
}
|