@band-app/server 0.12.0 → 0.13.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/dist/client/assets/{DockviewTerminalContainer-B6ls12RP.js → DockviewTerminalContainer-DY9YDc8o.js} +2 -2
- package/dist/client/assets/TerminalPanel-TQGgFc5g.js +4 -0
- package/dist/client/assets/{_basePickBy-D4ggI4i0.js → _basePickBy-CvS-VtsR.js} +1 -1
- package/dist/client/assets/{_baseUniq-E0-L58sh.js → _baseUniq-DFCKF5vj.js} +1 -1
- package/dist/client/assets/{arc-Cd0JgGvM.js → arc-BatjPjaN.js} +1 -1
- package/dist/client/assets/{architectureDiagram-VXUJARFQ-BngJQDkI.js → architectureDiagram-VXUJARFQ-Cnb8SKvZ.js} +1 -1
- package/dist/client/assets/{blockDiagram-VD42YOAC-dcYRXfph.js → blockDiagram-VD42YOAC-B6-qvfYN.js} +1 -1
- package/dist/client/assets/{c4Diagram-YG6GDRKO-B_8XfvfL.js → c4Diagram-YG6GDRKO-DAjB1VyY.js} +1 -1
- package/dist/client/assets/channel-DSgmIANq.js +1 -0
- package/dist/client/assets/{chunk-4BX2VUAB-CFd2iELe.js → chunk-4BX2VUAB-vjmc3quM.js} +1 -1
- package/dist/client/assets/{chunk-55IACEB6-ZI-o1tfy.js → chunk-55IACEB6-DDZjRfYY.js} +1 -1
- package/dist/client/assets/{chunk-B4BG7PRW-GcKh0u0t.js → chunk-B4BG7PRW-CKceWukV.js} +1 -1
- package/dist/client/assets/{chunk-DI55MBZ5-asFoohq8.js → chunk-DI55MBZ5-CRnRm2L0.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-GmvgLSQe.js → chunk-FMBD7UC4-Cogf-yAQ.js} +1 -1
- package/dist/client/assets/{chunk-QN33PNHL-CbSPviK8.js → chunk-QN33PNHL-D72Zq7QV.js} +1 -1
- package/dist/client/assets/{chunk-QZHKN3VN-C5_8IhWQ.js → chunk-QZHKN3VN-HUKu5hjd.js} +1 -1
- package/dist/client/assets/{chunk-TZMSLE5B-CvwKA6rf.js → chunk-TZMSLE5B-I8YlhesW.js} +1 -1
- package/dist/client/assets/classDiagram-2ON5EDUG-D57JPCVZ.js +1 -0
- package/dist/client/assets/classDiagram-v2-WZHVMYZB-D57JPCVZ.js +1 -0
- package/dist/client/assets/clone-DAQJfloD.js +1 -0
- package/dist/client/assets/{cose-bilkent-S5V4N54A-CFOKjm00.js → cose-bilkent-S5V4N54A-CEay61-1.js} +1 -1
- package/dist/client/assets/{dagre-6UL2VRFP-XkiKAfAP.js → dagre-6UL2VRFP-BcAohjbk.js} +2 -2
- package/dist/client/assets/{diagram-PSM6KHXK-D7OJGDF1.js → diagram-PSM6KHXK-DdrIOdqS.js} +1 -1
- package/dist/client/assets/{diagram-QEK2KX5R-CNDqB5ur.js → diagram-QEK2KX5R-Bn79GyS6.js} +1 -1
- package/dist/client/assets/{diagram-S2PKOQOG-DHWhfYWy.js → diagram-S2PKOQOG-g66OM_Vs.js} +1 -1
- package/dist/client/assets/{erDiagram-Q2GNP2WA-DlhzEgw7.js → erDiagram-Q2GNP2WA-BD0geyEq.js} +1 -1
- package/dist/client/assets/{flowDiagram-NV44I4VS-BabYr6zd.js → flowDiagram-NV44I4VS-B2LLnQ89.js} +1 -1
- package/dist/client/assets/{ganttDiagram-JELNMOA3-CUHxh1TA.js → ganttDiagram-JELNMOA3-DsMjiqCd.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-V2S2FVAM-DSDd-EJ_.js → gitGraphDiagram-V2S2FVAM-BXAPNFr2.js} +1 -1
- package/dist/client/assets/{graph-DXNVnA6H.js → graph-CWoXix-C.js} +1 -1
- package/dist/client/assets/{highlighted-body-B3W2YXNL-B79dTSwx.js → highlighted-body-B3W2YXNL-DAdM8cs5.js} +1 -1
- package/dist/client/assets/{index-4srNURyv.js → index-6eC517Nz.js} +1 -1
- package/dist/client/assets/{index-D-7FXjIA.js → index-B-RxFKXA.js} +1 -1
- package/dist/client/assets/{index-EdY6fqdn.js → index-B9fPtAFu.js} +2 -2
- package/dist/client/assets/{index-Cu2rqq8f.js → index-BSQYe-ie.js} +1 -1
- package/dist/client/assets/{index-_J9aGl-d.js → index-B_aLrZ9l.js} +1 -1
- package/dist/client/assets/{index-CPo0kKmX.js → index-BcC597Ty.js} +1 -1
- package/dist/client/assets/{index-DktZbpGM.js → index-Bu7btU1P.js} +1 -1
- package/dist/client/assets/{index-Bvony7H7.js → index-C8jQlbkH.js} +1 -1
- package/dist/client/assets/{index-DRS8jN52.js → index-D-NVfOaX.js} +1 -1
- package/dist/client/assets/{index-CjixM2mV.js → index-D4Gbu0g1.js} +1 -1
- package/dist/client/assets/{index-okqFBuO9.js → index-DeUgVu9X.js} +1 -1
- package/dist/client/assets/{index-BVLYlk0y.js → index-DgRC8NQ1.js} +1 -1
- package/dist/client/assets/{index-5jOljAlc.js → index-Dkltc6JL.js} +1 -1
- package/dist/client/assets/{index-BNkxX-BD.js → index-GgiGfqqJ.js} +1 -1
- package/dist/client/assets/{index-DutxFSic.js → index-HueLRLHI.js} +1 -1
- package/dist/client/assets/{index-BqDdB0tU.js → index-U42qt8_H.js} +1 -1
- package/dist/client/assets/{index-BQU-JREs.js → index-W_gE5gc5.js} +1 -1
- package/dist/client/assets/{index-CTR_yHqa.js → index-iloqZ__Z.js} +1 -1
- package/dist/client/assets/{infoDiagram-HS3SLOUP-tDVIKvh1.js → infoDiagram-HS3SLOUP-DwKN27-C.js} +1 -1
- package/dist/client/assets/{journeyDiagram-XKPGCS4Q-cFY68TOP.js → journeyDiagram-XKPGCS4Q-BAhwB5gg.js} +1 -1
- package/dist/client/assets/{kanban-definition-3W4ZIXB7-CTQ8WBzj.js → kanban-definition-3W4ZIXB7-OI1AHf6B.js} +1 -1
- package/dist/client/assets/{layout-BDsby01A.js → layout-CC7J3tBc.js} +1 -1
- package/dist/client/assets/{linear-B321DwjU.js → linear-CVYb-eZl.js} +1 -1
- package/dist/client/assets/main-4FaYQ5DQ.css +1 -0
- package/dist/client/assets/{main-gKX_m5Ko.js → main-CYVBBY3m.js} +217 -210
- package/dist/client/assets/{mindmap-definition-VGOIOE7T-CMbXBgve.js → mindmap-definition-VGOIOE7T-BPKIWA4L.js} +1 -1
- package/dist/client/assets/{pieDiagram-ADFJNKIX-pTHsfneI.js → pieDiagram-ADFJNKIX-DeHQukcp.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-AYHSOK5B-DNGhzaPi.js → quadrantDiagram-AYHSOK5B-DIA0izdW.js} +1 -1
- package/dist/client/assets/{requirementDiagram-UZGBJVZJ-E5dChVU-.js → requirementDiagram-UZGBJVZJ-DsiZI-w_.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-TZEHDZUN-B858cwBV.js → sankeyDiagram-TZEHDZUN-B3Rxgvvo.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-WL72ISMW-Clbop1Lh.js → sequenceDiagram-WL72ISMW-CdnagSZH.js} +1 -1
- package/dist/client/assets/{square-terminal-zBRLwN9m.js → square-terminal-BA_DN1Fj.js} +1 -1
- package/dist/client/assets/{stateDiagram-FKZM4ZOC-BRIoi38Z.js → stateDiagram-FKZM4ZOC-C9HYyYWO.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-4FDKWEC3-Jpp4ZnR3.js +1 -0
- package/dist/client/assets/{timeline-definition-IT6M3QCI-C26dPldo.js → timeline-definition-IT6M3QCI-BQXhdJ4l.js} +1 -1
- package/dist/client/assets/{treemap-GDKQZRPO-l9jcKCpW.js → treemap-GDKQZRPO-DbtL6I-E.js} +1 -1
- package/dist/client/assets/{useSessionListContext-MLsp0tQx.js → useSessionListContext-BDhxj2sp.js} +1 -1
- package/dist/client/assets/workspace._workspaceId-DrdBQ3ad.js +1 -0
- package/dist/client/assets/{workspace._workspaceId.changes-sSj8FhGC.js → workspace._workspaceId.changes-CV01j_RF.js} +1 -1
- package/dist/client/assets/workspace._workspaceId.code-Di11hnHW.js +1 -0
- package/dist/client/assets/{workspace._workspaceId.code._-BVzwjj6w.js → workspace._workspaceId.code._-4eyD4Vu7.js} +1 -1
- package/dist/client/assets/{workspace._workspaceId.code.index-V90FWX2z.js → workspace._workspaceId.code.index-BazGXzJC.js} +1 -1
- package/dist/client/assets/{workspace._workspaceId.index-Cczg6N56.js → workspace._workspaceId.index-CpmZsM_r.js} +1 -1
- package/dist/client/assets/{workspace._workspaceId.terminal-CFJVgZLo.js → workspace._workspaceId.terminal-Sx7Ov6J4.js} +2 -2
- package/dist/client/assets/{xychartDiagram-PRI3JC2R-Cwsp2BWQ.js → xychartDiagram-PRI3JC2R-B9LSl0HQ.js} +1 -1
- package/dist/server/assets/{DockviewTerminalContainer-B5lZSZ8Y.js → DockviewTerminalContainer-DVzWSMti.js} +3 -3
- package/dist/server/assets/TerminalPanel-BlWVFHNT.js +1016 -0
- package/dist/server/assets/{_basePickBy-CD1u5dYZ.js → _basePickBy-CTVBF0Kh.js} +2 -2
- package/dist/server/assets/{_baseUniq-BdmRs4hY.js → _baseUniq-BF3qFyKL.js} +1 -1
- package/dist/server/assets/{_tanstack-start-manifest_v-D4xipytj.js → _tanstack-start-manifest_v-sqmMCsD8.js} +1 -1
- package/dist/server/assets/{arc-CbjxAGh2.js → arc-CKzdlSDJ.js} +1 -1
- package/dist/server/assets/{architecture-7HQA4BMR-PvoHywyl.js → architecture-7HQA4BMR-C4KC3q3z.js} +6 -6
- package/dist/server/assets/{architectureDiagram-VXUJARFQ-CYVR-HeO.js → architectureDiagram-VXUJARFQ-ARkeTUdc.js} +6 -6
- package/dist/server/assets/{blockDiagram-VD42YOAC-oFwYjSKc.js → blockDiagram-VD42YOAC-lsOOO08K.js} +6 -6
- package/dist/server/assets/{c4Diagram-YG6GDRKO-BRcTuK4Y.js → c4Diagram-YG6GDRKO-CrZA0PWP.js} +2 -2
- package/dist/server/assets/{channel-DkpUIvTk.js → channel-Dd6dv_cn.js} +1 -1
- package/dist/server/assets/{chunk-4BX2VUAB-Db1P5ZKT.js → chunk-4BX2VUAB-CfcFinva.js} +1 -1
- package/dist/server/assets/{chunk-55IACEB6-CgF3c_s9.js → chunk-55IACEB6-C_zn3X68.js} +1 -1
- package/dist/server/assets/{chunk-B4BG7PRW-C4lPp_gR.js → chunk-B4BG7PRW-WqOdzIwb.js} +4 -4
- package/dist/server/assets/{chunk-DI55MBZ5-DBc5FE0O.js → chunk-DI55MBZ5-BazpEGnL.js} +3 -3
- package/dist/server/assets/{chunk-FMBD7UC4-BmyVhHTD.js → chunk-FMBD7UC4-abFXQK9X.js} +1 -1
- package/dist/server/assets/{chunk-QN33PNHL-MEMc-HV1.js → chunk-QN33PNHL-mQORkMwR.js} +1 -1
- package/dist/server/assets/{chunk-QZHKN3VN-D0s55-ob.js → chunk-QZHKN3VN-W_mObo75.js} +1 -1
- package/dist/server/assets/{chunk-TZMSLE5B-CHLAtcyP.js → chunk-TZMSLE5B-f9JxfLrS.js} +1 -1
- package/dist/server/assets/{classDiagram-v2-WZHVMYZB-D_KqC8CP.js → classDiagram-2ON5EDUG-D9B26ZaP.js} +5 -5
- package/dist/server/assets/{classDiagram-2ON5EDUG-D_KqC8CP.js → classDiagram-v2-WZHVMYZB-D9B26ZaP.js} +5 -5
- package/dist/server/assets/{clone-pAcegSV_.js → clone-dm3Dgnj4.js} +1 -1
- package/dist/server/assets/{cose-bilkent-S5V4N54A-I5bimXZO.js → cose-bilkent-S5V4N54A-FP17KwdC.js} +1 -1
- package/dist/server/assets/{dagre-6UL2VRFP-BFEXHp44.js → dagre-6UL2VRFP-D3G7rpV5.js} +6 -6
- package/dist/server/assets/{diagram-PSM6KHXK-B4UG8ra3.js → diagram-PSM6KHXK-CblBgzmP.js} +7 -7
- package/dist/server/assets/{diagram-QEK2KX5R-B65vkBbU.js → diagram-QEK2KX5R-GC4Djt9A.js} +6 -6
- package/dist/server/assets/{diagram-S2PKOQOG-DUV65VxH.js → diagram-S2PKOQOG-5B6KDW-y.js} +6 -6
- package/dist/server/assets/{erDiagram-Q2GNP2WA-eEKIOvT3.js → erDiagram-Q2GNP2WA-Be8xw8rI.js} +4 -4
- package/dist/server/assets/{flowDiagram-NV44I4VS-B0GxfKRu.js → flowDiagram-NV44I4VS-jzmSul5A.js} +5 -5
- package/dist/server/assets/{ganttDiagram-JELNMOA3-etJ_j_I0.js → ganttDiagram-JELNMOA3-DWzYYJJD.js} +2 -2
- package/dist/server/assets/{gitGraph-G5XIXVHT-DE59sCs-.js → gitGraph-G5XIXVHT-DBh_B9aL.js} +6 -6
- package/dist/server/assets/{gitGraphDiagram-V2S2FVAM-BZuXPjR3.js → gitGraphDiagram-V2S2FVAM-DtVbt-rt.js} +7 -7
- package/dist/server/assets/{graph-HlbQNgKh.js → graph-DX5yyy2e.js} +2 -2
- package/dist/server/assets/{highlighted-body-B3W2YXNL-CGZmJ48I.js → highlighted-body-B3W2YXNL-jVL8aR3Z.js} +1 -1
- package/dist/server/assets/{index-DfuZ1SKx.js → index-B5nFl_AI.js} +2 -2
- package/dist/server/assets/{index-KO85K6gg.js → index-BAdVYDyw.js} +2 -2
- package/dist/server/assets/{index-oAvn7Kbw.js → index-BhOjaygO.js} +2 -2
- package/dist/server/assets/{index-BquoD96E.js → index-BirnOPeb.js} +2 -2
- package/dist/server/assets/{index-kEUC1eer.js → index-BjR3sOAq.js} +2 -2
- package/dist/server/assets/{index-DsW29Azb.js → index-BoxcvdZf.js} +4 -4
- package/dist/server/assets/{index-DoEmUqp6.js → index-BsKQIZIq.js} +2 -2
- package/dist/server/assets/{index-Cq-S8ocb.js → index-CBiZXyl4.js} +1 -1
- package/dist/server/assets/{index-0Vp2EuiF.js → index-CD24s2ge.js} +3 -3
- package/dist/server/assets/{index-B2NbB5n_.js → index-CJFioIPI.js} +2 -2
- package/dist/server/assets/{index-BYATJkV0.js → index-CVpPQcdL.js} +1 -1
- package/dist/server/assets/{index-CqJMMgyN.js → index-CeFUhbU-.js} +2 -2
- package/dist/server/assets/{index-DVIqYu1B.js → index-DHKWIWry.js} +5 -5
- package/dist/server/assets/{index-CozxyE6F.js → index-DYzy4J8Y.js} +2 -2
- package/dist/server/assets/{index-CcRwhNJ0.js → index-DiZw5Slc.js} +3 -3
- package/dist/server/assets/{index-B_gLaw5q.js → index-fJ18TF5w.js} +5 -5
- package/dist/server/assets/{index-CReShbyg.js → index-icISvAHb.js} +2 -2
- package/dist/server/assets/{index-BggD7xEz.js → index-x1PcD09R.js} +2 -2
- package/dist/server/assets/{info-VBDWY6EO-DFb5bmEE.js → info-VBDWY6EO-CgGw-KWo.js} +6 -6
- package/dist/server/assets/{infoDiagram-HS3SLOUP-DUDXdFYe.js → infoDiagram-HS3SLOUP-Bm300kuC.js} +5 -5
- package/dist/server/assets/{journeyDiagram-XKPGCS4Q-BnEH3kK2.js → journeyDiagram-XKPGCS4Q-i8WJyl2v.js} +4 -4
- package/dist/server/assets/{kanban-definition-3W4ZIXB7-BPnM08KT.js → kanban-definition-3W4ZIXB7-BxqtaaIr.js} +2 -2
- package/dist/server/assets/{layout-CnirhobE.js → layout-C81zgoUj.js} +4 -4
- package/dist/server/assets/{linear-CLJDiqhJ.js → linear-dyZZUN6P.js} +1 -1
- package/dist/server/assets/{mermaid-3ZIDBTTL-Dyndt6u2.js → mermaid-3ZIDBTTL-bTD72h2_.js} +1 -1
- package/dist/server/assets/{mermaid-parser.core-DxDjr9RA.js → mermaid-parser.core-BpodyVoJ.js} +11 -11
- package/dist/server/assets/{mindmap-definition-VGOIOE7T-CPnGCXl9.js → mindmap-definition-VGOIOE7T-CGVjAsWC.js} +3 -3
- package/dist/server/assets/{packet-DYOGHKS2-Bt7VjiqG.js → packet-DYOGHKS2-BeHd7ss_.js} +6 -6
- package/dist/server/assets/{pie-VRWISCQL-CpD4BgbA.js → pie-VRWISCQL-CtMn8fFZ.js} +6 -6
- package/dist/server/assets/{pieDiagram-ADFJNKIX-BSx8zl8i.js → pieDiagram-ADFJNKIX-CS0mGgZR.js} +7 -7
- package/dist/server/assets/{quadrantDiagram-AYHSOK5B-B08pRdHD.js → quadrantDiagram-AYHSOK5B-C0QMIITK.js} +2 -2
- package/dist/server/assets/{radar-ZZBFDIW7-BeHVWQbv.js → radar-ZZBFDIW7-DIYuMHay.js} +6 -6
- package/dist/server/assets/{requirementDiagram-UZGBJVZJ-BtjVtNFU.js → requirementDiagram-UZGBJVZJ-Cu_xdr4s.js} +3 -3
- package/dist/server/assets/{router-VlIt4cC5.js → router-emaiBwQ1.js} +377 -240
- package/dist/server/assets/{sankeyDiagram-TZEHDZUN-CG6DECc6.js → sankeyDiagram-TZEHDZUN-sK1eHAYK.js} +1 -1
- package/dist/server/assets/{sequenceDiagram-WL72ISMW-DEfVJQli.js → sequenceDiagram-WL72ISMW-BxdrIxsT.js} +3 -3
- package/dist/server/assets/{square-terminal-DTO02O7S.js → square-terminal-DaamWbdC.js} +1 -1
- package/dist/server/assets/{stateDiagram-FKZM4ZOC-DblYyAIC.js → stateDiagram-FKZM4ZOC-YKAev_OI.js} +8 -8
- package/dist/server/assets/{stateDiagram-v2-4FDKWEC3-YhMCeOsH.js → stateDiagram-v2-4FDKWEC3-WXMNfM85.js} +4 -4
- package/dist/server/assets/{timeline-definition-IT6M3QCI-CDEJSclA.js → timeline-definition-IT6M3QCI-DrdVpYke.js} +2 -2
- package/dist/server/assets/{treemap-GDKQZRPO-Tg2S6NSt.js → treemap-GDKQZRPO-DZHSpt06.js} +6 -6
- package/dist/server/assets/{workspace._workspaceId-CVnwrhnX.js → workspace._workspaceId-KpmCwO8V.js} +10 -4
- package/dist/server/assets/{workspace._workspaceId.changes-CsJMr9Ve.js → workspace._workspaceId.changes-DF6oupkK.js} +1 -1
- package/dist/server/assets/{workspace._workspaceId.code._-CjSLVs7D.js → workspace._workspaceId.code._-fbsbn7Pa.js} +1 -1
- package/dist/server/assets/{workspace._workspaceId.code.index-BhUZuUX-.js → workspace._workspaceId.code.index-D2dHFGQr.js} +1 -1
- package/dist/server/assets/{workspace._workspaceId.index-CmBIxfCl.js → workspace._workspaceId.index-DpLhp52B.js} +1 -1
- package/dist/server/assets/{workspace._workspaceId.terminal-BehWU5SY.js → workspace._workspaceId.terminal-Dk3GXgkq.js} +2 -2
- package/dist/server/assets/{xychartDiagram-PRI3JC2R-DAT7FXvH.js → xychartDiagram-PRI3JC2R-BXCdvcf7.js} +2 -2
- package/dist/server/server.js +2 -2
- package/dist/start-server.mjs +662 -636
- package/package.json +6 -5
- package/dist/client/assets/TerminalPanel-CTjPLr1l.js +0 -4
- package/dist/client/assets/channel-BN9rr7kd.js +0 -1
- package/dist/client/assets/classDiagram-2ON5EDUG-f-3sWSXi.js +0 -1
- package/dist/client/assets/classDiagram-v2-WZHVMYZB-f-3sWSXi.js +0 -1
- package/dist/client/assets/clone-DyjKoDMl.js +0 -1
- package/dist/client/assets/main-7lUOPiu0.css +0 -1
- package/dist/client/assets/stateDiagram-v2-4FDKWEC3-BXdrXPzW.js +0 -1
- package/dist/client/assets/workspace._workspaceId-CKxw1xn1.js +0 -1
- package/dist/client/assets/workspace._workspaceId.code-CTG7u893.js +0 -1
- package/dist/server/assets/TerminalPanel-_8vdN94i.js +0 -442
|
@@ -0,0 +1,1016 @@
|
|
|
1
|
+
import { r as reactExports, j as jsxRuntimeExports } from "../server.js";
|
|
2
|
+
import { E as createLucideIcon, bh as ArrowLeft, c3 as ArrowRight, c4 as ArrowUp, c5 as ArrowDown, X, c6 as ClipboardPaste, b as useSettingsQuery, c7 as openExternalUrl, c8 as SearchBar } from "./router-emaiBwQ1.js";
|
|
3
|
+
import "node:async_hooks";
|
|
4
|
+
import "node:stream";
|
|
5
|
+
import "util";
|
|
6
|
+
import "crypto";
|
|
7
|
+
import "async_hooks";
|
|
8
|
+
import "stream";
|
|
9
|
+
import "node:stream/web";
|
|
10
|
+
import "node:process";
|
|
11
|
+
import "node:path";
|
|
12
|
+
import "node:url";
|
|
13
|
+
const __iconNode$1 = [
|
|
14
|
+
["rect", { width: "8", height: "4", x: "8", y: "2", rx: "1", ry: "1", key: "tgr4d6" }],
|
|
15
|
+
["path", { d: "M8 4H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2", key: "4jdomd" }],
|
|
16
|
+
["path", { d: "M16 4h2a2 2 0 0 1 2 2v4", key: "3hqy98" }],
|
|
17
|
+
["path", { d: "M21 14H11", key: "1bme5i" }],
|
|
18
|
+
["path", { d: "m15 10-4 4 4 4", key: "5dvupr" }]
|
|
19
|
+
];
|
|
20
|
+
const ClipboardCopy = createLucideIcon("clipboard-copy", __iconNode$1);
|
|
21
|
+
const __iconNode = [
|
|
22
|
+
["path", { d: "M12 20h-1a2 2 0 0 1-2-2 2 2 0 0 1-2 2H6", key: "1528k5" }],
|
|
23
|
+
["path", { d: "M13 8h7a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2h-7", key: "13ksps" }],
|
|
24
|
+
["path", { d: "M5 16H4a2 2 0 0 1-2-2v-4a2 2 0 0 1 2-2h1", key: "1n9rhb" }],
|
|
25
|
+
["path", { d: "M6 4h1a2 2 0 0 1 2 2 2 2 0 0 1 2-2h1", key: "1mj8rg" }],
|
|
26
|
+
["path", { d: "M9 6v12", key: "velyjx" }]
|
|
27
|
+
];
|
|
28
|
+
const TextCursorInput = createLucideIcon("text-cursor-input", __iconNode);
|
|
29
|
+
const TERMINAL_TOOLBAR_HEIGHT_PX = 49;
|
|
30
|
+
const TOUCH_QUERY = "(hover: none) and (pointer: coarse)";
|
|
31
|
+
function useVirtualKeyboardToolbar() {
|
|
32
|
+
const [enabled, setEnabled] = reactExports.useState(
|
|
33
|
+
() => typeof window === "undefined" ? false : window.matchMedia(TOUCH_QUERY).matches
|
|
34
|
+
);
|
|
35
|
+
const [bottomOffset, setBottomOffset] = reactExports.useState(0);
|
|
36
|
+
reactExports.useEffect(() => {
|
|
37
|
+
if (typeof window === "undefined") return;
|
|
38
|
+
const mql = window.matchMedia(TOUCH_QUERY);
|
|
39
|
+
const handler = (e) => setEnabled(e.matches);
|
|
40
|
+
setEnabled(mql.matches);
|
|
41
|
+
mql.addEventListener("change", handler);
|
|
42
|
+
return () => mql.removeEventListener("change", handler);
|
|
43
|
+
}, []);
|
|
44
|
+
reactExports.useEffect(() => {
|
|
45
|
+
if (typeof window === "undefined") return;
|
|
46
|
+
if (!enabled) {
|
|
47
|
+
setBottomOffset(0);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const vv = window.visualViewport;
|
|
51
|
+
if (!vv) return;
|
|
52
|
+
const update = () => {
|
|
53
|
+
const keyboardHeight = Math.max(0, window.innerHeight - (vv.offsetTop + vv.height));
|
|
54
|
+
setBottomOffset(keyboardHeight);
|
|
55
|
+
};
|
|
56
|
+
update();
|
|
57
|
+
vv.addEventListener("resize", update);
|
|
58
|
+
vv.addEventListener("scroll", update);
|
|
59
|
+
window.addEventListener("resize", update);
|
|
60
|
+
return () => {
|
|
61
|
+
vv.removeEventListener("resize", update);
|
|
62
|
+
vv.removeEventListener("scroll", update);
|
|
63
|
+
window.removeEventListener("resize", update);
|
|
64
|
+
};
|
|
65
|
+
}, [enabled]);
|
|
66
|
+
return {
|
|
67
|
+
enabled,
|
|
68
|
+
bottomOffset,
|
|
69
|
+
contentBottomInset: enabled ? TERMINAL_TOOLBAR_HEIGHT_PX : 0
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
const WORD_RE = /[A-Za-z0-9_./:\-+@~]/;
|
|
73
|
+
function pointToCell(clientX, clientY, terminal, screenEl) {
|
|
74
|
+
const rect = screenEl.getBoundingClientRect();
|
|
75
|
+
if (rect.width <= 0 || rect.height <= 0 || terminal.cols <= 0 || terminal.rows <= 0) {
|
|
76
|
+
return { col: 0, row: terminal.buffer.active.viewportY };
|
|
77
|
+
}
|
|
78
|
+
const cellW = rect.width / terminal.cols;
|
|
79
|
+
const cellH = rect.height / terminal.rows;
|
|
80
|
+
const col = clamp(Math.floor((clientX - rect.left) / cellW), 0, terminal.cols - 1);
|
|
81
|
+
const viewportRow = clamp(Math.floor((clientY - rect.top) / cellH), 0, terminal.rows - 1);
|
|
82
|
+
return { col, row: terminal.buffer.active.viewportY + viewportRow };
|
|
83
|
+
}
|
|
84
|
+
function findWordBoundaries(line, col) {
|
|
85
|
+
if (col < 0 || col >= line.length || !WORD_RE.test(line[col])) {
|
|
86
|
+
const safeCol = clamp(col, 0, Math.max(0, line.length));
|
|
87
|
+
return { start: safeCol, end: safeCol + 1 };
|
|
88
|
+
}
|
|
89
|
+
let start = col;
|
|
90
|
+
while (start > 0 && WORD_RE.test(line[start - 1])) start--;
|
|
91
|
+
let end = col + 1;
|
|
92
|
+
while (end < line.length && WORD_RE.test(line[end])) end++;
|
|
93
|
+
return { start, end };
|
|
94
|
+
}
|
|
95
|
+
function getLineText(terminal, row) {
|
|
96
|
+
return terminal.buffer.active.getLine(row)?.translateToString(false) ?? "";
|
|
97
|
+
}
|
|
98
|
+
function moveCell(cell, dir, terminal) {
|
|
99
|
+
const cols = terminal.cols;
|
|
100
|
+
const lastRow = Math.max(0, terminal.buffer.active.length - 1);
|
|
101
|
+
switch (dir) {
|
|
102
|
+
case "left":
|
|
103
|
+
if (cell.col > 0) return { col: cell.col - 1, row: cell.row };
|
|
104
|
+
if (cell.row > 0) return { col: cols - 1, row: cell.row - 1 };
|
|
105
|
+
return cell;
|
|
106
|
+
case "right":
|
|
107
|
+
if (cell.col < cols - 1) return { col: cell.col + 1, row: cell.row };
|
|
108
|
+
if (cell.row < lastRow) return { col: 0, row: cell.row + 1 };
|
|
109
|
+
return cell;
|
|
110
|
+
case "up":
|
|
111
|
+
if (cell.row > 0) return { col: cell.col, row: cell.row - 1 };
|
|
112
|
+
return cell;
|
|
113
|
+
case "down":
|
|
114
|
+
if (cell.row < lastRow) return { col: cell.col, row: cell.row + 1 };
|
|
115
|
+
return cell;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
function applySelection(terminal, anchor, head) {
|
|
119
|
+
const cols = terminal.cols;
|
|
120
|
+
if (cols <= 0) return;
|
|
121
|
+
const anchorLinear = anchor.row * cols + anchor.col;
|
|
122
|
+
const headLinear = head.row * cols + head.col;
|
|
123
|
+
const startLinear = Math.min(anchorLinear, headLinear);
|
|
124
|
+
const endLinear = Math.max(anchorLinear, headLinear);
|
|
125
|
+
const length = endLinear - startLinear + 1;
|
|
126
|
+
const startRow = Math.floor(startLinear / cols);
|
|
127
|
+
const startCol = startLinear - startRow * cols;
|
|
128
|
+
terminal.select(startCol, startRow, length);
|
|
129
|
+
}
|
|
130
|
+
function wordSelectionAt(cell, lineText) {
|
|
131
|
+
const { start, end } = findWordBoundaries(lineText, cell.col);
|
|
132
|
+
return {
|
|
133
|
+
anchor: { col: start, row: cell.row },
|
|
134
|
+
// `head` is the last cell of the selection (inclusive). `end` from
|
|
135
|
+
// findWordBoundaries is exclusive, so subtract one.
|
|
136
|
+
head: { col: Math.max(start, end - 1), row: cell.row }
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
function clamp(n, lo, hi) {
|
|
140
|
+
if (n < lo) return lo;
|
|
141
|
+
if (n > hi) return hi;
|
|
142
|
+
return n;
|
|
143
|
+
}
|
|
144
|
+
async function writeClipboardText(text) {
|
|
145
|
+
if (typeof navigator !== "undefined" && navigator.clipboard?.writeText) {
|
|
146
|
+
try {
|
|
147
|
+
await navigator.clipboard.writeText(text);
|
|
148
|
+
return true;
|
|
149
|
+
} catch {
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return legacyCopy(text);
|
|
153
|
+
}
|
|
154
|
+
function legacyCopy(text) {
|
|
155
|
+
if (typeof document === "undefined") return false;
|
|
156
|
+
const textarea = document.createElement("textarea");
|
|
157
|
+
textarea.value = text;
|
|
158
|
+
textarea.setAttribute("readonly", "");
|
|
159
|
+
textarea.style.position = "fixed";
|
|
160
|
+
textarea.style.top = "0";
|
|
161
|
+
textarea.style.left = "0";
|
|
162
|
+
textarea.style.width = "1px";
|
|
163
|
+
textarea.style.height = "1px";
|
|
164
|
+
textarea.style.padding = "0";
|
|
165
|
+
textarea.style.border = "0";
|
|
166
|
+
textarea.style.opacity = "0";
|
|
167
|
+
textarea.style.pointerEvents = "none";
|
|
168
|
+
document.body.appendChild(textarea);
|
|
169
|
+
const previouslyFocused = document.activeElement;
|
|
170
|
+
try {
|
|
171
|
+
textarea.focus();
|
|
172
|
+
textarea.select();
|
|
173
|
+
textarea.setSelectionRange(0, text.length);
|
|
174
|
+
return document.execCommand("copy");
|
|
175
|
+
} catch {
|
|
176
|
+
return false;
|
|
177
|
+
} finally {
|
|
178
|
+
document.body.removeChild(textarea);
|
|
179
|
+
previouslyFocused?.focus?.();
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
async function readClipboardText() {
|
|
183
|
+
if (typeof navigator === "undefined" || !navigator.clipboard?.readText) {
|
|
184
|
+
return "";
|
|
185
|
+
}
|
|
186
|
+
try {
|
|
187
|
+
return await navigator.clipboard.readText();
|
|
188
|
+
} catch {
|
|
189
|
+
return "";
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
const SEQ_ESC = "\x1B";
|
|
193
|
+
const SEQ_TAB = " ";
|
|
194
|
+
const SEQ_ARROW_UP = "\x1B[A";
|
|
195
|
+
const SEQ_ARROW_DOWN = "\x1B[B";
|
|
196
|
+
const SEQ_ARROW_RIGHT = "\x1B[C";
|
|
197
|
+
const SEQ_ARROW_LEFT = "\x1B[D";
|
|
198
|
+
const tap = (fn) => (e) => {
|
|
199
|
+
e.preventDefault();
|
|
200
|
+
void fn();
|
|
201
|
+
};
|
|
202
|
+
function TerminalToolbar({
|
|
203
|
+
terminal,
|
|
204
|
+
sendInput,
|
|
205
|
+
pendingCtrl,
|
|
206
|
+
onToggleCtrl,
|
|
207
|
+
selectionMode,
|
|
208
|
+
onExtendSelection,
|
|
209
|
+
onExitSelection,
|
|
210
|
+
onSelectAll
|
|
211
|
+
}) {
|
|
212
|
+
const { enabled, bottomOffset } = useVirtualKeyboardToolbar();
|
|
213
|
+
const handleCopy = reactExports.useCallback(async () => {
|
|
214
|
+
if (!terminal.hasSelection()) return;
|
|
215
|
+
const text = terminal.getSelection();
|
|
216
|
+
if (!text) return;
|
|
217
|
+
const ok = await writeClipboardText(text);
|
|
218
|
+
if (!ok) console.warn("[TerminalToolbar] clipboard write failed");
|
|
219
|
+
}, [terminal]);
|
|
220
|
+
const handleCopyAndExit = reactExports.useCallback(async () => {
|
|
221
|
+
await handleCopy();
|
|
222
|
+
onExitSelection();
|
|
223
|
+
}, [handleCopy, onExitSelection]);
|
|
224
|
+
const handlePaste = reactExports.useCallback(async () => {
|
|
225
|
+
const text = await readClipboardText();
|
|
226
|
+
if (text) sendInput(text);
|
|
227
|
+
}, [sendInput]);
|
|
228
|
+
const idleKeyButtons = reactExports.useMemo(
|
|
229
|
+
() => [
|
|
230
|
+
{ label: "Esc", seq: SEQ_ESC, ariaLabel: "Send Escape" },
|
|
231
|
+
{ label: "Tab", seq: SEQ_TAB, ariaLabel: "Send Tab" }
|
|
232
|
+
],
|
|
233
|
+
[]
|
|
234
|
+
);
|
|
235
|
+
const idleArrows = reactExports.useMemo(
|
|
236
|
+
() => [
|
|
237
|
+
{ Icon: ArrowLeft, seq: SEQ_ARROW_LEFT, ariaLabel: "Arrow Left" },
|
|
238
|
+
{ Icon: ArrowRight, seq: SEQ_ARROW_RIGHT, ariaLabel: "Arrow Right" },
|
|
239
|
+
{ Icon: ArrowUp, seq: SEQ_ARROW_UP, ariaLabel: "Arrow Up" },
|
|
240
|
+
{ Icon: ArrowDown, seq: SEQ_ARROW_DOWN, ariaLabel: "Arrow Down" }
|
|
241
|
+
],
|
|
242
|
+
[]
|
|
243
|
+
);
|
|
244
|
+
const selectionArrows = reactExports.useMemo(
|
|
245
|
+
() => [
|
|
246
|
+
{ Icon: ArrowLeft, dir: "left", ariaLabel: "Extend selection left" },
|
|
247
|
+
{ Icon: ArrowRight, dir: "right", ariaLabel: "Extend selection right" },
|
|
248
|
+
{ Icon: ArrowUp, dir: "up", ariaLabel: "Extend selection up" },
|
|
249
|
+
{ Icon: ArrowDown, dir: "down", ariaLabel: "Extend selection down" }
|
|
250
|
+
],
|
|
251
|
+
[]
|
|
252
|
+
);
|
|
253
|
+
if (!enabled) return null;
|
|
254
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
255
|
+
"div",
|
|
256
|
+
{
|
|
257
|
+
"data-testid": "terminal-toolbar",
|
|
258
|
+
"data-mode": selectionMode ? "selection" : "idle",
|
|
259
|
+
role: "toolbar",
|
|
260
|
+
"aria-label": selectionMode ? "Terminal selection controls" : "Terminal accessory keys",
|
|
261
|
+
style: { bottom: bottomOffset },
|
|
262
|
+
className: "fixed inset-x-0 z-50 flex justify-center border-t border-border bg-background/95 shadow-lg backdrop-blur-md",
|
|
263
|
+
children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex w-full max-w-3xl items-center gap-1 overflow-x-auto px-2 py-1.5", children: selectionMode ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
264
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
265
|
+
ToolbarButton,
|
|
266
|
+
{
|
|
267
|
+
ariaLabel: "Copy selection and exit",
|
|
268
|
+
title: "Copy",
|
|
269
|
+
onPointerDown: tap(handleCopyAndExit),
|
|
270
|
+
variant: "primary",
|
|
271
|
+
children: [
|
|
272
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(ClipboardCopy, { className: "size-4" }),
|
|
273
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs", children: "Copy" })
|
|
274
|
+
]
|
|
275
|
+
}
|
|
276
|
+
),
|
|
277
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
278
|
+
ToolbarButton,
|
|
279
|
+
{
|
|
280
|
+
ariaLabel: "Exit selection mode",
|
|
281
|
+
title: "Done",
|
|
282
|
+
onPointerDown: tap(onExitSelection),
|
|
283
|
+
children: [
|
|
284
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(X, { className: "size-4" }),
|
|
285
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs", children: "Done" })
|
|
286
|
+
]
|
|
287
|
+
}
|
|
288
|
+
),
|
|
289
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mx-1 h-6 w-px shrink-0 bg-border", "aria-hidden": "true" }),
|
|
290
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
291
|
+
"span",
|
|
292
|
+
{
|
|
293
|
+
className: "mr-1 shrink-0 select-none text-xs font-medium uppercase tracking-wide text-muted-foreground",
|
|
294
|
+
"aria-hidden": "true",
|
|
295
|
+
children: "Extend"
|
|
296
|
+
}
|
|
297
|
+
),
|
|
298
|
+
selectionArrows.map(({ Icon, dir, ariaLabel }) => /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
299
|
+
ToolbarButton,
|
|
300
|
+
{
|
|
301
|
+
ariaLabel,
|
|
302
|
+
title: ariaLabel,
|
|
303
|
+
onPointerDown: tap(() => onExtendSelection(dir)),
|
|
304
|
+
children: /* @__PURE__ */ jsxRuntimeExports.jsx(Icon, { className: "size-4" })
|
|
305
|
+
},
|
|
306
|
+
dir
|
|
307
|
+
))
|
|
308
|
+
] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
309
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
310
|
+
ToolbarButton,
|
|
311
|
+
{
|
|
312
|
+
ariaLabel: "Paste from clipboard",
|
|
313
|
+
title: "Paste",
|
|
314
|
+
onPointerDown: tap(handlePaste),
|
|
315
|
+
children: [
|
|
316
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(ClipboardPaste, { className: "size-4" }),
|
|
317
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs", children: "Paste" })
|
|
318
|
+
]
|
|
319
|
+
}
|
|
320
|
+
),
|
|
321
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
322
|
+
ToolbarButton,
|
|
323
|
+
{
|
|
324
|
+
ariaLabel: "Select all",
|
|
325
|
+
title: "Select all",
|
|
326
|
+
onPointerDown: tap(onSelectAll),
|
|
327
|
+
children: [
|
|
328
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(TextCursorInput, { className: "size-4" }),
|
|
329
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs", children: "All" })
|
|
330
|
+
]
|
|
331
|
+
}
|
|
332
|
+
),
|
|
333
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mx-1 h-6 w-px shrink-0 bg-border", "aria-hidden": "true" }),
|
|
334
|
+
idleKeyButtons.map(({ label, seq, ariaLabel }) => /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
335
|
+
ToolbarButton,
|
|
336
|
+
{
|
|
337
|
+
ariaLabel,
|
|
338
|
+
title: label,
|
|
339
|
+
onPointerDown: tap(() => sendInput(seq)),
|
|
340
|
+
children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs font-medium", children: label })
|
|
341
|
+
},
|
|
342
|
+
label
|
|
343
|
+
)),
|
|
344
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
345
|
+
ToolbarButton,
|
|
346
|
+
{
|
|
347
|
+
ariaLabel: pendingCtrl ? "Cancel pending Ctrl" : "Arm Ctrl modifier",
|
|
348
|
+
title: "Ctrl (sticky — taps the next key as Ctrl+key)",
|
|
349
|
+
onPointerDown: tap(onToggleCtrl),
|
|
350
|
+
active: pendingCtrl,
|
|
351
|
+
children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs font-medium", children: "Ctrl" })
|
|
352
|
+
}
|
|
353
|
+
),
|
|
354
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mx-1 h-6 w-px shrink-0 bg-border", "aria-hidden": "true" }),
|
|
355
|
+
idleArrows.map(({ Icon, seq, ariaLabel }) => /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
356
|
+
ToolbarButton,
|
|
357
|
+
{
|
|
358
|
+
ariaLabel,
|
|
359
|
+
title: ariaLabel,
|
|
360
|
+
onPointerDown: tap(() => sendInput(seq)),
|
|
361
|
+
children: /* @__PURE__ */ jsxRuntimeExports.jsx(Icon, { className: "size-4" })
|
|
362
|
+
},
|
|
363
|
+
ariaLabel
|
|
364
|
+
))
|
|
365
|
+
] }) })
|
|
366
|
+
}
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
function ToolbarButton({
|
|
370
|
+
ariaLabel,
|
|
371
|
+
title,
|
|
372
|
+
onPointerDown,
|
|
373
|
+
active,
|
|
374
|
+
variant = "default",
|
|
375
|
+
disabled,
|
|
376
|
+
children
|
|
377
|
+
}) {
|
|
378
|
+
const palette = active ? "bg-primary text-primary-foreground" : variant === "primary" ? "bg-primary text-primary-foreground hover:bg-primary/90" : "bg-muted/60 hover:bg-muted active:bg-muted";
|
|
379
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
380
|
+
"button",
|
|
381
|
+
{
|
|
382
|
+
type: "button",
|
|
383
|
+
"aria-label": ariaLabel,
|
|
384
|
+
"aria-pressed": active,
|
|
385
|
+
title,
|
|
386
|
+
onPointerDown,
|
|
387
|
+
disabled,
|
|
388
|
+
className: [
|
|
389
|
+
"inline-flex h-9 min-w-9 shrink-0 items-center justify-center gap-1 rounded-md px-2",
|
|
390
|
+
"text-foreground transition-colors",
|
|
391
|
+
palette,
|
|
392
|
+
"disabled:opacity-40"
|
|
393
|
+
].join(" "),
|
|
394
|
+
children
|
|
395
|
+
}
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
const LONG_PRESS_MS = 500;
|
|
399
|
+
const LONG_PRESS_SLOP_PX = 10;
|
|
400
|
+
const SEARCH_DECORATIONS = {
|
|
401
|
+
matchBackground: "#515c6a",
|
|
402
|
+
activeMatchBackground: "#a9913680",
|
|
403
|
+
matchOverviewRuler: "#a9913680",
|
|
404
|
+
activeMatchColorOverviewRuler: "#a99136"
|
|
405
|
+
};
|
|
406
|
+
const DEFAULT_SEARCH_OPTIONS = {
|
|
407
|
+
caseSensitive: false,
|
|
408
|
+
wholeWord: false,
|
|
409
|
+
regex: false
|
|
410
|
+
};
|
|
411
|
+
function toXtermSearchOptions(opts) {
|
|
412
|
+
return {
|
|
413
|
+
caseSensitive: opts.caseSensitive,
|
|
414
|
+
wholeWord: opts.wholeWord,
|
|
415
|
+
regex: opts.regex,
|
|
416
|
+
decorations: SEARCH_DECORATIONS
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
const DARK_TERMINAL_THEME = {
|
|
420
|
+
background: "#1e1e1e",
|
|
421
|
+
foreground: "#e8e8e8",
|
|
422
|
+
cursor: "#e8e8e8",
|
|
423
|
+
selectionBackground: "rgba(255, 255, 255, 0.2)"
|
|
424
|
+
};
|
|
425
|
+
const LIGHT_TERMINAL_THEME = {
|
|
426
|
+
background: "#ffffff",
|
|
427
|
+
foreground: "#1e1e1e",
|
|
428
|
+
cursor: "#1e1e1e",
|
|
429
|
+
cursorAccent: "#ffffff",
|
|
430
|
+
selectionBackground: "rgba(0, 0, 0, 0.15)",
|
|
431
|
+
// Tweak ANSI colors so they remain readable on a white background. The default
|
|
432
|
+
// bright-yellow / bright-green xterm palette washes out badly in light mode.
|
|
433
|
+
black: "#000000",
|
|
434
|
+
red: "#cd3131",
|
|
435
|
+
green: "#0a8043",
|
|
436
|
+
yellow: "#946800",
|
|
437
|
+
blue: "#0451a5",
|
|
438
|
+
magenta: "#bc05bc",
|
|
439
|
+
cyan: "#0598bc",
|
|
440
|
+
white: "#555555",
|
|
441
|
+
brightBlack: "#666666",
|
|
442
|
+
brightRed: "#cd3131",
|
|
443
|
+
brightGreen: "#0a8043",
|
|
444
|
+
brightYellow: "#946800",
|
|
445
|
+
brightBlue: "#0451a5",
|
|
446
|
+
brightMagenta: "#bc05bc",
|
|
447
|
+
brightCyan: "#0598bc",
|
|
448
|
+
brightWhite: "#1e1e1e"
|
|
449
|
+
};
|
|
450
|
+
function isDarkMode() {
|
|
451
|
+
return document.documentElement.classList.contains("dark");
|
|
452
|
+
}
|
|
453
|
+
function getTerminalTheme() {
|
|
454
|
+
return isDarkMode() ? DARK_TERMINAL_THEME : LIGHT_TERMINAL_THEME;
|
|
455
|
+
}
|
|
456
|
+
function TerminalPanel({
|
|
457
|
+
workspaceId,
|
|
458
|
+
terminalId,
|
|
459
|
+
visible,
|
|
460
|
+
paneMetadata,
|
|
461
|
+
autoFocus,
|
|
462
|
+
onTitleChange
|
|
463
|
+
}) {
|
|
464
|
+
const containerRef = reactExports.useRef(null);
|
|
465
|
+
const terminalRef = reactExports.useRef(null);
|
|
466
|
+
const fitAddonRef = reactExports.useRef(null);
|
|
467
|
+
const searchAddonRef = reactExports.useRef(null);
|
|
468
|
+
const webglAddonRef = reactExports.useRef(null);
|
|
469
|
+
const wsRef = reactExports.useRef(null);
|
|
470
|
+
const onTitleChangeRef = reactExports.useRef(onTitleChange);
|
|
471
|
+
onTitleChangeRef.current = onTitleChange;
|
|
472
|
+
const { settings } = useSettingsQuery();
|
|
473
|
+
const useWebGLRenderer = settings.useWebGLTerminalRenderer ?? true;
|
|
474
|
+
const useWebGLRendererRef = reactExports.useRef(useWebGLRenderer);
|
|
475
|
+
useWebGLRendererRef.current = useWebGLRenderer;
|
|
476
|
+
const [searchOpen, setSearchOpen] = reactExports.useState(false);
|
|
477
|
+
const [searchQuery, setSearchQuery] = reactExports.useState("");
|
|
478
|
+
const [searchOptions, setSearchOptions] = reactExports.useState(DEFAULT_SEARCH_OPTIONS);
|
|
479
|
+
const [matchInfo, setMatchInfo] = reactExports.useState({
|
|
480
|
+
total: 0,
|
|
481
|
+
current: 0
|
|
482
|
+
});
|
|
483
|
+
const searchBarRef = reactExports.useRef(null);
|
|
484
|
+
const openSearchRef = reactExports.useRef(() => {
|
|
485
|
+
});
|
|
486
|
+
const [terminalReady, setTerminalReady] = reactExports.useState(false);
|
|
487
|
+
const pendingCtrlRef = reactExports.useRef(false);
|
|
488
|
+
const [pendingCtrl, setPendingCtrl] = reactExports.useState(false);
|
|
489
|
+
const handleToggleCtrl = reactExports.useCallback(() => {
|
|
490
|
+
const next = !pendingCtrlRef.current;
|
|
491
|
+
pendingCtrlRef.current = next;
|
|
492
|
+
setPendingCtrl(next);
|
|
493
|
+
}, []);
|
|
494
|
+
const selectionAnchorRef = reactExports.useRef(null);
|
|
495
|
+
const selectionHeadRef = reactExports.useRef(null);
|
|
496
|
+
const [selectionMode, setSelectionMode] = reactExports.useState(false);
|
|
497
|
+
const exitSelectionMode = reactExports.useCallback(() => {
|
|
498
|
+
selectionAnchorRef.current = null;
|
|
499
|
+
selectionHeadRef.current = null;
|
|
500
|
+
setSelectionMode(false);
|
|
501
|
+
terminalRef.current?.clearSelection();
|
|
502
|
+
}, []);
|
|
503
|
+
const handleExtendSelection = reactExports.useCallback((direction) => {
|
|
504
|
+
const terminal = terminalRef.current;
|
|
505
|
+
const anchor = selectionAnchorRef.current;
|
|
506
|
+
const head = selectionHeadRef.current;
|
|
507
|
+
if (!terminal || !anchor || !head) return;
|
|
508
|
+
const next = moveCell(head, direction, terminal);
|
|
509
|
+
selectionHeadRef.current = next;
|
|
510
|
+
applySelection(terminal, anchor, next);
|
|
511
|
+
}, []);
|
|
512
|
+
const handleSelectAll = reactExports.useCallback(() => {
|
|
513
|
+
const terminal = terminalRef.current;
|
|
514
|
+
if (!terminal) return;
|
|
515
|
+
const anchor = { col: 0, row: 0 };
|
|
516
|
+
const lastRow = Math.max(0, terminal.buffer.active.length - 1);
|
|
517
|
+
const head = { col: Math.max(0, terminal.cols - 1), row: lastRow };
|
|
518
|
+
applySelection(terminal, anchor, head);
|
|
519
|
+
selectionAnchorRef.current = anchor;
|
|
520
|
+
selectionHeadRef.current = head;
|
|
521
|
+
setSelectionMode(true);
|
|
522
|
+
terminal.blur();
|
|
523
|
+
}, []);
|
|
524
|
+
reactExports.useEffect(() => {
|
|
525
|
+
if (!containerRef.current) return;
|
|
526
|
+
let cancelled = false;
|
|
527
|
+
let cleanup;
|
|
528
|
+
Promise.all([
|
|
529
|
+
import("./xterm-CkV9CBvP.js"),
|
|
530
|
+
import("./addon-fit-D89xfLfG.js"),
|
|
531
|
+
import("./addon-web-links-CzLDtFWc.js"),
|
|
532
|
+
import("./addon-search-BS53_-cp.js"),
|
|
533
|
+
import("./addon-webgl-BeO2q2vc.js")
|
|
534
|
+
]).then(([xtermMod, fitMod, webLinksMod, searchMod, webglMod]) => {
|
|
535
|
+
const { Terminal: XTerm } = xtermMod;
|
|
536
|
+
const { FitAddon: XFitAddon } = fitMod;
|
|
537
|
+
const { WebLinksAddon: XWebLinksAddon } = webLinksMod;
|
|
538
|
+
const { SearchAddon: XSearchAddon } = searchMod;
|
|
539
|
+
const { WebglAddon: XWebglAddon } = webglMod;
|
|
540
|
+
if (cancelled || !containerRef.current) return;
|
|
541
|
+
const wantsWebGL = useWebGLRendererRef.current;
|
|
542
|
+
Promise.resolve({ });
|
|
543
|
+
const terminal = new XTerm({
|
|
544
|
+
// Opt in to addon-only APIs (decorations, markers, etc.) that
|
|
545
|
+
// xterm.js 6.1-beta gates behind this flag. Without it, the search
|
|
546
|
+
// addon's `findNext` throws "You must set the allowProposedApi
|
|
547
|
+
// option to true to use proposed API" the first time the user
|
|
548
|
+
// types in the find bar. The addons we ship (search, webgl) are
|
|
549
|
+
// first-party xterm.js addons that depend on these APIs.
|
|
550
|
+
allowProposedApi: true,
|
|
551
|
+
cursorBlink: true,
|
|
552
|
+
fontSize: 13,
|
|
553
|
+
fontFamily: "'SF Mono', Menlo, Monaco, 'Courier New', monospace",
|
|
554
|
+
// iTerm-style row spacing — only safe with the WebGL renderer.
|
|
555
|
+
// The WebGL addon redraws box-drawing (U+2500-U+257F), block
|
|
556
|
+
// elements (U+2580-U+259F), powerline separators, and other
|
|
557
|
+
// continuous glyphs at the full cell rect (via its `customGlyphs`
|
|
558
|
+
// option, default true) so a 1.2 lineHeight doesn't slice horizontal
|
|
559
|
+
// gaps through the opencode banner, claude-code's powerline
|
|
560
|
+
// statusline, or other ASCII art. xterm.js's DOM renderer does
|
|
561
|
+
// NOT do this — falling back to it means we have to revert to
|
|
562
|
+
// lineHeight: 1.0 to keep block art continuous.
|
|
563
|
+
// See https://github.com/band-app/band/issues/391 for history.
|
|
564
|
+
lineHeight: wantsWebGL ? 1.2 : 1,
|
|
565
|
+
macOptionIsMeta: true,
|
|
566
|
+
// Alt+Left/Right → word navigation on macOS
|
|
567
|
+
scrollback: 1e4,
|
|
568
|
+
theme: getTerminalTheme()
|
|
569
|
+
});
|
|
570
|
+
const themeObserver = new MutationObserver(() => {
|
|
571
|
+
terminal.options.theme = getTerminalTheme();
|
|
572
|
+
});
|
|
573
|
+
themeObserver.observe(document.documentElement, {
|
|
574
|
+
attributes: true,
|
|
575
|
+
attributeFilter: ["class"]
|
|
576
|
+
});
|
|
577
|
+
const fitAddon = new XFitAddon();
|
|
578
|
+
terminal.loadAddon(fitAddon);
|
|
579
|
+
terminal.loadAddon(new XWebLinksAddon((_event, uri) => openExternalUrl(uri)));
|
|
580
|
+
terminal.open(containerRef.current);
|
|
581
|
+
let webglContextLossDisposable;
|
|
582
|
+
const attachWebGL = () => {
|
|
583
|
+
try {
|
|
584
|
+
const addon = new XWebglAddon({ customGlyphs: true });
|
|
585
|
+
terminal.loadAddon(addon);
|
|
586
|
+
const screenEl = containerRef.current?.querySelector(
|
|
587
|
+
".xterm-screen"
|
|
588
|
+
);
|
|
589
|
+
const webglCanvas = screenEl?.querySelector(
|
|
590
|
+
":scope > canvas:last-of-type"
|
|
591
|
+
);
|
|
592
|
+
if (webglCanvas) {
|
|
593
|
+
webglCanvas.style.pointerEvents = "none";
|
|
594
|
+
webglCanvas.style.touchAction = "none";
|
|
595
|
+
}
|
|
596
|
+
webglAddonRef.current = addon;
|
|
597
|
+
webglContextLossDisposable?.dispose();
|
|
598
|
+
webglContextLossDisposable = addon.onContextLoss(() => {
|
|
599
|
+
console.warn("[TerminalPanel] WebGL context lost, reattaching addon");
|
|
600
|
+
addon.dispose();
|
|
601
|
+
webglAddonRef.current = null;
|
|
602
|
+
attachWebGL();
|
|
603
|
+
});
|
|
604
|
+
return true;
|
|
605
|
+
} catch (err) {
|
|
606
|
+
console.warn("[TerminalPanel] WebGL renderer unavailable, falling back to DOM", err);
|
|
607
|
+
return false;
|
|
608
|
+
}
|
|
609
|
+
};
|
|
610
|
+
if (wantsWebGL) attachWebGL();
|
|
611
|
+
const searchAddon = new XSearchAddon();
|
|
612
|
+
terminal.loadAddon(searchAddon);
|
|
613
|
+
const searchResultsDisposable = searchAddon.onDidChangeResults((event) => {
|
|
614
|
+
setMatchInfo({
|
|
615
|
+
total: event.resultCount,
|
|
616
|
+
current: event.resultIndex >= 0 ? event.resultIndex + 1 : 0
|
|
617
|
+
});
|
|
618
|
+
});
|
|
619
|
+
terminal.attachCustomKeyEventHandler((e) => {
|
|
620
|
+
if (e.type === "keydown") {
|
|
621
|
+
if (pendingCtrlRef.current && e.key.length === 1 && !e.metaKey && !e.altKey && !e.ctrlKey) {
|
|
622
|
+
const lower = e.key.toLowerCase();
|
|
623
|
+
const code = lower.charCodeAt(0);
|
|
624
|
+
if (code >= 97 && code <= 122) {
|
|
625
|
+
terminal.input(String.fromCharCode(code - 96));
|
|
626
|
+
pendingCtrlRef.current = false;
|
|
627
|
+
setPendingCtrl(false);
|
|
628
|
+
e.preventDefault();
|
|
629
|
+
return false;
|
|
630
|
+
}
|
|
631
|
+
pendingCtrlRef.current = false;
|
|
632
|
+
setPendingCtrl(false);
|
|
633
|
+
}
|
|
634
|
+
if ((e.metaKey || e.ctrlKey) && !e.shiftKey && !e.altKey && e.key.toLowerCase() === "f") {
|
|
635
|
+
e.preventDefault();
|
|
636
|
+
openSearchRef.current();
|
|
637
|
+
return false;
|
|
638
|
+
}
|
|
639
|
+
if (e.key === "Enter" && e.shiftKey && !e.altKey && !e.metaKey && !e.ctrlKey) {
|
|
640
|
+
terminal.input("\x1B[13;2u");
|
|
641
|
+
return false;
|
|
642
|
+
}
|
|
643
|
+
if (e.altKey && !e.metaKey && !e.ctrlKey) {
|
|
644
|
+
if (e.key === "ArrowLeft") {
|
|
645
|
+
terminal.input("\x1Bb");
|
|
646
|
+
return false;
|
|
647
|
+
}
|
|
648
|
+
if (e.key === "ArrowRight") {
|
|
649
|
+
terminal.input("\x1Bf");
|
|
650
|
+
return false;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
return true;
|
|
655
|
+
});
|
|
656
|
+
terminalRef.current = terminal;
|
|
657
|
+
fitAddonRef.current = fitAddon;
|
|
658
|
+
searchAddonRef.current = searchAddon;
|
|
659
|
+
setTerminalReady(true);
|
|
660
|
+
const containerEl = containerRef.current;
|
|
661
|
+
let lastTouchY = null;
|
|
662
|
+
const onTouchStart = (e) => {
|
|
663
|
+
lastTouchY = e.touches.length === 1 ? e.touches[0].clientY : null;
|
|
664
|
+
};
|
|
665
|
+
const onTouchMove = (e) => {
|
|
666
|
+
if (e.touches.length !== 1 || lastTouchY === null) return;
|
|
667
|
+
const currentY = e.touches[0].clientY;
|
|
668
|
+
const deltaY = lastTouchY - currentY;
|
|
669
|
+
const viewport = containerEl.querySelector(".xterm-viewport");
|
|
670
|
+
const cellHeight = viewport && terminal.rows > 0 ? viewport.clientHeight / terminal.rows : 17;
|
|
671
|
+
const lineDelta = Math.trunc(deltaY / cellHeight);
|
|
672
|
+
if (lineDelta !== 0) {
|
|
673
|
+
terminal.scrollLines(lineDelta);
|
|
674
|
+
lastTouchY = currentY + (deltaY - lineDelta * cellHeight);
|
|
675
|
+
e.preventDefault();
|
|
676
|
+
}
|
|
677
|
+
};
|
|
678
|
+
const onTouchEnd = () => {
|
|
679
|
+
lastTouchY = null;
|
|
680
|
+
};
|
|
681
|
+
containerEl.addEventListener("touchstart", onTouchStart, { passive: true });
|
|
682
|
+
containerEl.addEventListener("touchmove", onTouchMove, { passive: false });
|
|
683
|
+
containerEl.addEventListener("touchend", onTouchEnd, { passive: true });
|
|
684
|
+
containerEl.addEventListener("touchcancel", onTouchEnd, { passive: true });
|
|
685
|
+
let longPressTimer = null;
|
|
686
|
+
let longPressStart = null;
|
|
687
|
+
const cancelLongPress = () => {
|
|
688
|
+
if (longPressTimer !== null) {
|
|
689
|
+
window.clearTimeout(longPressTimer);
|
|
690
|
+
longPressTimer = null;
|
|
691
|
+
}
|
|
692
|
+
longPressStart = null;
|
|
693
|
+
};
|
|
694
|
+
const onLongPressStart = (e) => {
|
|
695
|
+
cancelLongPress();
|
|
696
|
+
if (e.touches.length !== 1) return;
|
|
697
|
+
const t = e.touches[0];
|
|
698
|
+
longPressStart = { x: t.clientX, y: t.clientY };
|
|
699
|
+
longPressTimer = window.setTimeout(() => {
|
|
700
|
+
longPressTimer = null;
|
|
701
|
+
const start = longPressStart;
|
|
702
|
+
if (!start) return;
|
|
703
|
+
const screenEl = containerEl.querySelector(".xterm-screen");
|
|
704
|
+
if (!screenEl) return;
|
|
705
|
+
const cell = pointToCell(start.x, start.y, terminal, screenEl);
|
|
706
|
+
const lineText = getLineText(terminal, cell.row);
|
|
707
|
+
const { anchor, head } = wordSelectionAt(cell, lineText);
|
|
708
|
+
applySelection(terminal, anchor, head);
|
|
709
|
+
selectionAnchorRef.current = anchor;
|
|
710
|
+
selectionHeadRef.current = head;
|
|
711
|
+
setSelectionMode(true);
|
|
712
|
+
tapStartX = null;
|
|
713
|
+
tapStartY = null;
|
|
714
|
+
terminal.blur();
|
|
715
|
+
if (typeof navigator.vibrate === "function") {
|
|
716
|
+
try {
|
|
717
|
+
navigator.vibrate(15);
|
|
718
|
+
} catch {
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
}, LONG_PRESS_MS);
|
|
722
|
+
};
|
|
723
|
+
const onLongPressMove = (e) => {
|
|
724
|
+
if (!longPressStart || e.touches.length !== 1) return;
|
|
725
|
+
const t = e.touches[0];
|
|
726
|
+
const dx = Math.abs(t.clientX - longPressStart.x);
|
|
727
|
+
const dy = Math.abs(t.clientY - longPressStart.y);
|
|
728
|
+
if (dx > LONG_PRESS_SLOP_PX || dy > LONG_PRESS_SLOP_PX) cancelLongPress();
|
|
729
|
+
};
|
|
730
|
+
containerEl.addEventListener("touchstart", onLongPressStart, { passive: true });
|
|
731
|
+
containerEl.addEventListener("touchmove", onLongPressMove, { passive: true });
|
|
732
|
+
containerEl.addEventListener("touchend", cancelLongPress, { passive: true });
|
|
733
|
+
containerEl.addEventListener("touchcancel", cancelLongPress, { passive: true });
|
|
734
|
+
let tapStartX = null;
|
|
735
|
+
let tapStartY = null;
|
|
736
|
+
const onTapStart = (e) => {
|
|
737
|
+
if (e.touches.length === 1) {
|
|
738
|
+
tapStartX = e.touches[0].clientX;
|
|
739
|
+
tapStartY = e.touches[0].clientY;
|
|
740
|
+
} else {
|
|
741
|
+
tapStartX = null;
|
|
742
|
+
tapStartY = null;
|
|
743
|
+
}
|
|
744
|
+
};
|
|
745
|
+
const onTapEnd = (e) => {
|
|
746
|
+
const startX = tapStartX;
|
|
747
|
+
const startY = tapStartY;
|
|
748
|
+
tapStartX = null;
|
|
749
|
+
tapStartY = null;
|
|
750
|
+
if (startX === null || startY === null || e.changedTouches.length !== 1) return;
|
|
751
|
+
const dx = Math.abs(e.changedTouches[0].clientX - startX);
|
|
752
|
+
const dy = Math.abs(e.changedTouches[0].clientY - startY);
|
|
753
|
+
if (dx < 10 && dy < 10) {
|
|
754
|
+
if (selectionAnchorRef.current !== null) {
|
|
755
|
+
selectionAnchorRef.current = null;
|
|
756
|
+
selectionHeadRef.current = null;
|
|
757
|
+
setSelectionMode(false);
|
|
758
|
+
terminal.clearSelection();
|
|
759
|
+
}
|
|
760
|
+
terminal.focus();
|
|
761
|
+
}
|
|
762
|
+
};
|
|
763
|
+
const onTapCancel = () => {
|
|
764
|
+
tapStartX = null;
|
|
765
|
+
tapStartY = null;
|
|
766
|
+
};
|
|
767
|
+
containerEl.addEventListener("touchstart", onTapStart, { passive: true });
|
|
768
|
+
containerEl.addEventListener("touchend", onTapEnd, { passive: true });
|
|
769
|
+
containerEl.addEventListener("touchcancel", onTapCancel, { passive: true });
|
|
770
|
+
const proto = location.protocol === "https:" ? "wss:" : "ws:";
|
|
771
|
+
const ws = new WebSocket(
|
|
772
|
+
`${proto}//${location.host}/terminal?workspaceId=${encodeURIComponent(workspaceId)}&terminalId=${encodeURIComponent(terminalId)}`
|
|
773
|
+
);
|
|
774
|
+
wsRef.current = ws;
|
|
775
|
+
ws.binaryType = "arraybuffer";
|
|
776
|
+
ws.onopen = () => {
|
|
777
|
+
if (paneMetadata && (paneMetadata.command || paneMetadata.cwd || paneMetadata.env)) {
|
|
778
|
+
const initMsg = { type: "init" };
|
|
779
|
+
if (paneMetadata.command) initMsg.command = paneMetadata.command;
|
|
780
|
+
if (paneMetadata.cwd) initMsg.cwd = paneMetadata.cwd;
|
|
781
|
+
if (paneMetadata.env) initMsg.env = paneMetadata.env;
|
|
782
|
+
ws.send(JSON.stringify(initMsg));
|
|
783
|
+
}
|
|
784
|
+
fitAddon.fit();
|
|
785
|
+
ws.send(
|
|
786
|
+
JSON.stringify({
|
|
787
|
+
type: "resize",
|
|
788
|
+
cols: terminal.cols,
|
|
789
|
+
rows: terminal.rows
|
|
790
|
+
})
|
|
791
|
+
);
|
|
792
|
+
if (autoFocus) {
|
|
793
|
+
terminal.focus();
|
|
794
|
+
}
|
|
795
|
+
};
|
|
796
|
+
ws.onmessage = (event) => {
|
|
797
|
+
if (event.data instanceof ArrayBuffer) {
|
|
798
|
+
terminal.write(new Uint8Array(event.data));
|
|
799
|
+
} else {
|
|
800
|
+
try {
|
|
801
|
+
const msg = JSON.parse(event.data);
|
|
802
|
+
if (msg.type === "title" && typeof msg.title === "string") {
|
|
803
|
+
onTitleChangeRef.current?.(msg.title);
|
|
804
|
+
}
|
|
805
|
+
} catch {
|
|
806
|
+
terminal.write(event.data);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
};
|
|
810
|
+
ws.onclose = () => {
|
|
811
|
+
terminal.write("\r\n\x1B[90m[Terminal disconnected]\x1B[0m\r\n");
|
|
812
|
+
};
|
|
813
|
+
terminal.onData((data) => {
|
|
814
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
815
|
+
ws.send(data);
|
|
816
|
+
}
|
|
817
|
+
});
|
|
818
|
+
terminal.onTitleChange((title) => {
|
|
819
|
+
onTitleChangeRef.current?.(title);
|
|
820
|
+
});
|
|
821
|
+
let selectionRafId = null;
|
|
822
|
+
const reapplySelectionOnNextFrame = () => {
|
|
823
|
+
if (selectionRafId !== null) return;
|
|
824
|
+
if (!selectionAnchorRef.current || !selectionHeadRef.current) return;
|
|
825
|
+
selectionRafId = requestAnimationFrame(() => {
|
|
826
|
+
selectionRafId = null;
|
|
827
|
+
const anchor = selectionAnchorRef.current;
|
|
828
|
+
const head = selectionHeadRef.current;
|
|
829
|
+
if (anchor && head) applySelection(terminal, anchor, head);
|
|
830
|
+
});
|
|
831
|
+
};
|
|
832
|
+
const selectionResizeDisposable = terminal.onResize(reapplySelectionOnNextFrame);
|
|
833
|
+
let lastDpr = window.devicePixelRatio;
|
|
834
|
+
const handleDprChange = () => {
|
|
835
|
+
const currentDpr = window.devicePixelRatio;
|
|
836
|
+
if (currentDpr === lastDpr) return;
|
|
837
|
+
lastDpr = currentDpr;
|
|
838
|
+
const fs = terminal.options.fontSize;
|
|
839
|
+
if (typeof fs === "number") {
|
|
840
|
+
terminal.options.fontSize = fs + 1;
|
|
841
|
+
terminal.options.fontSize = fs;
|
|
842
|
+
}
|
|
843
|
+
const addon = webglAddonRef.current;
|
|
844
|
+
if (addon) {
|
|
845
|
+
addon.dispose();
|
|
846
|
+
webglAddonRef.current = null;
|
|
847
|
+
attachWebGL();
|
|
848
|
+
}
|
|
849
|
+
fitAddon.fit();
|
|
850
|
+
};
|
|
851
|
+
const resizeObserver = new ResizeObserver((entries) => {
|
|
852
|
+
const entry = entries[0];
|
|
853
|
+
if (!entry || entry.contentRect.width === 0 || entry.contentRect.height === 0) return;
|
|
854
|
+
handleDprChange();
|
|
855
|
+
fitAddon.fit();
|
|
856
|
+
if (ws.readyState === WebSocket.OPEN && terminal.cols > 0 && terminal.rows > 0) {
|
|
857
|
+
ws.send(
|
|
858
|
+
JSON.stringify({
|
|
859
|
+
type: "resize",
|
|
860
|
+
cols: terminal.cols,
|
|
861
|
+
rows: terminal.rows
|
|
862
|
+
})
|
|
863
|
+
);
|
|
864
|
+
}
|
|
865
|
+
});
|
|
866
|
+
resizeObserver.observe(containerRef.current);
|
|
867
|
+
let dprMql;
|
|
868
|
+
const bindDprListener = () => {
|
|
869
|
+
dprMql = window.matchMedia(`(resolution: ${window.devicePixelRatio}dppx)`);
|
|
870
|
+
dprMql.addEventListener("change", onDprMediaChange);
|
|
871
|
+
};
|
|
872
|
+
const onDprMediaChange = () => {
|
|
873
|
+
handleDprChange();
|
|
874
|
+
dprMql?.removeEventListener("change", onDprMediaChange);
|
|
875
|
+
bindDprListener();
|
|
876
|
+
};
|
|
877
|
+
bindDprListener();
|
|
878
|
+
cleanup = () => {
|
|
879
|
+
themeObserver.disconnect();
|
|
880
|
+
resizeObserver.disconnect();
|
|
881
|
+
searchResultsDisposable.dispose();
|
|
882
|
+
selectionResizeDisposable.dispose();
|
|
883
|
+
if (selectionRafId !== null) cancelAnimationFrame(selectionRafId);
|
|
884
|
+
webglContextLossDisposable?.dispose();
|
|
885
|
+
dprMql?.removeEventListener("change", onDprMediaChange);
|
|
886
|
+
cancelLongPress();
|
|
887
|
+
containerEl.removeEventListener("touchstart", onTouchStart);
|
|
888
|
+
containerEl.removeEventListener("touchmove", onTouchMove);
|
|
889
|
+
containerEl.removeEventListener("touchend", onTouchEnd);
|
|
890
|
+
containerEl.removeEventListener("touchcancel", onTouchEnd);
|
|
891
|
+
containerEl.removeEventListener("touchstart", onLongPressStart);
|
|
892
|
+
containerEl.removeEventListener("touchmove", onLongPressMove);
|
|
893
|
+
containerEl.removeEventListener("touchend", cancelLongPress);
|
|
894
|
+
containerEl.removeEventListener("touchcancel", cancelLongPress);
|
|
895
|
+
containerEl.removeEventListener("touchstart", onTapStart);
|
|
896
|
+
containerEl.removeEventListener("touchend", onTapEnd);
|
|
897
|
+
containerEl.removeEventListener("touchcancel", onTapCancel);
|
|
898
|
+
ws.close();
|
|
899
|
+
terminal.dispose();
|
|
900
|
+
terminalRef.current = null;
|
|
901
|
+
fitAddonRef.current = null;
|
|
902
|
+
searchAddonRef.current = null;
|
|
903
|
+
webglAddonRef.current = null;
|
|
904
|
+
wsRef.current = null;
|
|
905
|
+
selectionAnchorRef.current = null;
|
|
906
|
+
selectionHeadRef.current = null;
|
|
907
|
+
pendingCtrlRef.current = false;
|
|
908
|
+
setSelectionMode(false);
|
|
909
|
+
setPendingCtrl(false);
|
|
910
|
+
setTerminalReady(false);
|
|
911
|
+
};
|
|
912
|
+
});
|
|
913
|
+
return () => {
|
|
914
|
+
cancelled = true;
|
|
915
|
+
cleanup?.();
|
|
916
|
+
};
|
|
917
|
+
}, [terminalId, workspaceId, paneMetadata, autoFocus]);
|
|
918
|
+
reactExports.useEffect(() => {
|
|
919
|
+
if (visible && fitAddonRef.current) {
|
|
920
|
+
requestAnimationFrame(() => {
|
|
921
|
+
fitAddonRef.current?.fit();
|
|
922
|
+
const term = terminalRef.current;
|
|
923
|
+
const ws = wsRef.current;
|
|
924
|
+
if (term && ws?.readyState === WebSocket.OPEN && term.cols > 0 && term.rows > 0) {
|
|
925
|
+
ws.send(JSON.stringify({ type: "resize", cols: term.cols, rows: term.rows }));
|
|
926
|
+
}
|
|
927
|
+
});
|
|
928
|
+
}
|
|
929
|
+
}, [visible]);
|
|
930
|
+
reactExports.useEffect(() => {
|
|
931
|
+
const handler = () => {
|
|
932
|
+
if (!visible) return;
|
|
933
|
+
terminalRef.current?.focus();
|
|
934
|
+
};
|
|
935
|
+
window.addEventListener("band:focus-terminal", handler);
|
|
936
|
+
return () => window.removeEventListener("band:focus-terminal", handler);
|
|
937
|
+
}, [visible]);
|
|
938
|
+
const handleOpenSearch = reactExports.useCallback(() => {
|
|
939
|
+
setSearchOpen(true);
|
|
940
|
+
requestAnimationFrame(() => {
|
|
941
|
+
searchBarRef.current?.focus();
|
|
942
|
+
searchBarRef.current?.select();
|
|
943
|
+
});
|
|
944
|
+
}, []);
|
|
945
|
+
openSearchRef.current = handleOpenSearch;
|
|
946
|
+
const handleCloseSearch = reactExports.useCallback(() => {
|
|
947
|
+
searchAddonRef.current?.clearDecorations();
|
|
948
|
+
setSearchOpen(false);
|
|
949
|
+
setSearchQuery("");
|
|
950
|
+
setMatchInfo({ total: 0, current: 0 });
|
|
951
|
+
terminalRef.current?.focus();
|
|
952
|
+
}, []);
|
|
953
|
+
const handleNext = reactExports.useCallback(() => {
|
|
954
|
+
if (!searchQuery) return;
|
|
955
|
+
searchAddonRef.current?.findNext(searchQuery, toXtermSearchOptions(searchOptions));
|
|
956
|
+
}, [searchQuery, searchOptions]);
|
|
957
|
+
const handlePrevious = reactExports.useCallback(() => {
|
|
958
|
+
if (!searchQuery) return;
|
|
959
|
+
searchAddonRef.current?.findPrevious(searchQuery, toXtermSearchOptions(searchOptions));
|
|
960
|
+
}, [searchQuery, searchOptions]);
|
|
961
|
+
reactExports.useEffect(() => {
|
|
962
|
+
const addon = searchAddonRef.current;
|
|
963
|
+
if (!addon) return;
|
|
964
|
+
if (!searchQuery) {
|
|
965
|
+
addon.clearDecorations();
|
|
966
|
+
setMatchInfo({ total: 0, current: 0 });
|
|
967
|
+
return;
|
|
968
|
+
}
|
|
969
|
+
addon.findNext(searchQuery, toXtermSearchOptions(searchOptions));
|
|
970
|
+
}, [searchQuery, searchOptions]);
|
|
971
|
+
const { contentBottomInset } = useVirtualKeyboardToolbar();
|
|
972
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative flex h-full w-full flex-col", children: [
|
|
973
|
+
searchOpen && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
974
|
+
SearchBar,
|
|
975
|
+
{
|
|
976
|
+
ref: searchBarRef,
|
|
977
|
+
query: searchQuery,
|
|
978
|
+
onQueryChange: setSearchQuery,
|
|
979
|
+
options: searchOptions,
|
|
980
|
+
onOptionsChange: setSearchOptions,
|
|
981
|
+
placeholder: "Find in terminal...",
|
|
982
|
+
matchInfo,
|
|
983
|
+
onNext: handleNext,
|
|
984
|
+
onPrevious: handlePrevious,
|
|
985
|
+
onClose: handleCloseSearch
|
|
986
|
+
}
|
|
987
|
+
),
|
|
988
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "relative min-h-0 flex-1", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
989
|
+
"div",
|
|
990
|
+
{
|
|
991
|
+
ref: containerRef,
|
|
992
|
+
className: "absolute inset-x-2 top-2 overflow-hidden",
|
|
993
|
+
style: { bottom: 8 + contentBottomInset }
|
|
994
|
+
}
|
|
995
|
+
) }),
|
|
996
|
+
terminalReady && terminalRef.current && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
997
|
+
TerminalToolbar,
|
|
998
|
+
{
|
|
999
|
+
terminal: terminalRef.current,
|
|
1000
|
+
sendInput: (data) => {
|
|
1001
|
+
const ws = wsRef.current;
|
|
1002
|
+
if (ws?.readyState === WebSocket.OPEN) ws.send(data);
|
|
1003
|
+
},
|
|
1004
|
+
pendingCtrl,
|
|
1005
|
+
onToggleCtrl: handleToggleCtrl,
|
|
1006
|
+
selectionMode,
|
|
1007
|
+
onExtendSelection: handleExtendSelection,
|
|
1008
|
+
onExitSelection: exitSelectionMode,
|
|
1009
|
+
onSelectAll: handleSelectAll
|
|
1010
|
+
}
|
|
1011
|
+
)
|
|
1012
|
+
] });
|
|
1013
|
+
}
|
|
1014
|
+
export {
|
|
1015
|
+
TerminalPanel
|
|
1016
|
+
};
|