@hienlh/ppm 0.23.5 → 0.23.7
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/CHANGELOG.md +89 -0
- package/CLAUDE.md +21 -1
- package/Dockerfile.test +8 -1
- package/assets/skills/ppm/SKILL.md +1 -1
- package/assets/skills/ppm/references/cli-reference.md +4 -4
- package/assets/skills/ppm/references/http-api.md +30 -1
- package/dist/web/assets/accounts-settings-section-BYWnY9hH.js +1 -0
- package/dist/web/assets/accounts-settings-section-BYWnY9hH.js.br +0 -0
- package/dist/web/assets/accounts-settings-section-BYWnY9hH.js.gz +0 -0
- package/dist/web/assets/{ai-resource-editor-DKcsVDle.js → ai-resource-editor-CY4n_Ccv.js} +1 -1
- package/dist/web/assets/ai-resource-editor-CY4n_Ccv.js.br +0 -0
- package/dist/web/assets/ai-resource-editor-CY4n_Ccv.js.gz +0 -0
- package/dist/web/assets/ai-settings-section-Bs3iUW4j.js +1 -0
- package/dist/web/assets/ai-settings-section-Bs3iUW4j.js.br +0 -0
- package/dist/web/assets/ai-settings-section-Bs3iUW4j.js.gz +0 -0
- package/dist/web/assets/ai-settings-section-DDs11XV4.js +1 -0
- package/dist/web/assets/api-client-CJPnCOG-.js +1 -0
- package/dist/web/assets/{api-client-c8YOpTlT.js → api-client-D-SMbdiH.js} +1 -1
- package/dist/web/assets/api-client-D-SMbdiH.js.br +0 -0
- package/dist/web/assets/api-client-D-SMbdiH.js.gz +0 -0
- package/dist/web/assets/api-settings-BDkkhFMq.js +1 -0
- package/dist/web/assets/{api-settings-BmvNoCbn.js → api-settings-Bv9mLCty.js} +1 -1
- package/dist/web/assets/api-settings-Bv9mLCty.js.br +0 -0
- package/dist/web/assets/api-settings-Bv9mLCty.js.gz +0 -0
- package/dist/web/assets/{api-themes-6fOQJgM9.js → api-themes-3wW-zLzU.js} +1 -1
- package/dist/web/assets/appearance-settings-section-C0BoIBmz.js +1 -0
- package/dist/web/assets/appearance-settings-section-C0BoIBmz.js.br +0 -0
- package/dist/web/assets/appearance-settings-section-C0BoIBmz.js.gz +0 -0
- package/dist/web/assets/apps-panel-DXAFsveZ.js +1 -0
- package/dist/web/assets/apps-panel-DXAFsveZ.js.br +0 -0
- package/dist/web/assets/apps-panel-DXAFsveZ.js.gz +0 -0
- package/dist/web/assets/{audio-preview-vaG2cB-k.js → audio-preview-BfES4Cx5.js} +1 -1
- package/dist/web/assets/audio-preview-BfES4Cx5.js.br +0 -0
- package/dist/web/assets/audio-preview-BfES4Cx5.js.gz +0 -0
- package/dist/web/assets/branch-review-tab-CA8hPSX9.js +1 -0
- package/dist/web/assets/branch-review-tab-CA8hPSX9.js.br +0 -0
- package/dist/web/assets/branch-review-tab-CA8hPSX9.js.gz +0 -0
- package/dist/web/assets/{button-Ci9EUgqd.js → button-DeAyLH9U.js} +1 -1
- package/dist/web/assets/button-DeAyLH9U.js.br +0 -0
- package/dist/web/assets/button-DeAyLH9U.js.gz +0 -0
- package/dist/web/assets/{chat-preparation-cache-BSIQGiWx.js → chat-preparation-cache-CcCd2g7A.js} +1 -1
- package/dist/web/assets/chat-preparation-cache-CcCd2g7A.js.br +0 -0
- package/dist/web/assets/chat-preparation-cache-CcCd2g7A.js.gz +0 -0
- package/dist/web/assets/chat-tab-BWi_bUzA.js +1 -0
- package/dist/web/assets/chat-tab-BWi_bUzA.js.br +0 -0
- package/dist/web/assets/chat-tab-BWi_bUzA.js.gz +0 -0
- package/dist/web/assets/chat-tab-BvJhIaVE.js +15 -0
- package/dist/web/assets/chat-tab-BvJhIaVE.js.br +0 -0
- package/dist/web/assets/chat-tab-BvJhIaVE.js.gz +0 -0
- package/dist/web/assets/code-editor-OJUdLVnn.js +7 -0
- package/dist/web/assets/code-editor-OJUdLVnn.js.br +0 -0
- package/dist/web/assets/code-editor-OJUdLVnn.js.gz +0 -0
- package/dist/web/assets/codex-reset-credit-button-Dgxy_VNp.js +1 -0
- package/dist/web/assets/codex-reset-credit-button-Dgxy_VNp.js.br +0 -0
- package/dist/web/assets/codex-reset-credit-button-Dgxy_VNp.js.gz +0 -0
- package/dist/web/assets/{conflict-editor-qMGJH0Bx.js → conflict-editor-BpKW_vbL.js} +1 -1
- package/dist/web/assets/conflict-editor-BpKW_vbL.js.br +0 -0
- package/dist/web/assets/conflict-editor-BpKW_vbL.js.gz +0 -0
- package/dist/web/assets/context-menu-7OX80HDB.js +1 -0
- package/dist/web/assets/context-menu-7OX80HDB.js.br +0 -0
- package/dist/web/assets/context-menu-7OX80HDB.js.gz +0 -0
- package/dist/web/assets/{csv-preview-D2I742dl.js → csv-preview-COYdTABa.js} +1 -1
- package/dist/web/assets/csv-preview-COYdTABa.js.br +0 -0
- package/dist/web/assets/csv-preview-COYdTABa.js.gz +0 -0
- package/dist/web/assets/{data-grid-overlay-editor-CQRVKr7y.js → data-grid-overlay-editor-CeZoduke.js} +1 -1
- package/dist/web/assets/data-grid-overlay-editor-CeZoduke.js.br +0 -0
- package/dist/web/assets/data-grid-overlay-editor-CeZoduke.js.gz +0 -0
- package/dist/web/assets/{database-viewer-Bz4LjgIa.js → database-viewer-BQcBqCKm.js} +1 -1
- package/dist/web/assets/database-viewer-BQcBqCKm.js.br +0 -0
- package/dist/web/assets/database-viewer-BQcBqCKm.js.gz +0 -0
- package/dist/web/assets/{design-export-client-ChMcMoqW.js → design-export-client-OrnGCSwu.js} +1 -1
- package/dist/web/assets/design-export-client-OrnGCSwu.js.br +0 -0
- package/dist/web/assets/design-export-client-OrnGCSwu.js.gz +0 -0
- package/dist/web/assets/{design-responsive-dialog-DNBhJJLf.js → design-responsive-dialog-vJjQrC4n.js} +1 -1
- package/dist/web/assets/design-responsive-dialog-vJjQrC4n.js.br +0 -0
- package/dist/web/assets/design-responsive-dialog-vJjQrC4n.js.gz +0 -0
- package/dist/web/assets/design-tab-BbmFk38H.js +33 -0
- package/dist/web/assets/design-tab-BbmFk38H.js.br +0 -0
- package/dist/web/assets/design-tab-BbmFk38H.js.gz +0 -0
- package/dist/web/assets/{designs-sidebar-panel-CapP1yXq.js → designs-sidebar-panel-D2kXwB8B.js} +1 -1
- package/dist/web/assets/designs-sidebar-panel-D2kXwB8B.js.br +0 -0
- package/dist/web/assets/designs-sidebar-panel-D2kXwB8B.js.gz +0 -0
- package/dist/web/assets/dialog-Bocc0Fgb.js +1 -0
- package/dist/web/assets/dialog-Bocc0Fgb.js.br +0 -0
- package/dist/web/assets/dialog-Bocc0Fgb.js.gz +0 -0
- package/dist/web/assets/{diff-viewer-CdXi7cZN.js → diff-viewer-Bj4DT6Mj.js} +1 -1
- package/dist/web/assets/diff-viewer-Bj4DT6Mj.js.br +0 -0
- package/dist/web/assets/diff-viewer-Bj4DT6Mj.js.gz +0 -0
- package/dist/web/assets/{docx-preview-C9XdKEj1.js → docx-preview-C2QqNGRE.js} +1 -1
- package/dist/web/assets/docx-preview-C2QqNGRE.js.br +0 -0
- package/dist/web/assets/docx-preview-C2QqNGRE.js.gz +0 -0
- package/dist/web/assets/editor-language-service-COqB2tuv.js +1 -0
- package/dist/web/assets/editor-language-service-COqB2tuv.js.br +3 -0
- package/dist/web/assets/editor-language-service-COqB2tuv.js.gz +0 -0
- package/dist/web/assets/explorer-body-DGrYajDN.js +2 -0
- package/dist/web/assets/explorer-body-DGrYajDN.js.br +0 -0
- package/dist/web/assets/explorer-body-DGrYajDN.js.gz +0 -0
- package/dist/web/assets/explorer-window-content-DLzkODTP.js +1 -0
- package/dist/web/assets/explorer-window-content-DLzkODTP.js.br +0 -0
- package/dist/web/assets/explorer-window-content-DLzkODTP.js.gz +0 -0
- package/dist/web/assets/{extension-manager-section-B2nZzJ2b.js → extension-manager-section-Bu0qcB2i.js} +1 -1
- package/dist/web/assets/extension-manager-section-Bu0qcB2i.js.br +0 -0
- package/dist/web/assets/extension-manager-section-Bu0qcB2i.js.gz +0 -0
- package/dist/web/assets/{extension-webview-BGoDxkdF.js → extension-webview-G_WJ0N52.js} +1 -1
- package/dist/web/assets/extension-webview-G_WJ0N52.js.br +0 -0
- package/dist/web/assets/extension-webview-G_WJ0N52.js.gz +0 -0
- package/dist/web/assets/{file-icons-gx_FBOXY.js → file-icons-l7fNOmdS.js} +1 -1
- package/dist/web/assets/file-icons-l7fNOmdS.js.br +0 -0
- package/dist/web/assets/file-icons-l7fNOmdS.js.gz +0 -0
- package/dist/web/assets/{file-store-D9KE0kQC.js → file-store-B0e7sLag.js} +1 -1
- package/dist/web/assets/file-store-B0e7sLag.js.br +0 -0
- package/dist/web/assets/file-store-B0e7sLag.js.gz +0 -0
- package/dist/web/assets/{files-settings-section-DC3OzzOl.js → files-settings-section-ChZ_CynX.js} +1 -1
- package/dist/web/assets/files-settings-section-ChZ_CynX.js.br +0 -0
- package/dist/web/assets/files-settings-section-ChZ_CynX.js.gz +0 -0
- package/dist/web/assets/format-bytes-__396eCN.js +1 -0
- package/dist/web/assets/{format-file-meta-mQwA6SuY.js → format-file-meta-o1XBuoKS.js} +1 -1
- package/dist/web/assets/format-file-meta-o1XBuoKS.js.br +0 -0
- package/dist/web/assets/format-file-meta-o1XBuoKS.js.gz +0 -0
- package/dist/web/assets/{fs-api-DL36slbd.js → fs-api-CeHKwx95.js} +1 -1
- package/dist/web/assets/fs-api-CeHKwx95.js.br +0 -0
- package/dist/web/assets/fs-api-CeHKwx95.js.gz +0 -0
- package/dist/web/assets/{general-settings-section-i41W43ER.js → general-settings-section-MSvDEbFa.js} +1 -1
- package/dist/web/assets/general-settings-section-MSvDEbFa.js.br +0 -0
- package/dist/web/assets/general-settings-section-MSvDEbFa.js.gz +0 -0
- package/dist/web/assets/{git-log-panel-DHzPIkJg.js → git-log-panel-gw5_a6j3.js} +1 -1
- package/dist/web/assets/git-log-panel-gw5_a6j3.js.br +0 -0
- package/dist/web/assets/git-log-panel-gw5_a6j3.js.gz +0 -0
- package/dist/web/assets/{git-repo-store-DSypNj19.js → git-repo-store-BpPnah5l.js} +1 -1
- package/dist/web/assets/git-repo-store-BpPnah5l.js.br +0 -0
- package/dist/web/assets/git-repo-store-BpPnah5l.js.gz +0 -0
- package/dist/web/assets/{glide-data-grid-Dj4vrDvj.js → glide-data-grid-Db2Cl26B.js} +5 -5
- package/dist/web/assets/glide-data-grid-Db2Cl26B.js.br +0 -0
- package/dist/web/assets/glide-data-grid-Db2Cl26B.js.gz +0 -0
- package/dist/web/assets/group-chat-tab-B4etmYTR.js +1 -0
- package/dist/web/assets/group-chat-tab-B4etmYTR.js.br +0 -0
- package/dist/web/assets/group-chat-tab-B4etmYTR.js.gz +0 -0
- package/dist/web/assets/{image-preview-CGdZrmLo.js → image-preview-CjNhpfWw.js} +1 -1
- package/dist/web/assets/index-CtAvmybi.js +52 -0
- package/dist/web/assets/index-CtAvmybi.js.br +0 -0
- package/dist/web/assets/index-CtAvmybi.js.gz +0 -0
- package/dist/web/assets/index-DeCJasyy.css +1 -0
- package/dist/web/assets/index-DeCJasyy.css.br +0 -0
- package/dist/web/assets/index-DeCJasyy.css.gz +0 -0
- package/dist/web/assets/{jira-watcher-section-BJ9edGEP.js → jira-watcher-section-CKSI1Alo.js} +1 -1
- package/dist/web/assets/keybindings-store-C4_yyU4u.js +1 -0
- package/dist/web/assets/{keyboard-shortcuts-section-CxdGSNGr.js → keyboard-shortcuts-section-CEaVUc9a.js} +1 -1
- package/dist/web/assets/keyboard-shortcuts-section-CEaVUc9a.js.br +0 -0
- package/dist/web/assets/keyboard-shortcuts-section-CEaVUc9a.js.gz +0 -0
- package/dist/web/assets/language-servers-section-ePtEpnDc.js +2 -0
- package/dist/web/assets/language-servers-section-ePtEpnDc.js.br +0 -0
- package/dist/web/assets/language-servers-section-ePtEpnDc.js.gz +0 -0
- package/dist/web/assets/lsp-client-XzmR3Brr.js +1 -0
- package/dist/web/assets/lsp-client-XzmR3Brr.js.br +0 -0
- package/dist/web/assets/lsp-client-XzmR3Brr.js.gz +0 -0
- package/dist/web/assets/markdown-renderer-CTtDap-l.js +1 -0
- package/dist/web/assets/{markdown-renderer-ok96IgnS.js → markdown-renderer-D05fAr57.js} +2 -2
- package/dist/web/assets/markdown-renderer-D05fAr57.js.br +0 -0
- package/dist/web/assets/markdown-renderer-D05fAr57.js.gz +0 -0
- package/dist/web/assets/{media-url-CzUJrREv.js → media-url-C6NumuN5.js} +1 -1
- package/dist/web/assets/metric-chart-canvas-DHJe0VER.js +1 -0
- package/dist/web/assets/metric-chart-canvas-DHJe0VER.js.br +0 -0
- package/dist/web/assets/metric-chart-canvas-DHJe0VER.js.gz +0 -0
- package/dist/web/assets/{mobile-bottom-sheet-CuNi2FRv.js → mobile-bottom-sheet-D4UyOatB.js} +1 -1
- package/dist/web/assets/mobile-bottom-sheet-D4UyOatB.js.br +0 -0
- package/dist/web/assets/mobile-bottom-sheet-D4UyOatB.js.gz +0 -0
- package/dist/web/assets/mobile-explorer-sheet-CcfGdGL8.js +1 -0
- package/dist/web/assets/mobile-explorer-sheet-CcfGdGL8.js.br +0 -0
- package/dist/web/assets/mobile-explorer-sheet-CcfGdGL8.js.gz +0 -0
- package/dist/web/assets/{new-design-dialog-Drkjb_LI.js → new-design-dialog-BgmQopJM.js} +1 -1
- package/dist/web/assets/new-design-dialog-BgmQopJM.js.br +0 -0
- package/dist/web/assets/new-design-dialog-BgmQopJM.js.gz +0 -0
- package/dist/web/assets/notification-store-q5QBklKA.js +1 -0
- package/dist/web/assets/{notifications-settings-section-C05WYqcE.js → notifications-settings-section-BBJ0r9OS.js} +1 -1
- package/dist/web/assets/notifications-settings-section-BBJ0r9OS.js.br +0 -0
- package/dist/web/assets/notifications-settings-section-BBJ0r9OS.js.gz +0 -0
- package/dist/web/assets/{onboarding-entry-CmUqv1kl.js → onboarding-entry-BYOYswsi.js} +1 -1
- package/dist/web/assets/onboarding-entry-BYOYswsi.js.br +0 -0
- package/dist/web/assets/onboarding-entry-BYOYswsi.js.gz +0 -0
- package/dist/web/assets/{onboarding-quick-orientation-DIhWv1CN.js → onboarding-quick-orientation-DPHYFMbA.js} +1 -1
- package/dist/web/assets/onboarding-quick-orientation-DPHYFMbA.js.br +0 -0
- package/dist/web/assets/onboarding-quick-orientation-DPHYFMbA.js.gz +0 -0
- package/dist/web/assets/overview-panel-pkPs44Tv.js +1 -0
- package/dist/web/assets/overview-panel-pkPs44Tv.js.br +0 -0
- package/dist/web/assets/overview-panel-pkPs44Tv.js.gz +0 -0
- package/dist/web/assets/{pdf-preview-qdXmMkOR.js → pdf-preview-c-L_kDAs.js} +1 -1
- package/dist/web/assets/pdf-preview-c-L_kDAs.js.br +0 -0
- package/dist/web/assets/pdf-preview-c-L_kDAs.js.gz +0 -0
- package/dist/web/assets/performance-panel-yPrq0DJV.js +3 -0
- package/dist/web/assets/performance-panel-yPrq0DJV.js.br +0 -0
- package/dist/web/assets/performance-panel-yPrq0DJV.js.gz +0 -0
- package/dist/web/assets/{postgres-viewer-Xvlun_n5.js → postgres-viewer-CnwDCMNd.js} +1 -1
- package/dist/web/assets/postgres-viewer-CnwDCMNd.js.br +0 -0
- package/dist/web/assets/postgres-viewer-CnwDCMNd.js.gz +0 -0
- package/dist/web/assets/{ppmbot-settings-section-C8PFlvaq.js → ppmbot-settings-section-BDXoSIDp.js} +1 -1
- package/dist/web/assets/ppmbot-settings-section-BDXoSIDp.js.br +0 -0
- package/dist/web/assets/ppmbot-settings-section-BDXoSIDp.js.gz +0 -0
- package/dist/web/assets/{pptx-export-CG6MRNok.js → pptx-export-D0RDY48h.js} +4 -4
- package/dist/web/assets/pptx-export-D0RDY48h.js.br +0 -0
- package/dist/web/assets/pptx-export-D0RDY48h.js.gz +0 -0
- package/dist/web/assets/{problems-panel-CFYO0Ro3.js → problems-panel-BgY8oyo9.js} +1 -1
- package/dist/web/assets/problems-panel-BgY8oyo9.js.br +0 -0
- package/dist/web/assets/problems-panel-BgY8oyo9.js.gz +0 -0
- package/dist/web/assets/process-row-format-CaaMcHzn.js +1 -0
- package/dist/web/assets/process-table-BOwDtXuG.js +1 -0
- package/dist/web/assets/process-table-BOwDtXuG.js.br +0 -0
- package/dist/web/assets/process-table-BOwDtXuG.js.gz +0 -0
- package/dist/web/assets/{project-framework-store-XxFXcUpb.js → project-framework-store-UDEMu28U.js} +1 -1
- package/dist/web/assets/{project-store-C5gJXkvb.js → project-store-DGa31y5B.js} +1 -1
- package/dist/web/assets/project-store-DGa31y5B.js.br +0 -0
- package/dist/web/assets/project-store-DGa31y5B.js.gz +0 -0
- package/dist/web/assets/{proxy-settings-section-BcHIEANE.js → proxy-settings-section-Db6Kc6xG.js} +1 -1
- package/dist/web/assets/proxy-settings-section-Db6Kc6xG.js.br +0 -0
- package/dist/web/assets/proxy-settings-section-Db6Kc6xG.js.gz +0 -0
- package/dist/web/assets/{query-audit-section-UKG_smyt.js → query-audit-section-C0PAXhfT.js} +1 -1
- package/dist/web/assets/query-audit-section-C0PAXhfT.js.br +0 -0
- package/dist/web/assets/query-audit-section-C0PAXhfT.js.gz +0 -0
- package/dist/web/assets/{remote-desktop-clipboard-notice-BGsp0edA.js → remote-desktop-clipboard-notice-DOVtobj_.js} +1 -1
- package/dist/web/assets/remote-desktop-clipboard-notice-DOVtobj_.js.br +0 -0
- package/dist/web/assets/remote-desktop-clipboard-notice-DOVtobj_.js.gz +0 -0
- package/dist/web/assets/remote-desktop-mobile-sheet-DXlU3msM.js +2 -0
- package/dist/web/assets/remote-desktop-mobile-sheet-DXlU3msM.js.br +0 -0
- package/dist/web/assets/remote-desktop-mobile-sheet-DXlU3msM.js.gz +0 -0
- package/dist/web/assets/remote-desktop-mobile-view-ClV4YTLU.js +1 -0
- package/dist/web/assets/remote-desktop-mobile-view-ClV4YTLU.js.br +0 -0
- package/dist/web/assets/remote-desktop-mobile-view-ClV4YTLU.js.gz +0 -0
- package/dist/web/assets/{remote-desktop-readiness-gate-CsZCCRJI.js → remote-desktop-readiness-gate-BsfoJzdc.js} +1 -1
- package/dist/web/assets/remote-desktop-readiness-gate-BsfoJzdc.js.br +0 -0
- package/dist/web/assets/remote-desktop-readiness-gate-BsfoJzdc.js.gz +0 -0
- package/dist/web/assets/remote-desktop-window-content-kDaXl1_Q.js +1 -0
- package/dist/web/assets/remote-desktop-window-content-kDaXl1_Q.js.br +0 -0
- package/dist/web/assets/remote-desktop-window-content-kDaXl1_Q.js.gz +0 -0
- package/dist/web/assets/{schedules-settings-section-Ci0O2Ng6.js → schedules-settings-section-CvbjQM8z.js} +1 -1
- package/dist/web/assets/schedules-settings-section-CvbjQM8z.js.br +0 -0
- package/dist/web/assets/schedules-settings-section-CvbjQM8z.js.gz +0 -0
- package/dist/web/assets/{scroll-area-XonvWYn_.js → scroll-area-sjOeAfoD.js} +1 -1
- package/dist/web/assets/scroll-area-sjOeAfoD.js.br +0 -0
- package/dist/web/assets/scroll-area-sjOeAfoD.js.gz +0 -0
- package/dist/web/assets/{separator-sQj04Ari.js → separator-9A3PCG1t.js} +1 -1
- package/dist/web/assets/services-panel-CimW47P8.js +1 -0
- package/dist/web/assets/services-panel-CimW47P8.js.br +0 -0
- package/dist/web/assets/services-panel-CimW47P8.js.gz +0 -0
- package/dist/web/assets/settings-body-BTXNtpWL.js +2 -0
- package/dist/web/assets/settings-body-BTXNtpWL.js.br +0 -0
- package/dist/web/assets/settings-body-BTXNtpWL.js.gz +0 -0
- package/dist/web/assets/settings-store-DvftLqzy.js +2 -0
- package/dist/web/assets/settings-store-DvftLqzy.js.br +0 -0
- package/dist/web/assets/settings-store-DvftLqzy.js.gz +0 -0
- package/dist/web/assets/settings-tab-CGhaRXjs.js +1 -0
- package/dist/web/assets/settings-window-content--Uyg_Psr.js +1 -0
- package/dist/web/assets/sortable-header-DUW9T1Hz.js +1 -0
- package/dist/web/assets/sortable-header-DUW9T1Hz.js.br +0 -0
- package/dist/web/assets/sortable-header-DUW9T1Hz.js.gz +0 -0
- package/dist/web/assets/{sql-query-editor-4iCGE4h2.js → sql-query-editor-Dn-cgDrW.js} +1 -1
- package/dist/web/assets/sql-query-editor-Dn-cgDrW.js.br +0 -0
- package/dist/web/assets/sql-query-editor-Dn-cgDrW.js.gz +0 -0
- package/dist/web/assets/{sqlite-viewer-CK6DulYk.js → sqlite-viewer-B0NfaMjq.js} +1 -1
- package/dist/web/assets/sqlite-viewer-B0NfaMjq.js.br +0 -0
- package/dist/web/assets/sqlite-viewer-B0NfaMjq.js.gz +0 -0
- package/dist/web/assets/system-monitor-body-CxCfRQFb.js +2 -0
- package/dist/web/assets/system-monitor-body-CxCfRQFb.js.br +0 -0
- package/dist/web/assets/system-monitor-body-CxCfRQFb.js.gz +0 -0
- package/dist/web/assets/system-monitor-tab-Cb3EZUdp.js +1 -0
- package/dist/web/assets/system-monitor-window-content-51CA-2PH.js +1 -0
- package/dist/web/assets/{tab-host-window-content-CWBaWKjA.js → tab-host-window-content-CSrcmKNG.js} +1 -1
- package/dist/web/assets/tab-host-window-content-CSrcmKNG.js.br +0 -0
- package/dist/web/assets/tab-host-window-content-CSrcmKNG.js.gz +0 -0
- package/dist/web/assets/{team-member-sheet-Br6mrxrw.js → team-member-sheet-D4X6nh-x.js} +2 -2
- package/dist/web/assets/team-member-sheet-D4X6nh-x.js.br +0 -0
- package/dist/web/assets/team-member-sheet-D4X6nh-x.js.gz +0 -0
- package/dist/web/assets/{team-member-window-content-CwCy7ZWf.js → team-member-window-content-BxkbGVIL.js} +1 -1
- package/dist/web/assets/team-member-window-content-BxkbGVIL.js.br +0 -0
- package/dist/web/assets/team-member-window-content-BxkbGVIL.js.gz +0 -0
- package/dist/web/assets/{terminal-tab-BW14RpCb.js → terminal-tab-BYo34IqU.js} +3 -3
- package/dist/web/assets/terminal-tab-BYo34IqU.js.br +0 -0
- package/dist/web/assets/terminal-tab-BYo34IqU.js.gz +0 -0
- package/dist/web/assets/{tool-cards-CgOBEUnb.js → tool-cards-DvXjOped.js} +4 -4
- package/dist/web/assets/tool-cards-DvXjOped.js.br +0 -0
- package/dist/web/assets/tool-cards-DvXjOped.js.gz +0 -0
- package/dist/web/assets/{use-blob-url-CmavyEP8.js → use-blob-url-CISBWKG5.js} +1 -1
- package/dist/web/assets/use-inline-blame-Br6mheS4.js +16 -0
- package/dist/web/assets/use-inline-blame-Br6mheS4.js.br +0 -0
- package/dist/web/assets/use-inline-blame-Br6mheS4.js.gz +0 -0
- package/dist/web/assets/use-lsp-BpJmvQ0n.js +6 -0
- package/dist/web/assets/use-lsp-BpJmvQ0n.js.br +0 -0
- package/dist/web/assets/use-lsp-BpJmvQ0n.js.gz +0 -0
- package/dist/web/assets/{use-monaco-theme-rH6epJYA.js → use-monaco-theme-UGOzyIhv.js} +1 -1
- package/dist/web/assets/use-process-kill-CyiiUD1E.js +1 -0
- package/dist/web/assets/use-process-kill-CyiiUD1E.js.br +0 -0
- package/dist/web/assets/use-process-kill-CyiiUD1E.js.gz +0 -0
- package/dist/web/assets/{use-websocket-QwURkjpV.js → use-websocket-Dpl21cHG.js} +1 -1
- package/dist/web/assets/{vendor-ui-BHh12_0v.js → vendor-ui-P9Ym7hCv.js} +1 -1
- package/dist/web/assets/vendor-ui-P9Ym7hCv.js.br +0 -0
- package/dist/web/assets/vendor-ui-P9Ym7hCv.js.gz +0 -0
- package/dist/web/assets/video-preview-D6Jw-IZK.js +1 -0
- package/dist/web/assets/video-preview-D6Jw-IZK.js.br +0 -0
- package/dist/web/assets/video-preview-D6Jw-IZK.js.gz +0 -0
- package/dist/web/assets/voice-settings-section-CcHQDYzN.js +1 -0
- package/dist/web/assets/voice-settings-section-CcHQDYzN.js.br +0 -0
- package/dist/web/assets/voice-settings-section-CcHQDYzN.js.gz +0 -0
- package/dist/web/assets/ws-auth-CqI8-aoA.js +1 -0
- package/dist/web/index.html +20 -20
- package/dist/web/index.html.br +0 -0
- package/dist/web/index.html.gz +0 -0
- package/dist/web/sw.js +1 -1
- package/dist/web/sw.js.br +0 -0
- package/dist/web/sw.js.gz +0 -0
- package/docs/deployment-guide.md +23 -0
- package/package.json +1 -1
- package/release-from-worktree.sh +224 -0
- package/src/cli/commands/config-cmd.ts +12 -0
- package/src/providers/claude-agent-sdk.ts +65 -2
- package/src/providers/codex-app-server/codex-jsonrpc-client.ts +16 -1
- package/src/providers/codex-app-server/codex-provider.ts +39 -7
- package/src/providers/codex-app-server/codex-reset-credit.ts +49 -0
- package/src/providers/provider-probe.ts +63 -0
- package/src/providers/registry.ts +109 -29
- package/src/server/index.ts +9 -0
- package/src/server/middleware/auth.ts +5 -1
- package/src/server/routes/codex-accounts.ts +14 -0
- package/src/server/routes/cross-origin-guard.ts +20 -0
- package/src/server/routes/git.ts +40 -4
- package/src/server/routes/lsp.ts +97 -4
- package/src/server/routes/resources.ts +72 -9
- package/src/server/routes/settings.ts +19 -1
- package/src/server/routes/speech.ts +66 -0
- package/src/server/routes/system-services.ts +76 -0
- package/src/server/routes/tunnel.ts +33 -1
- package/src/server/ws/chat.ts +74 -3
- package/src/services/codex-account-login.ts +26 -2
- package/src/services/codex-reset-credit.service.ts +83 -0
- package/src/services/compaction-savings.ts +127 -0
- package/src/services/db.service.ts +123 -8
- package/src/services/edge-forwarder.ts +191 -191
- package/src/services/fs-ops/fs-ops-trash.service.ts +22 -9
- package/src/services/git-refs/for-each-ref.ts +118 -0
- package/src/services/git.service.ts +25 -2
- package/src/services/host-info/spawn-runner.ts +48 -4
- package/src/services/jsonl-transcript-parser.ts +7 -42
- package/src/services/lsp/lsp-install.ts +289 -0
- package/src/services/lsp/lsp-manager.ts +160 -29
- package/src/services/lsp/server-registry.ts +120 -4
- package/src/services/named-tunnel/named-tunnel-config.ts +8 -2
- package/src/services/named-tunnel/named-tunnel-setup.service.ts +4 -0
- package/src/services/read-lines.ts +54 -0
- package/src/services/speech-to-text/whisper-catalog.ts +142 -0
- package/src/services/speech-to-text/whisper-download.ts +113 -0
- package/src/services/speech-to-text/whisper-hallucinations.ts +51 -0
- package/src/services/speech-to-text/whisper-install.service.ts +247 -0
- package/src/services/speech-to-text/whisper-paths.ts +62 -0
- package/src/services/speech-to-text/whisper-transcribe.service.ts +117 -0
- package/src/services/supervisor.ts +94 -11
- package/src/services/system-metrics/cpu-details-linux.ts +252 -0
- package/src/services/system-metrics/cpu-memory-collector.ts +140 -18
- package/src/services/system-metrics/device-collector-linux.ts +31 -0
- package/src/services/system-metrics/device-collector-types.ts +40 -0
- package/src/services/system-metrics/disk-devices-linux.ts +156 -0
- package/src/services/system-metrics/disk-inventory-linux.ts +150 -0
- package/src/services/system-metrics/fans-linux.ts +51 -0
- package/src/services/system-metrics/gpu-api-versions.ts +120 -0
- package/src/services/system-metrics/gpu-devices-linux.ts +370 -0
- package/src/services/system-metrics/gpu-fdinfo-linux.ts +253 -0
- package/src/services/system-metrics/hardware-inventory.ts +50 -0
- package/src/services/system-metrics/linux-fs.ts +43 -0
- package/src/services/system-metrics/memory-darwin.ts +91 -0
- package/src/services/system-metrics/memory-linux.ts +156 -0
- package/src/services/system-metrics/net-devices-linux.ts +114 -0
- package/src/services/system-metrics/net-inventory-linux.ts +100 -0
- package/src/services/system-metrics/partitions-linux.ts +158 -0
- package/src/services/system-metrics/process-collector-linux.ts +58 -6
- package/src/services/system-metrics/process-collector-types.ts +8 -1
- package/src/services/system-metrics/process-details-darwin.ts +166 -0
- package/src/services/system-metrics/process-details-linux.ts +150 -0
- package/src/services/system-metrics/process-grouping.ts +2 -1
- package/src/services/system-metrics/process-rows-builder.ts +2 -0
- package/src/services/system-metrics/process-swap-linux.ts +37 -0
- package/src/services/system-metrics/signal-executor.ts +84 -0
- package/src/services/system-metrics/signal-request-handler.ts +92 -0
- package/src/services/system-metrics/system-metrics-platform.ts +30 -4
- package/src/services/system-metrics/system-metrics-tick.ts +73 -4
- package/src/services/system-metrics/system-metrics.service.ts +87 -2
- package/src/services/system-metrics/udev-db.ts +40 -0
- package/src/services/system-services/app-cgroup-linux.ts +115 -0
- package/src/services/system-services/app-icon-service.ts +31 -0
- package/src/services/system-services/app-icons-linux.ts +124 -0
- package/src/services/system-services/apps-linux.ts +174 -0
- package/src/services/system-services/desktop-entry.ts +103 -0
- package/src/services/system-services/service-guard.ts +124 -0
- package/src/services/system-services/systemd-collector.ts +203 -0
- package/src/services/system-services/systemd-parse.ts +162 -0
- package/src/services/windows-process-tree.ts +351 -351
- package/src/shared/prompt-cache-idle.ts +197 -0
- package/src/shared/turn-usage.ts +85 -1
- package/src/shared/usage-extra.ts +19 -0
- package/src/types/chat.ts +29 -0
- package/src/types/config.ts +14 -0
- package/src/types/git.ts +41 -0
- package/src/types/system-hardware.ts +137 -0
- package/src/types/system-metrics.ts +231 -2
- package/src/types/system-services.ts +73 -0
- package/src/web/components/branch-review/branch-review-tab.tsx +20 -20
- package/src/web/components/branch-review/branch-review-tree-row.tsx +1 -13
- package/src/web/components/chat/chat-tab.tsx +3 -0
- package/src/web/components/chat/codex-usage-panel.tsx +4 -0
- package/src/web/components/chat/compaction-divider.tsx +48 -0
- package/src/web/components/chat/idle-cache-notice.tsx +67 -0
- package/src/web/components/chat/message-input.tsx +48 -13
- package/src/web/components/chat/message-list.tsx +15 -0
- package/src/web/components/chat/prompt-cache-chip.tsx +74 -0
- package/src/web/components/editor/code-editor.tsx +1 -0
- package/src/web/components/editor/editor-language-service.tsx +11 -3
- package/src/web/components/editor/lsp-status.tsx +115 -13
- package/src/web/components/git/branch-picker.tsx +419 -0
- package/src/web/components/git/branch-select.tsx +34 -0
- package/src/web/components/git/create-worktree-dialog.tsx +17 -16
- package/src/web/components/git/git-repo-picker.tsx +28 -40
- package/src/web/components/layout/status-bar.tsx +42 -19
- package/src/web/components/settings/accounts/account-card.tsx +10 -2
- package/src/web/components/settings/accounts/account-usage-extras.tsx +3 -2
- package/src/web/components/settings/accounts/codex-accounts-section.tsx +3 -1
- package/src/web/components/settings/accounts/codex-reset-credit-button.tsx +120 -0
- package/src/web/components/settings/ai-settings-section.tsx +67 -3
- package/src/web/components/settings/appearance-settings-section.tsx +2 -28
- package/src/web/components/settings/language-servers-section.tsx +362 -0
- package/src/web/components/settings/settings-categories.ts +8 -3
- package/src/web/components/settings/settings-section-content.tsx +2 -0
- package/src/web/components/settings/voice-settings-section.tsx +283 -0
- package/src/web/components/system/apps/app-row.tsx +110 -0
- package/src/web/components/system/apps/app-rows.ts +140 -0
- package/src/web/components/system/apps/apps-panel.tsx +129 -0
- package/src/web/components/system/metric-chart-canvas.tsx +79 -3
- package/src/web/components/system/overview-cards/card-shell.tsx +54 -0
- package/src/web/components/system/overview-cards/cpu-card.tsx +11 -6
- package/src/web/components/system/overview-cards/disk-card.tsx +11 -6
- package/src/web/components/system/overview-cards/gpu-card.tsx +16 -6
- package/src/web/components/system/overview-cards/mem-card.tsx +11 -6
- package/src/web/components/system/overview-cards/net-card.tsx +11 -6
- package/src/web/components/system/overview-device-target.ts +69 -0
- package/src/web/components/system/overview-panel.tsx +15 -1
- package/src/web/components/system/performance/cpu-core-grid.tsx +74 -0
- package/src/web/components/system/performance/cpu-detail.tsx +220 -0
- package/src/web/components/system/performance/detail-parts.tsx +149 -0
- package/src/web/components/system/performance/device-list.ts +139 -0
- package/src/web/components/system/performance/disk-detail.tsx +64 -0
- package/src/web/components/system/performance/fans-detail.tsx +35 -0
- package/src/web/components/system/performance/gpu-detail.tsx +116 -0
- package/src/web/components/system/performance/memory-detail.tsx +109 -0
- package/src/web/components/system/performance/nic-detail.tsx +55 -0
- package/src/web/components/system/performance/partition-list.tsx +79 -0
- package/src/web/components/system/performance/performance-panel.tsx +206 -0
- package/src/web/components/system/performance/use-hardware-inventory.ts +48 -0
- package/src/web/components/system/process-columns-grid.ts +5 -3
- package/src/web/components/system/process-details-dialog.tsx +116 -0
- package/src/web/components/system/process-group-row.tsx +6 -1
- package/src/web/components/system/process-row-format.ts +6 -0
- package/src/web/components/system/process-row-menu.tsx +72 -0
- package/src/web/components/system/process-row.tsx +7 -1
- package/src/web/components/system/process-table-model.ts +1 -1
- package/src/web/components/system/process-table-sort.ts +8 -4
- package/src/web/components/system/process-table-toolbar.tsx +10 -2
- package/src/web/components/system/process-table-totals.ts +3 -0
- package/src/web/components/system/process-table.tsx +25 -2
- package/src/web/components/system/services/service-action-confirm.tsx +100 -0
- package/src/web/components/system/services/service-columns.ts +103 -0
- package/src/web/components/system/services/service-details-sheet.tsx +97 -0
- package/src/web/components/system/services/service-resources.ts +105 -0
- package/src/web/components/system/services/service-row.tsx +146 -0
- package/src/web/components/system/services/service-rows.ts +92 -0
- package/src/web/components/system/services/service-sort.ts +90 -0
- package/src/web/components/system/services/services-header.tsx +51 -0
- package/src/web/components/system/services/services-panel.tsx +207 -0
- package/src/web/components/system/services/use-services.ts +92 -0
- package/src/web/components/system/sortable-header.tsx +13 -8
- package/src/web/components/system/sysmon-preferences.tsx +85 -0
- package/src/web/components/system/system-monitor-body.tsx +101 -43
- package/src/web/components/system/system-monitor-window-content.tsx +2 -3
- package/src/web/components/system/use-process-signal.ts +87 -0
- package/src/web/components/tunnels/public-tunnel-switch.tsx +82 -0
- package/src/web/components/tunnels/tunnel-manager-tab.tsx +4 -0
- package/src/web/components/ui/dialog.tsx +14 -1
- package/src/web/components/ui/search-select.tsx +279 -0
- package/src/web/components/ui/start-ellipsis.tsx +18 -0
- package/src/web/hooks/use-chat.ts +28 -0
- package/src/web/hooks/use-global-keybindings.ts +7 -5
- package/src/web/hooks/use-lsp.ts +23 -2
- package/src/web/hooks/use-minute-clock.ts +20 -0
- package/src/web/hooks/use-whisper-voice-input.ts +113 -0
- package/src/web/lib/api-tunnels.ts +19 -0
- package/src/web/lib/branch-select-items.ts +29 -0
- package/src/web/lib/cpu-graph-mode.ts +90 -0
- package/src/web/lib/format-bytes.ts +14 -0
- package/src/web/lib/git-ref-picker.ts +120 -0
- package/src/web/lib/lsp/lsp-client.ts +32 -0
- package/src/web/lib/search-select-rows.ts +55 -0
- package/src/web/lib/temperature.ts +27 -0
- package/src/web/lib/voice-wav.ts +69 -0
- package/src/web/stores/git-status-store.ts +31 -17
- package/src/web/stores/keybindings-store.ts +14 -1
- package/src/web/stores/settings-store.ts +65 -0
- package/src/web/styles/globals.css +11 -0
- package/dist/web/assets/accounts-settings-section-CgG5P3Jm.js +0 -1
- package/dist/web/assets/accounts-settings-section-CgG5P3Jm.js.br +0 -0
- package/dist/web/assets/accounts-settings-section-CgG5P3Jm.js.gz +0 -0
- package/dist/web/assets/ai-resource-editor-DKcsVDle.js.br +0 -0
- package/dist/web/assets/ai-resource-editor-DKcsVDle.js.gz +0 -0
- package/dist/web/assets/ai-settings-section-D7zJ-hCO.js +0 -1
- package/dist/web/assets/ai-settings-section-D7zJ-hCO.js.br +0 -0
- package/dist/web/assets/ai-settings-section-D7zJ-hCO.js.gz +0 -0
- package/dist/web/assets/ai-settings-section-OPqC2bYG.js +0 -1
- package/dist/web/assets/api-client-BgedDTvo.js +0 -1
- package/dist/web/assets/api-client-c8YOpTlT.js.br +0 -0
- package/dist/web/assets/api-client-c8YOpTlT.js.gz +0 -0
- package/dist/web/assets/api-settings-BmvNoCbn.js.br +0 -0
- package/dist/web/assets/api-settings-BmvNoCbn.js.gz +0 -0
- package/dist/web/assets/api-settings-Bti1-Zn3.js +0 -1
- package/dist/web/assets/appearance-settings-section-DL466x83.js +0 -1
- package/dist/web/assets/appearance-settings-section-DL466x83.js.br +0 -0
- package/dist/web/assets/appearance-settings-section-DL466x83.js.gz +0 -0
- package/dist/web/assets/audio-preview-vaG2cB-k.js.br +0 -0
- package/dist/web/assets/audio-preview-vaG2cB-k.js.gz +0 -0
- package/dist/web/assets/branch-review-tab-Dag86n_R.js +0 -1
- package/dist/web/assets/branch-review-tab-Dag86n_R.js.br +0 -0
- package/dist/web/assets/branch-review-tab-Dag86n_R.js.gz +0 -0
- package/dist/web/assets/button-Ci9EUgqd.js.br +0 -0
- package/dist/web/assets/button-Ci9EUgqd.js.gz +0 -0
- package/dist/web/assets/chat-preparation-cache-BSIQGiWx.js.br +0 -0
- package/dist/web/assets/chat-preparation-cache-BSIQGiWx.js.gz +0 -0
- package/dist/web/assets/chat-tab-CcYZLXls.js +0 -15
- package/dist/web/assets/chat-tab-CcYZLXls.js.br +0 -0
- package/dist/web/assets/chat-tab-CcYZLXls.js.gz +0 -0
- package/dist/web/assets/chat-tab-QFDhzzBZ.js +0 -1
- package/dist/web/assets/chat-tab-QFDhzzBZ.js.br +0 -0
- package/dist/web/assets/chat-tab-QFDhzzBZ.js.gz +0 -0
- package/dist/web/assets/code-editor-K-IN8VwJ.js +0 -7
- package/dist/web/assets/code-editor-K-IN8VwJ.js.br +0 -0
- package/dist/web/assets/code-editor-K-IN8VwJ.js.gz +0 -0
- package/dist/web/assets/codex-sign-in-again-dialog-C8JvblLG.js +0 -1
- package/dist/web/assets/codex-sign-in-again-dialog-C8JvblLG.js.br +0 -0
- package/dist/web/assets/codex-sign-in-again-dialog-C8JvblLG.js.gz +0 -0
- package/dist/web/assets/conflict-editor-qMGJH0Bx.js.br +0 -0
- package/dist/web/assets/conflict-editor-qMGJH0Bx.js.gz +0 -0
- package/dist/web/assets/context-menu-CjYTq8DI.js +0 -1
- package/dist/web/assets/context-menu-CjYTq8DI.js.br +0 -0
- package/dist/web/assets/context-menu-CjYTq8DI.js.gz +0 -0
- package/dist/web/assets/csv-preview-D2I742dl.js.br +0 -0
- package/dist/web/assets/csv-preview-D2I742dl.js.gz +0 -0
- package/dist/web/assets/data-grid-overlay-editor-CQRVKr7y.js.br +0 -0
- package/dist/web/assets/data-grid-overlay-editor-CQRVKr7y.js.gz +0 -0
- package/dist/web/assets/database-viewer-Bz4LjgIa.js.br +0 -0
- package/dist/web/assets/database-viewer-Bz4LjgIa.js.gz +0 -0
- package/dist/web/assets/design-export-client-ChMcMoqW.js.br +0 -0
- package/dist/web/assets/design-export-client-ChMcMoqW.js.gz +0 -0
- package/dist/web/assets/design-responsive-dialog-DNBhJJLf.js.br +0 -0
- package/dist/web/assets/design-responsive-dialog-DNBhJJLf.js.gz +0 -0
- package/dist/web/assets/design-tab-C987MtMR.js +0 -33
- package/dist/web/assets/design-tab-C987MtMR.js.br +0 -0
- package/dist/web/assets/design-tab-C987MtMR.js.gz +0 -0
- package/dist/web/assets/designs-sidebar-panel-CapP1yXq.js.br +0 -0
- package/dist/web/assets/designs-sidebar-panel-CapP1yXq.js.gz +0 -0
- package/dist/web/assets/dialog-B8clVkyi.js +0 -1
- package/dist/web/assets/dialog-B8clVkyi.js.br +0 -0
- package/dist/web/assets/dialog-B8clVkyi.js.gz +0 -0
- package/dist/web/assets/diff-viewer-CdXi7cZN.js.br +0 -0
- package/dist/web/assets/diff-viewer-CdXi7cZN.js.gz +0 -0
- package/dist/web/assets/docx-preview-C9XdKEj1.js.br +0 -0
- package/dist/web/assets/docx-preview-C9XdKEj1.js.gz +0 -0
- package/dist/web/assets/editor-language-service-Zle_PZV8.js +0 -1
- package/dist/web/assets/editor-language-service-Zle_PZV8.js.br +0 -0
- package/dist/web/assets/editor-language-service-Zle_PZV8.js.gz +0 -0
- package/dist/web/assets/explorer-body-DHMQR1Rk.js +0 -2
- package/dist/web/assets/explorer-body-DHMQR1Rk.js.br +0 -0
- package/dist/web/assets/explorer-body-DHMQR1Rk.js.gz +0 -0
- package/dist/web/assets/explorer-window-content-Df40nQBk.js +0 -1
- package/dist/web/assets/explorer-window-content-Df40nQBk.js.br +0 -0
- package/dist/web/assets/explorer-window-content-Df40nQBk.js.gz +0 -0
- package/dist/web/assets/extension-manager-section-B2nZzJ2b.js.br +0 -0
- package/dist/web/assets/extension-manager-section-B2nZzJ2b.js.gz +0 -0
- package/dist/web/assets/extension-webview-BGoDxkdF.js.br +0 -0
- package/dist/web/assets/extension-webview-BGoDxkdF.js.gz +0 -0
- package/dist/web/assets/file-icons-gx_FBOXY.js.br +0 -0
- package/dist/web/assets/file-icons-gx_FBOXY.js.gz +0 -0
- package/dist/web/assets/file-store-D9KE0kQC.js.br +0 -0
- package/dist/web/assets/file-store-D9KE0kQC.js.gz +0 -0
- package/dist/web/assets/files-settings-section-DC3OzzOl.js.br +0 -0
- package/dist/web/assets/files-settings-section-DC3OzzOl.js.gz +0 -0
- package/dist/web/assets/format-bytes-DmSHAKFe.js +0 -1
- package/dist/web/assets/format-file-meta-mQwA6SuY.js.br +0 -0
- package/dist/web/assets/format-file-meta-mQwA6SuY.js.gz +0 -0
- package/dist/web/assets/fs-api-DL36slbd.js.br +0 -0
- package/dist/web/assets/fs-api-DL36slbd.js.gz +0 -0
- package/dist/web/assets/general-settings-section-i41W43ER.js.br +0 -0
- package/dist/web/assets/general-settings-section-i41W43ER.js.gz +0 -0
- package/dist/web/assets/git-log-panel-DHzPIkJg.js.br +0 -0
- package/dist/web/assets/git-log-panel-DHzPIkJg.js.gz +0 -0
- package/dist/web/assets/git-repo-store-DSypNj19.js.br +0 -0
- package/dist/web/assets/git-repo-store-DSypNj19.js.gz +0 -0
- package/dist/web/assets/glide-data-grid-Dj4vrDvj.js.br +0 -0
- package/dist/web/assets/glide-data-grid-Dj4vrDvj.js.gz +0 -0
- package/dist/web/assets/group-chat-tab-DfwZb-fI.js +0 -1
- package/dist/web/assets/group-chat-tab-DfwZb-fI.js.br +0 -0
- package/dist/web/assets/group-chat-tab-DfwZb-fI.js.gz +0 -0
- package/dist/web/assets/index-DMA-So69.css +0 -1
- package/dist/web/assets/index-DMA-So69.css.br +0 -0
- package/dist/web/assets/index-DMA-So69.css.gz +0 -0
- package/dist/web/assets/index-jG9mLs-J.js +0 -52
- package/dist/web/assets/index-jG9mLs-J.js.br +0 -0
- package/dist/web/assets/index-jG9mLs-J.js.gz +0 -0
- package/dist/web/assets/keybindings-store-BlapkC6O.js +0 -1
- package/dist/web/assets/keyboard-shortcuts-section-CxdGSNGr.js.br +0 -0
- package/dist/web/assets/keyboard-shortcuts-section-CxdGSNGr.js.gz +0 -0
- package/dist/web/assets/markdown-renderer-DJ9EVU1B.js +0 -1
- package/dist/web/assets/markdown-renderer-ok96IgnS.js.br +0 -0
- package/dist/web/assets/markdown-renderer-ok96IgnS.js.gz +0 -0
- package/dist/web/assets/mobile-bottom-sheet-CuNi2FRv.js.br +0 -0
- package/dist/web/assets/mobile-bottom-sheet-CuNi2FRv.js.gz +0 -0
- package/dist/web/assets/mobile-explorer-sheet-Cc03LUKy.js +0 -1
- package/dist/web/assets/mobile-explorer-sheet-Cc03LUKy.js.br +0 -0
- package/dist/web/assets/mobile-explorer-sheet-Cc03LUKy.js.gz +0 -0
- package/dist/web/assets/new-design-dialog-Drkjb_LI.js.br +0 -0
- package/dist/web/assets/new-design-dialog-Drkjb_LI.js.gz +0 -0
- package/dist/web/assets/notification-store-DCyxho0O.js +0 -1
- package/dist/web/assets/notifications-settings-section-C05WYqcE.js.br +0 -0
- package/dist/web/assets/notifications-settings-section-C05WYqcE.js.gz +0 -0
- package/dist/web/assets/onboarding-entry-CmUqv1kl.js.br +0 -0
- package/dist/web/assets/onboarding-entry-CmUqv1kl.js.gz +0 -0
- package/dist/web/assets/onboarding-quick-orientation-DIhWv1CN.js.br +0 -0
- package/dist/web/assets/onboarding-quick-orientation-DIhWv1CN.js.gz +0 -0
- package/dist/web/assets/overview-panel-BZAW_nag.js +0 -1
- package/dist/web/assets/overview-panel-BZAW_nag.js.br +0 -0
- package/dist/web/assets/overview-panel-BZAW_nag.js.gz +0 -0
- package/dist/web/assets/pdf-preview-qdXmMkOR.js.br +0 -0
- package/dist/web/assets/pdf-preview-qdXmMkOR.js.gz +0 -0
- package/dist/web/assets/postgres-viewer-Xvlun_n5.js.br +0 -0
- package/dist/web/assets/postgres-viewer-Xvlun_n5.js.gz +0 -0
- package/dist/web/assets/ppmbot-settings-section-C8PFlvaq.js.br +0 -0
- package/dist/web/assets/ppmbot-settings-section-C8PFlvaq.js.gz +0 -0
- package/dist/web/assets/pptx-export-CG6MRNok.js.br +0 -0
- package/dist/web/assets/pptx-export-CG6MRNok.js.gz +0 -0
- package/dist/web/assets/problems-panel-CFYO0Ro3.js.br +0 -0
- package/dist/web/assets/problems-panel-CFYO0Ro3.js.gz +0 -0
- package/dist/web/assets/process-table-BVeRguFg.js +0 -1
- package/dist/web/assets/process-table-BVeRguFg.js.br +0 -0
- package/dist/web/assets/process-table-BVeRguFg.js.gz +0 -0
- package/dist/web/assets/project-store-C5gJXkvb.js.br +0 -0
- package/dist/web/assets/project-store-C5gJXkvb.js.gz +0 -0
- package/dist/web/assets/proxy-settings-section-BcHIEANE.js.br +0 -0
- package/dist/web/assets/proxy-settings-section-BcHIEANE.js.gz +0 -0
- package/dist/web/assets/query-audit-section-UKG_smyt.js.br +0 -0
- package/dist/web/assets/query-audit-section-UKG_smyt.js.gz +0 -0
- package/dist/web/assets/remote-desktop-clipboard-notice-BGsp0edA.js.br +0 -0
- package/dist/web/assets/remote-desktop-clipboard-notice-BGsp0edA.js.gz +0 -0
- package/dist/web/assets/remote-desktop-mobile-sheet-DxkmF8qb.js +0 -2
- package/dist/web/assets/remote-desktop-mobile-sheet-DxkmF8qb.js.br +0 -0
- package/dist/web/assets/remote-desktop-mobile-sheet-DxkmF8qb.js.gz +0 -0
- package/dist/web/assets/remote-desktop-mobile-view-DMD0M9Jh.js +0 -1
- package/dist/web/assets/remote-desktop-mobile-view-DMD0M9Jh.js.br +0 -0
- package/dist/web/assets/remote-desktop-mobile-view-DMD0M9Jh.js.gz +0 -0
- package/dist/web/assets/remote-desktop-readiness-gate-CsZCCRJI.js.br +0 -0
- package/dist/web/assets/remote-desktop-readiness-gate-CsZCCRJI.js.gz +0 -0
- package/dist/web/assets/remote-desktop-window-content-DrHw_vwY.js +0 -1
- package/dist/web/assets/remote-desktop-window-content-DrHw_vwY.js.br +0 -0
- package/dist/web/assets/remote-desktop-window-content-DrHw_vwY.js.gz +0 -0
- package/dist/web/assets/schedules-settings-section-Ci0O2Ng6.js.br +0 -0
- package/dist/web/assets/schedules-settings-section-Ci0O2Ng6.js.gz +0 -0
- package/dist/web/assets/scroll-area-XonvWYn_.js.br +0 -0
- package/dist/web/assets/scroll-area-XonvWYn_.js.gz +0 -0
- package/dist/web/assets/settings-body-B6JGOIYb.js +0 -2
- package/dist/web/assets/settings-body-B6JGOIYb.js.br +0 -0
- package/dist/web/assets/settings-body-B6JGOIYb.js.gz +0 -0
- package/dist/web/assets/settings-store-Br5c1efC.js +0 -2
- package/dist/web/assets/settings-store-Br5c1efC.js.br +0 -0
- package/dist/web/assets/settings-store-Br5c1efC.js.gz +0 -0
- package/dist/web/assets/settings-tab-CIQM4fNE.js +0 -1
- package/dist/web/assets/settings-window-content-BGNNz-E5.js +0 -1
- package/dist/web/assets/sql-query-editor-4iCGE4h2.js.br +0 -3
- package/dist/web/assets/sql-query-editor-4iCGE4h2.js.gz +0 -0
- package/dist/web/assets/sqlite-viewer-CK6DulYk.js.br +0 -0
- package/dist/web/assets/sqlite-viewer-CK6DulYk.js.gz +0 -0
- package/dist/web/assets/system-monitor-body-J9JtvAWV.js +0 -2
- package/dist/web/assets/system-monitor-body-J9JtvAWV.js.br +0 -0
- package/dist/web/assets/system-monitor-body-J9JtvAWV.js.gz +0 -0
- package/dist/web/assets/system-monitor-tab-DjBeRE81.js +0 -1
- package/dist/web/assets/system-monitor-window-content-BT7-ugFx.js +0 -1
- package/dist/web/assets/tab-host-window-content-CWBaWKjA.js.br +0 -0
- package/dist/web/assets/tab-host-window-content-CWBaWKjA.js.gz +0 -0
- package/dist/web/assets/team-member-sheet-Br6mrxrw.js.br +0 -0
- package/dist/web/assets/team-member-sheet-Br6mrxrw.js.gz +0 -0
- package/dist/web/assets/team-member-window-content-CwCy7ZWf.js.br +0 -0
- package/dist/web/assets/team-member-window-content-CwCy7ZWf.js.gz +0 -0
- package/dist/web/assets/terminal-tab-BW14RpCb.js.br +0 -0
- package/dist/web/assets/terminal-tab-BW14RpCb.js.gz +0 -0
- package/dist/web/assets/tool-cards-CgOBEUnb.js.br +0 -0
- package/dist/web/assets/tool-cards-CgOBEUnb.js.gz +0 -0
- package/dist/web/assets/use-inline-blame-Bzjfru4n.js +0 -16
- package/dist/web/assets/use-inline-blame-Bzjfru4n.js.br +0 -0
- package/dist/web/assets/use-inline-blame-Bzjfru4n.js.gz +0 -0
- package/dist/web/assets/use-lsp-hGdjL2hV.js +0 -6
- package/dist/web/assets/use-lsp-hGdjL2hV.js.br +0 -0
- package/dist/web/assets/use-lsp-hGdjL2hV.js.gz +0 -0
- package/dist/web/assets/vendor-ui-BHh12_0v.js.br +0 -0
- package/dist/web/assets/vendor-ui-BHh12_0v.js.gz +0 -0
- package/dist/web/assets/video-preview-P9T-ZVdN.js +0 -1
- package/dist/web/assets/video-preview-P9T-ZVdN.js.br +0 -0
- package/dist/web/assets/video-preview-P9T-ZVdN.js.gz +0 -0
- package/dist/web/assets/ws-auth-kXgsX2Js.js +0 -1
- package/packages/vscode-compat/node_modules/.bin/tsc.bunx +0 -0
- package/packages/vscode-compat/node_modules/.bin/tsc.exe +0 -0
- package/packages/vscode-compat/node_modules/.bin/tsserver.bunx +0 -0
- package/packages/vscode-compat/node_modules/.bin/tsserver.exe +0 -0
- /package/dist/web/assets/{__vite-browser-external-BpSQWxcq.js → __vite-browser-external-BILfYaAs.js} +0 -0
- /package/dist/web/assets/{run-in-terminal-HIdNgYim.js → run-in-terminal-2pSiup8e.js} +0 -0
- /package/dist/web/assets/{vendor-xterm-DLDtzLib.js → vendor-xterm-DF1JTfsy.js} +0 -0
- /package/dist/web/assets/{vendor-xterm-DLDtzLib.js.br → vendor-xterm-DF1JTfsy.js.br} +0 -0
- /package/dist/web/assets/{vendor-xterm-DLDtzLib.js.gz → vendor-xterm-DF1JTfsy.js.gz} +0 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sending one signal to a process — Mission Center's "Send Signal" submenu.
|
|
3
|
+
*
|
|
4
|
+
* Separate from `useProcessKill` because the two mean different things: a kill
|
|
5
|
+
* escalates TERM to KILL, while asking for SIGSTOP must send SIGSTOP and nothing
|
|
6
|
+
* else. The server enforces the SAME guard for both, so a process the kill button
|
|
7
|
+
* refuses cannot be suspended either.
|
|
8
|
+
*/
|
|
9
|
+
import { useCallback, useState } from "react";
|
|
10
|
+
import { toast } from "sonner";
|
|
11
|
+
import { getAuthToken } from "@/lib/api-client";
|
|
12
|
+
import type {
|
|
13
|
+
ProcessInfo, ProcessSignal, SignalProcessRequest, SignalProcessResult,
|
|
14
|
+
} from "../../../types/system-metrics";
|
|
15
|
+
|
|
16
|
+
/** Human wording for the menu, in the order Mission Center lists them. */
|
|
17
|
+
export const SIGNAL_LABELS: Record<ProcessSignal, string> = {
|
|
18
|
+
STOP: "Suspend (SIGSTOP)",
|
|
19
|
+
CONT: "Continue (SIGCONT)",
|
|
20
|
+
TERM: "Terminate (SIGTERM)",
|
|
21
|
+
KILL: "Kill (SIGKILL)",
|
|
22
|
+
HUP: "Hang up (SIGHUP)",
|
|
23
|
+
INT: "Interrupt (SIGINT)",
|
|
24
|
+
USR1: "User signal 1",
|
|
25
|
+
USR2: "User signal 2",
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/** The two that cannot be caught or ignored, so the UI can warn before sending. */
|
|
29
|
+
export const UNCATCHABLE_SIGNALS: readonly ProcessSignal[] = ["KILL", "STOP"];
|
|
30
|
+
|
|
31
|
+
/** `api.post` has no way to add the `X-PPM-Request` header the route requires,
|
|
32
|
+
* so this mirrors `killProcess` exactly — one Error on any failure. */
|
|
33
|
+
export async function signalProcess(request: SignalProcessRequest): Promise<SignalProcessResult> {
|
|
34
|
+
const token = getAuthToken();
|
|
35
|
+
const headers: Record<string, string> = {
|
|
36
|
+
"Content-Type": "application/json",
|
|
37
|
+
"X-PPM-Request": "1",
|
|
38
|
+
};
|
|
39
|
+
if (token) headers.Authorization = `Bearer ${token}`;
|
|
40
|
+
|
|
41
|
+
const res = await fetch("/api/system/resources/signal", {
|
|
42
|
+
method: "POST",
|
|
43
|
+
headers,
|
|
44
|
+
body: JSON.stringify(request),
|
|
45
|
+
});
|
|
46
|
+
let json: { ok: boolean; data?: SignalProcessResult; error?: string };
|
|
47
|
+
try {
|
|
48
|
+
json = await res.json();
|
|
49
|
+
} catch {
|
|
50
|
+
throw new Error(res.ok ? "Empty response from server" : `Server error (HTTP ${res.status})`);
|
|
51
|
+
}
|
|
52
|
+
if (json.ok === false) throw new Error(json.error ?? `HTTP ${res.status}`);
|
|
53
|
+
return json.data as SignalProcessResult;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function buildSignalRequest(
|
|
57
|
+
proc: ProcessInfo,
|
|
58
|
+
signal: ProcessSignal,
|
|
59
|
+
tree: boolean,
|
|
60
|
+
): SignalProcessRequest {
|
|
61
|
+
// `startedAt` is the identity guard: a recycled pid is a 409, never a signal
|
|
62
|
+
// delivered to whatever took the number over.
|
|
63
|
+
return { pid: proc.pid, startedAt: proc.startedAt, signal, tree };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function useProcessSignal() {
|
|
67
|
+
const [pending, setPending] = useState<number | null>(null);
|
|
68
|
+
|
|
69
|
+
const send = useCallback(async (proc: ProcessInfo, signal: ProcessSignal, tree = false) => {
|
|
70
|
+
setPending(proc.pid);
|
|
71
|
+
try {
|
|
72
|
+
const result = await signalProcess(buildSignalRequest(proc, signal, tree));
|
|
73
|
+
const count = result.signalled.length;
|
|
74
|
+
toast.success(
|
|
75
|
+
count > 1
|
|
76
|
+
? `Sent SIG${signal} to ${proc.name} and ${count - 1} child processes`
|
|
77
|
+
: `Sent SIG${signal} to ${proc.name} (${proc.pid})`,
|
|
78
|
+
);
|
|
79
|
+
} catch (e) {
|
|
80
|
+
toast.error(e instanceof Error ? e.message : `Could not signal ${proc.name}`);
|
|
81
|
+
} finally {
|
|
82
|
+
setPending(null);
|
|
83
|
+
}
|
|
84
|
+
}, []);
|
|
85
|
+
|
|
86
|
+
return { send, pending };
|
|
87
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Master on/off switch for PPM's own public tunnel.
|
|
3
|
+
*
|
|
4
|
+
* The switch only writes config — the supervisor owns the cloudflared process
|
|
5
|
+
* and picks the change up as a `retunnel` — so "switched on" and "actually
|
|
6
|
+
* reachable" are a few seconds apart. The row reports both rather than
|
|
7
|
+
* pretending the toggle is instant, and polls on the same 10s cadence as the
|
|
8
|
+
* tunnel list below it.
|
|
9
|
+
*/
|
|
10
|
+
import { useCallback, useEffect, useState } from "react";
|
|
11
|
+
import { Globe, Loader2 } from "@/lib/icons";
|
|
12
|
+
import { Switch } from "@/components/ui/switch";
|
|
13
|
+
import { toast } from "sonner";
|
|
14
|
+
import { publicTunnelApi, type PublicTunnelStatus } from "@/lib/api-tunnels";
|
|
15
|
+
|
|
16
|
+
export function PublicTunnelSwitch() {
|
|
17
|
+
const [status, setStatus] = useState<PublicTunnelStatus | null>(null);
|
|
18
|
+
const [pending, setPending] = useState(false);
|
|
19
|
+
|
|
20
|
+
const refresh = useCallback(async () => {
|
|
21
|
+
try {
|
|
22
|
+
setStatus(await publicTunnelApi.status());
|
|
23
|
+
} catch {
|
|
24
|
+
// Keep the last known state — a failed poll is not a state change.
|
|
25
|
+
}
|
|
26
|
+
}, []);
|
|
27
|
+
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
refresh();
|
|
30
|
+
const interval = setInterval(refresh, 10_000);
|
|
31
|
+
return () => clearInterval(interval);
|
|
32
|
+
}, [refresh]);
|
|
33
|
+
|
|
34
|
+
// Nothing to toggle until the first read lands; rendering a default-on switch
|
|
35
|
+
// first would flicker to off on every load of a machine that has it off.
|
|
36
|
+
if (!status) return null;
|
|
37
|
+
|
|
38
|
+
// Absent on a server that predates the switch, where the tunnel was
|
|
39
|
+
// unconditional — so absent reads as on, never off.
|
|
40
|
+
const enabled = status.enabled ?? true;
|
|
41
|
+
const starting = enabled && !status.active;
|
|
42
|
+
|
|
43
|
+
async function toggle(next: boolean) {
|
|
44
|
+
setPending(true);
|
|
45
|
+
setStatus((s) => (s ? { ...s, enabled: next } : s));
|
|
46
|
+
try {
|
|
47
|
+
await publicTunnelApi.setEnabled(next);
|
|
48
|
+
await refresh();
|
|
49
|
+
} catch (e) {
|
|
50
|
+
setStatus((s) => (s ? { ...s, enabled: !next } : s));
|
|
51
|
+
toast.error(e instanceof Error ? e.message : "Could not change the tunnel setting");
|
|
52
|
+
} finally {
|
|
53
|
+
setPending(false);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<div className="flex items-center gap-3 p-3 border-b border-border bg-surface">
|
|
59
|
+
<Globe className="size-4 shrink-0 text-text-secondary" />
|
|
60
|
+
<div className="flex-1 min-w-0">
|
|
61
|
+
<div className="text-sm font-medium text-text-primary">Public tunnel</div>
|
|
62
|
+
<p className="text-xs text-text-secondary leading-relaxed truncate">
|
|
63
|
+
{!enabled
|
|
64
|
+
? "Off — PPM stays reachable on this network only"
|
|
65
|
+
: starting
|
|
66
|
+
? "Starting…"
|
|
67
|
+
: status.url ?? "On — waiting for a public URL"}
|
|
68
|
+
</p>
|
|
69
|
+
</div>
|
|
70
|
+
{(pending || starting) && <Loader2 className="size-4 shrink-0 animate-spin text-text-subtle" />}
|
|
71
|
+
{/* The control is 20x36; the pseudo-element takes its hit area to 44x60
|
|
72
|
+
so it is tappable on a phone without changing the row's layout. */}
|
|
73
|
+
<Switch
|
|
74
|
+
checked={enabled}
|
|
75
|
+
onCheckedChange={toggle}
|
|
76
|
+
disabled={pending}
|
|
77
|
+
aria-label="Public tunnel"
|
|
78
|
+
className="relative shrink-0 before:absolute before:-inset-3 before:content-['']"
|
|
79
|
+
/>
|
|
80
|
+
</div>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
@@ -4,6 +4,7 @@ import { tunnelsApi, type TunnelEntry } from "@/lib/api-tunnels";
|
|
|
4
4
|
import { copyToClipboard } from "@/lib/clipboard";
|
|
5
5
|
import { toast } from "sonner";
|
|
6
6
|
import { NamedTunnelSection } from "@/components/tunnels/named-tunnel/named-tunnel-section";
|
|
7
|
+
import { PublicTunnelSwitch } from "@/components/tunnels/public-tunnel-switch";
|
|
7
8
|
|
|
8
9
|
/** Badge label + style per tunnel source. */
|
|
9
10
|
const SOURCE_META: Record<TunnelEntry["source"], { label: string; cls: string }> = {
|
|
@@ -92,6 +93,9 @@ export function TunnelManagerTab() {
|
|
|
92
93
|
|
|
93
94
|
return (
|
|
94
95
|
<div className="flex flex-col h-full w-full bg-background">
|
|
96
|
+
{/* Master switch first: everything below it is moot while the tunnel is off */}
|
|
97
|
+
<PublicTunnelSwitch />
|
|
98
|
+
|
|
95
99
|
{/* Named tunnel status + setup entry point, above the quick-forward form */}
|
|
96
100
|
<NamedTunnelSection />
|
|
97
101
|
|
|
@@ -68,7 +68,20 @@ function DialogContent({
|
|
|
68
68
|
<DialogPrimitive.Content
|
|
69
69
|
data-slot="dialog-content"
|
|
70
70
|
className={cn(
|
|
71
|
-
|
|
71
|
+
// `grid-cols-[minmax(0,1fr)]` rather than the implicit `auto` column: an
|
|
72
|
+
// auto track's base size is its items' MIN-CONTENT contribution, so one
|
|
73
|
+
// unbreakable string anywhere inside — a systemd unit name, a log line,
|
|
74
|
+
// a path — widens the column past the dialog and every child with it,
|
|
75
|
+
// drawing outside the panel. `overflow-wrap: break-word` does not help,
|
|
76
|
+
// because by specification it does not affect min-content size.
|
|
77
|
+
"fixed top-[50%] left-[50%] z-50 grid grid-cols-[minmax(0,1fr)] w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border bg-background text-foreground p-6 shadow-lg duration-200 outline-none data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 sm:max-w-lg",
|
|
78
|
+
// The close button is absolutely positioned over the top-right corner,
|
|
79
|
+
// which is the header's first line — so a title long enough to reach it
|
|
80
|
+
// renders underneath it. Reserve the corner, but only when the button is
|
|
81
|
+
// actually there.
|
|
82
|
+
// A descendant selector, not a child one: several dialogs put the header
|
|
83
|
+
// inside a flex/scroll wrapper.
|
|
84
|
+
showCloseButton && "[&_[data-slot=dialog-header]]:pr-8",
|
|
72
85
|
className
|
|
73
86
|
)}
|
|
74
87
|
{...props}
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A select whose list can be searched: a trigger showing the current choice,
|
|
3
|
+
* and a panel with a filter box over the options.
|
|
4
|
+
*
|
|
5
|
+
* It exists because the lists it serves are the case a native `<select>` or a
|
|
6
|
+
* plain menu handles worst — dozens to hundreds of names that agree for most of
|
|
7
|
+
* their length (`fix/NX-1234-…` branches, `nxsys-backend-nx5833` checkouts), where
|
|
8
|
+
* the only way to the one you want is reading past the ones you do not. The shape
|
|
9
|
+
* is the checkout quick pick's, and it shares that picker's keyboard arithmetic
|
|
10
|
+
* (`moveSelection`) and its selection tint.
|
|
11
|
+
*
|
|
12
|
+
* A Radix popover rather than a panel positioned by hand, because the surfaces
|
|
13
|
+
* that mount it are `overflow-hidden`: a dropdown anchored inside one is clipped at
|
|
14
|
+
* its bottom edge, which shows as a list a few rows tall with no scrollbar and no
|
|
15
|
+
* sign that anything was cut. The portal escapes that, and collision detection
|
|
16
|
+
* keeps it on screen when the panel is docked low in the window. Below `md` it is
|
|
17
|
+
* a bottom sheet instead, per the mobile rules.
|
|
18
|
+
*/
|
|
19
|
+
import { useCallback, useEffect, useMemo, useRef, useState, type ElementType } from "react";
|
|
20
|
+
import { Popover } from "radix-ui";
|
|
21
|
+
import { Check, ChevronDown, Search } from "@/lib/icons";
|
|
22
|
+
import { cn } from "@/lib/utils";
|
|
23
|
+
import { useIsMobile } from "@/hooks/use-is-mobile";
|
|
24
|
+
import { BottomSheet } from "@/components/ui/mobile-bottom-sheet";
|
|
25
|
+
import { usePortalContainer } from "@/components/ui/portal-container-context";
|
|
26
|
+
import { StartEllipsis } from "@/components/ui/start-ellipsis";
|
|
27
|
+
import { firstSelectable, moveSelection } from "@/lib/git-ref-picker";
|
|
28
|
+
import { rowIndexOf, searchRows, type SearchSelectItem } from "@/lib/search-select-rows";
|
|
29
|
+
|
|
30
|
+
export interface SearchSelectProps {
|
|
31
|
+
value: string;
|
|
32
|
+
items: SearchSelectItem[];
|
|
33
|
+
onChange: (value: string) => void;
|
|
34
|
+
/** Names the control for screen readers, and titles the sheet on mobile. */
|
|
35
|
+
label: string;
|
|
36
|
+
/** The filter box's placeholder — "Search branches". */
|
|
37
|
+
searchPlaceholder: string;
|
|
38
|
+
/** What an empty result says — "No matching branches". */
|
|
39
|
+
emptyText: string;
|
|
40
|
+
/** Leading icon on the trigger, if the surroundings do not already carry one. */
|
|
41
|
+
icon?: ElementType;
|
|
42
|
+
/** Trigger text while `value` names none of the items. */
|
|
43
|
+
placeholder?: string;
|
|
44
|
+
testId?: string;
|
|
45
|
+
/** Width and look, from the caller — the trigger shares a row with everything else. */
|
|
46
|
+
className?: string;
|
|
47
|
+
/**
|
|
48
|
+
* Set this inside a Radix dialog, and only there.
|
|
49
|
+
*
|
|
50
|
+
* A modal dialog traps focus, and the popover is portalled outside it: the
|
|
51
|
+
* dialog's focus scope sees focus land on an element it does not contain and
|
|
52
|
+
* pulls it straight back, so the filter box cannot be typed in at all. A
|
|
53
|
+
* modal popover has a trapped scope of its own, which pushes onto Radix's
|
|
54
|
+
* scope stack and pauses the dialog's — the one arrangement where both
|
|
55
|
+
* behave. It costs a scroll lock and `aria-hidden` on everything behind,
|
|
56
|
+
* which is why it is not the default out in a panel.
|
|
57
|
+
*/
|
|
58
|
+
modal?: boolean;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function SearchSelect({
|
|
62
|
+
value, items, onChange, label, searchPlaceholder, emptyText, icon: Icon, placeholder,
|
|
63
|
+
testId, className, modal,
|
|
64
|
+
}: SearchSelectProps) {
|
|
65
|
+
const isMobile = useIsMobile();
|
|
66
|
+
const portalContainer = usePortalContainer();
|
|
67
|
+
const [open, setOpen] = useState(false);
|
|
68
|
+
const [query, setQuery] = useState("");
|
|
69
|
+
const [selected, setSelected] = useState(-1);
|
|
70
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
|
71
|
+
const listRef = useRef<HTMLDivElement>(null);
|
|
72
|
+
|
|
73
|
+
const rows = useMemo(() => searchRows(items, query), [items, query]);
|
|
74
|
+
const current = items.find((i) => i.value === value);
|
|
75
|
+
|
|
76
|
+
/*
|
|
77
|
+
* Highlight the choice already in force when it survives the filter, and the
|
|
78
|
+
* first match otherwise. Opening on row zero instead would put Enter on an
|
|
79
|
+
* option nobody asked for, and on a list this long it hides where you
|
|
80
|
+
* currently are behind several screens of scrolling.
|
|
81
|
+
*/
|
|
82
|
+
useEffect(() => {
|
|
83
|
+
const at = rowIndexOf(rows, value);
|
|
84
|
+
setSelected(at >= 0 ? at : firstSelectable(rows));
|
|
85
|
+
}, [rows, value]);
|
|
86
|
+
|
|
87
|
+
// A filter is a question about this opening, not a setting.
|
|
88
|
+
useEffect(() => { if (!open) setQuery(""); }, [open]);
|
|
89
|
+
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
if (!open) return;
|
|
92
|
+
listRef.current?.querySelector<HTMLElement>('[data-active="true"]')?.scrollIntoView?.({ block: "nearest" });
|
|
93
|
+
}, [open, selected]);
|
|
94
|
+
|
|
95
|
+
const pick = useCallback((next: string) => {
|
|
96
|
+
onChange(next);
|
|
97
|
+
setOpen(false);
|
|
98
|
+
}, [onChange]);
|
|
99
|
+
|
|
100
|
+
function handleKeyDown(e: React.KeyboardEvent) {
|
|
101
|
+
switch (e.key) {
|
|
102
|
+
case "ArrowDown":
|
|
103
|
+
e.preventDefault();
|
|
104
|
+
setSelected((i) => moveSelection(rows, i, 1));
|
|
105
|
+
break;
|
|
106
|
+
case "ArrowUp":
|
|
107
|
+
e.preventDefault();
|
|
108
|
+
setSelected((i) => moveSelection(rows, i, -1));
|
|
109
|
+
break;
|
|
110
|
+
case "Enter": {
|
|
111
|
+
e.preventDefault();
|
|
112
|
+
const row = rows[selected];
|
|
113
|
+
if (row?.kind === "item") pick(row.item.value);
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
case "Escape":
|
|
117
|
+
e.preventDefault();
|
|
118
|
+
setOpen(false);
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const body = (
|
|
124
|
+
<>
|
|
125
|
+
<div className="flex items-center gap-2 border-b border-border px-2.5 py-2 shrink-0">
|
|
126
|
+
<Search className="size-4 shrink-0 text-text-subtle" />
|
|
127
|
+
<input
|
|
128
|
+
ref={inputRef}
|
|
129
|
+
type="text"
|
|
130
|
+
value={query}
|
|
131
|
+
onChange={(e) => setQuery(e.target.value)}
|
|
132
|
+
placeholder={searchPlaceholder}
|
|
133
|
+
aria-label={`Search ${label.toLowerCase()}`}
|
|
134
|
+
className="min-w-0 flex-1 bg-transparent text-sm text-text-primary outline-none placeholder:text-text-subtle"
|
|
135
|
+
/>
|
|
136
|
+
</div>
|
|
137
|
+
<div
|
|
138
|
+
ref={listRef}
|
|
139
|
+
role="listbox"
|
|
140
|
+
aria-label={label}
|
|
141
|
+
className="min-h-0 flex-1 overflow-y-auto py-1"
|
|
142
|
+
data-testid={testId && `${testId}-list`}
|
|
143
|
+
>
|
|
144
|
+
{rows.length === 0 ? (
|
|
145
|
+
<p className="px-3 py-4 text-center text-sm text-text-subtle">{emptyText}</p>
|
|
146
|
+
) : (
|
|
147
|
+
rows.map((row, i) =>
|
|
148
|
+
row.kind === "separator" ? (
|
|
149
|
+
<div
|
|
150
|
+
key={`sep-${row.label}`}
|
|
151
|
+
className="border-t border-border-soft px-3 pt-2 pb-1 text-[10px] uppercase tracking-wide text-text-3 first:border-t-0 first:pt-1"
|
|
152
|
+
>
|
|
153
|
+
{row.label}
|
|
154
|
+
</div>
|
|
155
|
+
) : (
|
|
156
|
+
<OptionRow
|
|
157
|
+
key={row.item.value}
|
|
158
|
+
item={row.item}
|
|
159
|
+
active={i === selected}
|
|
160
|
+
picked={row.item.value === value}
|
|
161
|
+
mobile={isMobile}
|
|
162
|
+
onPick={() => pick(row.item.value)}
|
|
163
|
+
onHover={() => setSelected(i)}
|
|
164
|
+
/>
|
|
165
|
+
),
|
|
166
|
+
)
|
|
167
|
+
)}
|
|
168
|
+
</div>
|
|
169
|
+
</>
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
const trigger = (
|
|
173
|
+
<button
|
|
174
|
+
type="button"
|
|
175
|
+
aria-expanded={open}
|
|
176
|
+
// A panel with a filter box in it, not a bare list — and `combobox` would
|
|
177
|
+
// be a promise this button cannot keep, since the text input it would
|
|
178
|
+
// have to own does not exist until the panel is open. Radix says the same
|
|
179
|
+
// thing on the desktop trigger; this is for the sheet, which has no Radix
|
|
180
|
+
// under it.
|
|
181
|
+
aria-haspopup="dialog"
|
|
182
|
+
aria-label={label}
|
|
183
|
+
title={current?.title ?? current?.label ?? value}
|
|
184
|
+
data-testid={testId}
|
|
185
|
+
onClick={isMobile ? () => setOpen(true) : undefined}
|
|
186
|
+
// h-10 under a finger: the rows this sits in are compact by design on a
|
|
187
|
+
// desktop, but the trigger is the one thing in them anyone taps.
|
|
188
|
+
className={cn(
|
|
189
|
+
"flex h-10 items-center gap-1 rounded-md border border-border bg-panel px-2 text-xs text-text-primary min-w-0 md:h-7",
|
|
190
|
+
className,
|
|
191
|
+
)}
|
|
192
|
+
>
|
|
193
|
+
{Icon && <Icon className="size-3.5 shrink-0 text-text-3" />}
|
|
194
|
+
<StartEllipsis>{current?.label ?? (value || placeholder || "")}</StartEllipsis>
|
|
195
|
+
<ChevronDown className="size-3.5 shrink-0 text-text-3" />
|
|
196
|
+
</button>
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
if (isMobile) {
|
|
200
|
+
return (
|
|
201
|
+
<>
|
|
202
|
+
{trigger}
|
|
203
|
+
{/*
|
|
204
|
+
No autofocus here, deliberately: the keyboard would cover most of the
|
|
205
|
+
sheet before the list has been seen once, and the filter box is one
|
|
206
|
+
tap away at the top of it.
|
|
207
|
+
*/}
|
|
208
|
+
<BottomSheet open={open} onClose={() => setOpen(false)} className="popover-solid flex max-h-[80dvh] flex-col">
|
|
209
|
+
<div className="shrink-0 px-3 pb-1 text-xs font-medium text-text-2">{label}</div>
|
|
210
|
+
<div className="flex min-h-0 flex-1 flex-col" onKeyDown={handleKeyDown}>{body}</div>
|
|
211
|
+
</BottomSheet>
|
|
212
|
+
</>
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return (
|
|
217
|
+
<Popover.Root open={open} onOpenChange={setOpen} modal={modal}>
|
|
218
|
+
<Popover.Trigger asChild>{trigger}</Popover.Trigger>
|
|
219
|
+
<Popover.Portal container={portalContainer}>
|
|
220
|
+
<Popover.Content
|
|
221
|
+
align="start"
|
|
222
|
+
sideOffset={4}
|
|
223
|
+
collisionPadding={8}
|
|
224
|
+
onKeyDown={handleKeyDown}
|
|
225
|
+
// Radix focuses the first focusable child, which is already the filter
|
|
226
|
+
// box; saying so explicitly keeps it true if a control is ever added
|
|
227
|
+
// above it.
|
|
228
|
+
onOpenAutoFocus={(e) => { e.preventDefault(); inputRef.current?.focus(); }}
|
|
229
|
+
// `popover-solid`, or the glass themes render the list over whatever
|
|
230
|
+
// is behind the panel.
|
|
231
|
+
className="popover-solid z-50 flex w-[min(26rem,calc(100vw-1.5rem))] flex-col overflow-hidden rounded-md border border-border text-popover-foreground shadow-lg max-h-[min(20rem,var(--radix-popover-content-available-height))]"
|
|
232
|
+
>
|
|
233
|
+
{body}
|
|
234
|
+
</Popover.Content>
|
|
235
|
+
</Popover.Portal>
|
|
236
|
+
</Popover.Root>
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* One option.
|
|
242
|
+
*
|
|
243
|
+
* The active tint is `primary/15` rather than shadcn's `accent`, for the reason
|
|
244
|
+
* spelled out in `branch-picker.tsx`: `accent` is a hover *surface* in this app
|
|
245
|
+
* and composites to a contrast ratio of about 1.01 over the panel it sits on,
|
|
246
|
+
* i.e. a keyboard selection nobody can see.
|
|
247
|
+
*/
|
|
248
|
+
function OptionRow({ item, active, picked, mobile, onPick, onHover }: {
|
|
249
|
+
item: SearchSelectItem;
|
|
250
|
+
active: boolean;
|
|
251
|
+
picked: boolean;
|
|
252
|
+
mobile: boolean;
|
|
253
|
+
onPick: () => void;
|
|
254
|
+
onHover: () => void;
|
|
255
|
+
}) {
|
|
256
|
+
const Icon = item.icon;
|
|
257
|
+
return (
|
|
258
|
+
<button
|
|
259
|
+
type="button"
|
|
260
|
+
role="option"
|
|
261
|
+
aria-selected={picked}
|
|
262
|
+
data-active={active || undefined}
|
|
263
|
+
data-value={item.value}
|
|
264
|
+
title={item.title}
|
|
265
|
+
onClick={onPick}
|
|
266
|
+
onMouseEnter={onHover}
|
|
267
|
+
className={cn(
|
|
268
|
+
"flex w-full items-center gap-2 px-3 text-left text-sm",
|
|
269
|
+
mobile ? "min-h-11 py-2" : "py-1",
|
|
270
|
+
active ? "bg-primary/15 text-text-primary" : "text-text-2",
|
|
271
|
+
)}
|
|
272
|
+
>
|
|
273
|
+
{Icon && <Icon className="size-3.5 shrink-0 text-text-3" />}
|
|
274
|
+
<StartEllipsis>{item.label}</StartEllipsis>
|
|
275
|
+
{item.hint && <span className="shrink-0 text-[10px] text-text-3">{item.hint}</span>}
|
|
276
|
+
{picked && <Check className="size-3.5 shrink-0 text-primary" />}
|
|
277
|
+
</button>
|
|
278
|
+
);
|
|
279
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ellipsize from the start, so the end of a string — the part that differs —
|
|
3
|
+
* survives.
|
|
4
|
+
*
|
|
5
|
+
* A right-to-left box cuts at its left edge, and `<bdi>` keeps the name itself
|
|
6
|
+
* reading left to right; the isolate is load-bearing rather than tidy, because
|
|
7
|
+
* outside one a leading `.` takes the paragraph's direction and `.gitignore`
|
|
8
|
+
* renders as `gitignore.`. Used for file paths, where the filename is the
|
|
9
|
+
* distinguishing part, and for branch names, where a repository's branches all
|
|
10
|
+
* begin `fix/NX-`.
|
|
11
|
+
*/
|
|
12
|
+
export function StartEllipsis({ children }: { children: string }) {
|
|
13
|
+
return (
|
|
14
|
+
<span dir="rtl" className="truncate text-left min-w-0 flex-1">
|
|
15
|
+
<bdi>{children}</bdi>
|
|
16
|
+
</span>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
@@ -10,6 +10,8 @@ import { playNotificationSound } from "@/lib/notification-sounds";
|
|
|
10
10
|
import { toast } from "sonner";
|
|
11
11
|
import type { ChatMessage, ChatEvent } from "../../types/chat";
|
|
12
12
|
import type { BackgroundAgentStatus } from "../../shared/background-agent-status";
|
|
13
|
+
import type { PromptCacheState } from "../../shared/prompt-cache-idle";
|
|
14
|
+
import { prefixTokens } from "../../shared/turn-usage";
|
|
13
15
|
import type { ChatWsServerMessage, SessionPhase, BackgroundShell, VersionGroup } from "../../types/api";
|
|
14
16
|
import { useBackgroundOutputStore } from "../stores/background-output-store";
|
|
15
17
|
|
|
@@ -66,6 +68,8 @@ interface UseChatReturn {
|
|
|
66
68
|
pendingApproval: ApprovalRequest | null;
|
|
67
69
|
contextWindowPct: number | null;
|
|
68
70
|
compactStatus: "compacting" | null;
|
|
71
|
+
/** Prompt-cache clock for this session; drives the idle re-cache notice. */
|
|
72
|
+
promptCache: PromptCacheState | null;
|
|
69
73
|
/** MCP servers this session's subprocess reported as needing a sign-in. */
|
|
70
74
|
mcpNeedsAuth: string[];
|
|
71
75
|
statusMessage: string | null;
|
|
@@ -153,6 +157,7 @@ export function useChat(
|
|
|
153
157
|
const [pendingApproval, setPendingApproval] = useState<ApprovalRequest | null>(null);
|
|
154
158
|
const [contextWindowPct, setContextWindowPct] = useState<number | null>(null);
|
|
155
159
|
const [compactStatus, setCompactStatus] = useState<"compacting" | null>(null);
|
|
160
|
+
const [promptCache, setPromptCache] = useState<PromptCacheState | null>(null);
|
|
156
161
|
/** MCP servers this session's subprocess reported as needing a sign-in. */
|
|
157
162
|
const [mcpNeedsAuth, setMcpNeedsAuth] = useState<string[]>([]);
|
|
158
163
|
const [backgroundShells, setBackgroundShells] = useState<BackgroundShell[]>([]);
|
|
@@ -791,6 +796,25 @@ export function useChat(
|
|
|
791
796
|
}
|
|
792
797
|
return prev;
|
|
793
798
|
});
|
|
799
|
+
// This turn just rewrote the cache, so the idle clock restarts here. Done locally
|
|
800
|
+
// rather than waiting for the next `session_state`: a tab left open for hours may
|
|
801
|
+
// never reconnect, and that is exactly the case the notice exists for.
|
|
802
|
+
if (doneUsage) {
|
|
803
|
+
setPromptCache((prev) => prev && {
|
|
804
|
+
...prev,
|
|
805
|
+
lastTurnEndedAt: Date.now(),
|
|
806
|
+
billedPrefixTokens: prefixTokens(doneUsage),
|
|
807
|
+
// Left at its previous value when this turn measured none, rather than cleared:
|
|
808
|
+
// a context does not shrink, so the older figure is still the better answer.
|
|
809
|
+
...(doneUsage.contextTokens != null && { contextTokens: doneUsage.contextTokens }),
|
|
810
|
+
// Same reason: a turn that only read the cache reports no window, and the one
|
|
811
|
+
// already in hand is still the window this session's cache lives in.
|
|
812
|
+
...(doneUsage.cacheTtlMs != null && { ttlMs: doneUsage.cacheTtlMs }),
|
|
813
|
+
// Unconditional, unlike the two above: `undefined` here is the turn reporting
|
|
814
|
+
// that it re-cached after a compaction, which has to clear the flag.
|
|
815
|
+
compactedAt: doneUsage.compactedAt,
|
|
816
|
+
});
|
|
817
|
+
}
|
|
794
818
|
streamingContentRef.current = "";
|
|
795
819
|
streamingEventsRef.current = [];
|
|
796
820
|
streamingAccountRef.current = null;
|
|
@@ -985,6 +1009,9 @@ export function useChat(
|
|
|
985
1009
|
// Sync compact indicator from authoritative server state (covers reconnect).
|
|
986
1010
|
// state.compactStatus is "compacting" | null — treat undefined as null for back-compat.
|
|
987
1011
|
setCompactStatus(state.compactStatus === "compacting" ? "compacting" : null);
|
|
1012
|
+
// The server is the only holder of when the cache was last written and how big the
|
|
1013
|
+
// replayed prefix was — neither is in the transcript, so a reload has to be told.
|
|
1014
|
+
setPromptCache((state.promptCache as PromptCacheState | undefined) ?? null);
|
|
988
1015
|
setMcpNeedsAuth(Array.isArray(state.mcpNeedsAuth) ? state.mcpNeedsAuth : []);
|
|
989
1016
|
// If idle, refetch history (completed turns) and hide overlay.
|
|
990
1017
|
// Skip when nothing could have changed: the phase was already idle locally
|
|
@@ -1558,6 +1585,7 @@ export function useChat(
|
|
|
1558
1585
|
pendingApproval,
|
|
1559
1586
|
contextWindowPct,
|
|
1560
1587
|
compactStatus,
|
|
1588
|
+
promptCache,
|
|
1561
1589
|
mcpNeedsAuth,
|
|
1562
1590
|
statusMessage,
|
|
1563
1591
|
sessionTitle,
|
|
@@ -3,7 +3,8 @@ import { useTabStore } from "@/stores/tab-store";
|
|
|
3
3
|
import { useSettingsStore } from "@/stores/settings-store";
|
|
4
4
|
import { useProjectStore } from "@/stores/project-store";
|
|
5
5
|
import { usePanelStore } from "@/stores/panel-store";
|
|
6
|
-
import { useKeybindingsStore, parseCombo, eventMatchesCombo } from "@/stores/keybindings-store";
|
|
6
|
+
import { useKeybindingsStore, parseCombo, eventMatchesCombo, matchesDockBacktick } from "@/stores/keybindings-store";
|
|
7
|
+
import { isMobileDevice } from "@/hooks/use-is-mobile";
|
|
7
8
|
import { useExtensionStore } from "@/stores/extension-store";
|
|
8
9
|
import { useCompareStore } from "@/stores/compare-store";
|
|
9
10
|
import { openSettings } from "@/components/settings/open-settings";
|
|
@@ -89,9 +90,10 @@ export function useGlobalKeybindings() {
|
|
|
89
90
|
const { matchesEvent: m } = useKeybindingsStore.getState();
|
|
90
91
|
// Mod+S — always prevent browser save dialog
|
|
91
92
|
if (m(e, "save-prevent")) { e.preventDefault(); }
|
|
92
|
-
// toggle-dock (Mod+'
|
|
93
|
-
//
|
|
94
|
-
|
|
93
|
+
// toggle-dock (Mod+', and VS Code's Ctrl+` on desktop) fires even when the
|
|
94
|
+
// terminal textarea is focused (VSCode parity). Both are modifier combos, so
|
|
95
|
+
// neither can produce a plain quote or backtick in the shell.
|
|
96
|
+
if (m(e, "toggle-dock") || matchesDockBacktick(e, isMobileDevice())) {
|
|
95
97
|
e.preventDefault();
|
|
96
98
|
usePanelStore.getState().toggleDock();
|
|
97
99
|
}
|
|
@@ -126,7 +128,7 @@ export function useGlobalKeybindings() {
|
|
|
126
128
|
}
|
|
127
129
|
|
|
128
130
|
// Toggle terminal dock
|
|
129
|
-
if (match(e, "toggle-dock")) {
|
|
131
|
+
if (match(e, "toggle-dock") || matchesDockBacktick(e, isMobileDevice())) {
|
|
130
132
|
e.preventDefault();
|
|
131
133
|
usePanelStore.getState().toggleDock();
|
|
132
134
|
return;
|
package/src/web/hooks/use-lsp.ts
CHANGED
|
@@ -12,11 +12,13 @@
|
|
|
12
12
|
* skips, the bridge asks for the whole document back rather than applying a
|
|
13
13
|
* delta to a copy that is already wrong.
|
|
14
14
|
*/
|
|
15
|
-
import { useEffect, useRef, useState } from "react";
|
|
15
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
16
16
|
import type * as MonacoType from "monaco-editor";
|
|
17
|
+
import { api, projectUrl } from "@/lib/api-client";
|
|
17
18
|
import {
|
|
18
19
|
acquireLspConnection,
|
|
19
20
|
releaseLspConnection,
|
|
21
|
+
retryUnavailableLspDocuments,
|
|
20
22
|
type LspConnection,
|
|
21
23
|
type LspDocumentStatus,
|
|
22
24
|
} from "@/lib/lsp/lsp-client";
|
|
@@ -40,6 +42,8 @@ export interface LspState {
|
|
|
40
42
|
status: LspDocumentStatus | null;
|
|
41
43
|
/** Diagnostics for this file, for a Problems view to list. */
|
|
42
44
|
diagnostics: LspDiagnostic[];
|
|
45
|
+
/** Install the missing server on the host, then take the open files off "not installed". */
|
|
46
|
+
install: (serverId: string) => Promise<void>;
|
|
43
47
|
}
|
|
44
48
|
|
|
45
49
|
export interface LspDiagnostic {
|
|
@@ -166,7 +170,24 @@ export function useLsp({ editor, monaco, projectName, filePath, enabled }: UseLs
|
|
|
166
170
|
};
|
|
167
171
|
}, [active, editor, monaco, projectName, filePath]);
|
|
168
172
|
|
|
169
|
-
|
|
173
|
+
/**
|
|
174
|
+
* The Install button's half in the browser.
|
|
175
|
+
*
|
|
176
|
+
* The retry runs even when the request failed, and deliberately: a long install behind a
|
|
177
|
+
* proxy can time out the fetch while `bun add` finishes anyway, and asking again is how the
|
|
178
|
+
* editor finds out. A real failure costs one extra round trip and the status comes straight
|
|
179
|
+
* back to "not installed", with the error the host gave for it.
|
|
180
|
+
*/
|
|
181
|
+
const install = useCallback(async (serverId: string) => {
|
|
182
|
+
if (!projectName) return;
|
|
183
|
+
try {
|
|
184
|
+
await api.post(`${projectUrl(projectName)}/lsp/install`, { serverId });
|
|
185
|
+
} finally {
|
|
186
|
+
retryUnavailableLspDocuments();
|
|
187
|
+
}
|
|
188
|
+
}, [projectName]);
|
|
189
|
+
|
|
190
|
+
return { status, diagnostics, install };
|
|
170
191
|
}
|
|
171
192
|
|
|
172
193
|
/** Tell the server a save happened, so servers that only analyse on save catch up. */
|